Skip to content

Fix: Triage Y-stream CVEs when Z-stream clones are NOT_AFFECTED - #792

Open
majamassarini wants to merge 13 commits into
packit:mainfrom
majamassarini:fix/PACKIT-5281-ystream-not-affected-check
Open

Fix: Triage Y-stream CVEs when Z-stream clones are NOT_AFFECTED#792
majamassarini wants to merge 13 commits into
packit:mainfrom
majamassarini:fix/PACKIT-5281-ystream-not-affected-check

Conversation

@majamassarini

@majamassarini majamassarini commented Sep 1, 2026

Copy link
Copy Markdown
Member

Summary

Fixes PACKIT-5281: Y-stream CVEs were incorrectly skipped or postponed when Z-stream clones were NOT_AFFECTED.

Problem

When a Y-stream CVE (e.g., rhel-9.9) is checked for eligibility:

  • Low/Moderate severity + CentOS Stream first approach → told "fix is handled via Z-stream CentOS path"
  • Important/Critical severity → told "waiting for Z-stream to ship"

Both messages were incorrect when the CVE was actually NOT AFFECTED in the component. The Y-stream should have been triaged to confirm it's also not affected, instead of being skipped or postponed.

Impact: Maintainers had to manually close Y-stream issues that should have been automatically triaged.

Solution

Check Z-stream triage status before skipping or postponing Y-stream CVEs.

Changes

  1. Added _check_zstream_not_affected()

    • Searches for Z-stream clones with ymir_triaged_not_affected label
  2. Added _check_zstream_pending_triage()

    • Searches for Z-stream clones without terminal ymir_triaged* labels
  3. Modified _check_lowmod_ystream_eligibility()

    • For Low/Moderate Y-stream CVEs with CS_FIRST approach:
      • ✅ If Z-stream is NOT_AFFECTED → return IMMEDIATELY (triage Y-stream)
      • ⏳ If Z-stream pending triage → return PENDING_DEPENDENCIES (wait for results)
      • ❌ Otherwise → return NEVER (skip Y-stream - existing behavior)
  4. Modified _check_for_dependency_blocker()

    • For Important/Critical Y-stream CVEs:
      • ✅ If Z-stream is NOT_AFFECTED → return None (proceed with triage)
      • ⏳ Otherwise → return PENDING_DEPENDENCIES (postpone - existing behavior)

Related

  • Implements the strategy suggested in PACKIT-5281: "triage all Z streams first. The Y-streams should then pick up findings from latest Z"

🤖 Generated with Claude Code

@qodo-for-packit

Copy link
Copy Markdown

PR Summary by Qodo

Triage Y-stream CVEs when Z-stream clones are not affected

🐞 Bug fix 🕐 20-40 Minutes

Grey Divider

AI Description

• Detect not-affected Z-stream clones before deferring Y-stream CVE triage.
• Hold low/moderate CS-first cases while Z-stream triage remains pending.
• Preserve skip and postponement outcomes for affected Z-stream clones.
Diagram

graph TD
  B{"Severity"} -->|Low Moderate| C["CS-first path"] --> E{"Z triage state"} -->|Not affected| G["Immediate triage"]
  B -->|Important Critical| D["Dependency gate"] --> F{"Z clone state"} -->|Unshipped| H["Pending dependencies"]
  E -->|Pending| H
  E -->|Affected| I["Skip Y-stream"]
  F -->|Shipped or not affected| G
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Single clone-state classifier
  • ➕ Queries Jira once for all relevant clone fields and labels
  • ➕ Uses one consistent snapshot for shipment and triage decisions
  • ➕ Centralizes stream filtering and terminal-label semantics
  • ➖ Requires a broader refactor of existing shipment and fix-approach helpers
  • ➖ Must preserve nuanced status, resolution, and Koji behavior
2. Extend existing eligibility helpers
  • ➕ Reuses clone results already fetched by each eligibility path
  • ➕ Avoids introducing two additional Jira searches
  • ➕ Keeps severity-specific behavior explicit
  • ➖ Expands helper return contracts and caller complexity
  • ➖ May continue duplicating classification rules between severity paths

Recommendation: The behavioral strategy is correct, but a shared clone-state classifier would be the strongest long-term design because it reduces Jira calls, duplicated stream filtering, and inconsistent snapshots. If minimizing scope is essential, the current focused implementation is acceptable, but its new not-affected and pending branches should be protected with unit tests.

Files changed (1) +212 / -0

Bug fix (1) +212 / -0
jira.pyAccount for Z-stream triage state in Y-stream eligibility +212/-0

Account for Z-stream triage state in Y-stream eligibility

• Adds Jira lookups for same-major Z-stream clones marked not affected or still awaiting terminal triage. Low/Moderate CS-first CVEs now proceed, wait, or skip based on that state, while Important/Critical CVEs proceed when a clone is not affected instead of waiting for shipment.

ymir/tools/privileged/jira.py

@qodo-for-packit

qodo-for-packit Bot commented Sep 1, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Module streams share clone status ✓ Resolved 🐞 Bug ≡ Correctness ⭐ New
Description
The new searches do not distinguish modular streams, so a NOT_AFFECTED or pending Z-stream tracker
for one module stream is treated as belonging to another tracker with the same CVE, component, and
fix version. This can incorrectly make the Y-stream immediately eligible or postpone it behind an
unrelated module stream.
Code

ymir/tools/privileged/jira.py[R793-796]

+    jql = (
+        f'summary ~ "{escaped_cve_id}" AND component = "{escaped_component}"'
+        f' AND labels = "SecurityTracking" AND labels = "ymir_triaged_not_affected"'
+        f' AND key != "{exclude_key}"'
Relevance

●●● Strong

Recent accepted precedent supports exact modular-summary matching to prevent cross-stream tracker
confusion.

PR-#743

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new JQL and local filters never inspect candidate summaries or module streams. Elsewhere,
tracker identity explicitly includes an exact parse_module_stream match, and regression tests
demonstrate that modular/non-modular trackers and different PostgreSQL streams may otherwise share
CVE, component, and fix-version values.

ymir/tools/privileged/jira.py[793-817]
ymir/tools/privileged/jira.py[524-562]
ymir/tools/privileged/tests/unit/test_jira.py[1951-1984]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Z-stream status helpers match issues only by CVE, component, and fix version. Modular trackers can share those values while representing different module streams, causing unrelated NOT_AFFECTED or pending status to alter Y-stream eligibility.

## Issue Context
Use the current issue summary to derive its module stream with `parse_module_stream`, request candidate summaries from Jira, and retain only candidates whose parsed module stream exactly matches the current tracker, including the distinction between modular and non-modular trackers. Apply this consistently to both new helpers and their call sites, with regression tests for different module streams.

## Fix Focus Areas
- ymir/tools/privileged/jira.py[773-899]
- ymir/tools/privileged/jira.py[1097-1123]
- ymir/tools/privileged/jira.py[1292-1360]
- ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[96-335]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. NOT_AFFECTED clone stays postponed ✓ Resolved 🐞 Bug ≡ Correctness
Description
For Low/Moderate CVEs, the new NOT_AFFECTED check only runs after _check_zstream_fix_approach
returns CS_FIRST, but an open clone triaged as NOT_AFFECTED normally has no Fixed in Build and
therefore returns PENDING first. The Y-stream is consequently postponed indefinitely instead of
becoming immediately eligible, leaving the core reported scenario unfixed.
Code

ymir/tools/privileged/jira.py[R1308-1311]

+            # Before skipping the Y-stream, check if Z-stream clones were NOT_AFFECTED
+            try:
+                not_affected_clones = await _check_zstream_not_affected(
+                    cve_id, component, issue_key, major_version
Relevance

●●● Strong

Clear control-flow bug prevents the PR’s stated NOT_AFFECTED scenario; similar Jira correctness
fixes were accepted recently.

PR-#729

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The pre-check classifies every relevant clone without Fixed in Build as pending and the caller
returns immediately for that result. The newly added lookup is nested only under the later
CS_FIRST branch, while the triage workflow records NOT_AFFECTED by adding a terminal label rather
than setting Fixed in Build or changing status, so a normally triaged open clone never reaches this
lookup.

ymir/tools/privileged/jira.py[689-693]
ymir/tools/privileged/jira.py[1292-1324]
ymir/agents/triage_agent.py[1496-1517]
ymir/agents/triage_agent.py[1524-1531]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Low/Moderate Y-stream flow returns `PENDING_DEPENDENCIES` before checking whether the pending Z-stream clone has already been triaged as NOT_AFFECTED. Move or extend the NOT_AFFECTED decision so it executes before the `FixApproach.PENDING` return, and add a regression test using an open NOT_AFFECTED clone without Fixed in Build.

## Issue Context
`_check_zstream_fix_approach` classifies any relevant open clone lacking Fixed in Build as pending. Triage completion writes the `ymir_triaged_not_affected` terminal label without closing the issue or populating Fixed in Build, making this the normal shape of a NOT_AFFECTED result.

## Fix Focus Areas
- ymir/tools/privileged/jira.py[1277-1324]
- ymir/tools/privileged/tests/unit/test_jira.py[1232-1298]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Terminal clones remain pending ✓ Resolved 🐞 Bug ☼ Reliability
Description
The pending-triage JQL does not exclude ymir_needs_attention or exhausted ymir_triage_errored,
both of which are terminal triage outcomes elsewhere in the repository. A Y-stream can consequently
remain postponed waiting for a Z-stream clone that will not be automatically triaged again.
Code

ymir/tools/privileged/jira.py[R828-830]

+        f' AND labels != "ymir_triaged_postponed"'
+        f' AND labels != "ymir_triaged_not_affected"'
+        f' AND labels != "ymir_triaged"'
Relevance

●●● Strong

PR #785 recently accepted adding missing terminal triage labels to prevent reprocessing and
indefinite blocking.

PR-#785

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new query excludes six ymir_triaged* labels only. Triage maps clarification-needed to
ymir_needs_attention and errors to ymir_triage_errored, and the consolidation terminal-label
contract explicitly excludes both from further triage processing.

ymir/tools/privileged/jira.py[820-831]
ymir/agents/triage_agent.py[116-125]
ymir/agents/rebase_consolidation.py[98-115]
PR-#785

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pending Z-stream query omits terminal clarification-needed and exhausted-error labels, causing completed or blocked clones to be reported as pending indefinitely.

## Issue Context
`Resolution.CLARIFICATION_NEEDED` maps to `ymir_needs_attention`, while exhausted triage errors use `ymir_triage_errored`. Existing consolidation logic treats both labels as terminal.

## Fix Focus Areas
- ymir/tools/privileged/jira.py[820-831]
- ymir/common/constants.py[159-191]
- ymir/agents/triage_agent.py[116-125]
- ymir/agents/rebase_consolidation.py[98-115]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (2)
4. Older clone overrides latest ✓ Resolved 🐞 Bug ≡ Correctness
Description
The helpers pool both current and upcoming Z-streams and treat any NOT_AFFECTED result as decisive,
even though the repository selects the upcoming stream as the applicable Z-stream when one exists.
An older current clone marked NOT_AFFECTED can therefore make the Y-stream immediately eligible
while the latest upcoming clone is affected or still pending.
Code

ymir/tools/privileged/jira.py[R779-784]

+    relevant_z_streams = {
+        variant.lower()
+        for streams in (current_z_streams, upcoming_z_streams)
+        for major, v in streams.items()
+        if major not in maintenance_majors and major == major_version
+        for variant in get_fix_version_variants(v)
Relevance

●● Moderate

Concrete version-selection risk, but no closely matching precedent confirms acceptance for this
repository-specific Z-stream behavior.

PR-#429

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both helpers build relevant_z_streams from current and upcoming configurations and the caller
returns immediately when any matching clone is NOT_AFFECTED. The checked-in configuration can
contain both streams for one major, while VersionMapperTool explicitly chooses
upcoming_z_streams[major] before falling back to the current stream.

ymir/tools/privileged/jira.py[774-795]
ymir/tools/privileged/jira.py[845-866]
ymir/tools/privileged/jira.py[1286-1314]
templates/rhel-config.json[6-13]
ymir/tools/unprivileged/version_mapper.py[81-86]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Z-stream status checks combine current and upcoming streams, allowing an older current-stream clone to override the applicable upcoming clone's triage result.

## Issue Context
The version mapper gives the upcoming Z-stream precedence over the current stream. The eligibility checks should inspect the applicable/latest stream rather than accepting any same-major result.

## Fix Focus Areas
- ymir/tools/privileged/jira.py[774-795]
- ymir/tools/privileged/jira.py[845-866]
- ymir/tools/privileged/jira.py[1286-1314]
- ymir/tools/unprivileged/version_mapper.py[81-86]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Lookup failure permanently skips ✓ Resolved 🐞 Bug ☼ Reliability
Description
The CS-first branch converts failures from either new Jira lookup into an empty result and then
returns NEVER, falsely asserting that the fix will be inherited. The triage workflow receives no
error field, so it records an open-ended terminal outcome instead of retrying the transient lookup
failure.
Code

ymir/tools/privileged/jira.py[R1282-1284]

+            except Exception as e:
+                logger.warning(f"Failed to check Z-stream NOT_AFFECTED status for {cve_id}: {e}")
+                not_affected_clones = []
Relevance

●● Moderate

Failure propagation is often accepted, but a closely related swallowed-fetch-failure finding was
rejected, leaving mixed precedent.

PR-#540
PR-#706

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both added exception handlers assign an empty list, after which the branch can return NEVER with
no error. By contrast, the surrounding _check_zstream_fix_approach failure path sets error, and
check_cve_eligibility only routes operational failures to retry when that field is present;
otherwise it creates an open-ended analysis result.

ymir/tools/privileged/jira.py[1246-1258]
ymir/tools/privileged/jira.py[1277-1284]
ymir/tools/privileged/jira.py[1305-1343]
ymir/agents/triage_agent.py[592-648]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Failures in the new NOT_AFFECTED and pending-triage lookups are treated as negative results, causing a permanent eligibility skip instead of a retryable operational error.

## Issue Context
The surrounding fix-approach check returns an eligibility result containing `error` when its lookup fails. The triage workflow uses that field to select the retry path; without it, `NEVER` becomes an open-ended terminal result.

## Fix Focus Areas
- ymir/tools/privileged/jira.py[1277-1284]
- ymir/tools/privileged/jira.py[1305-1312]
- ymir/tools/privileged/jira.py[1333-1343]
- ymir/agents/triage_agent.py[592-648]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. Current fallback remains untested ✓ Resolved 📘 Rule violation ▣ Testability
Description
The test labeled as the current Z-stream fallback uses major version 10, but RHEL_CONFIG defines
rhel-10.3.z only in upcoming_z_streams, so the test exercises the upcoming-selection branch
rather than fallback to current_z_streams. A regression removing the fallback could therefore
still pass, leaving the changed privileged-tool behavior without effective unit coverage.
Code

ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[R68-70]

+    variants = await _get_applicable_zstream_variants("10")
+    # get_fix_version_variants returns both Y and Z forms
+    assert variants == {"rhel-10.3", "rhel-10.3.z"}
Relevance

●●● Strong

The test clearly misses the fallback branch; adding a true current-stream-only configuration is a
deterministic coverage fix.

PR-#670
PR-#743

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 1589 requires tests that cover changed privileged-tool behavior and fail if that behavior is
reverted. The shared configuration maps RHEL 10 to rhel-10.3.z under upcoming_z_streams and has
no RHEL 10 entry under current_z_streams; because production selects upcoming streams before
falling back to current streams, calling the helper with "10" and asserting that value cannot
exercise or verify the fallback branch.

Rule 1589: Require unit tests for changes to privileged tools (ymir/tools/privileged/, esp. distgit.py)
ymir/tools/privileged/jira.py[761-763]
ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[27-31]
ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[62-70]
ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[61-70]
ymir/tools/privileged/jira.py[761-768]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The current-fallback test uses major version 10, which is configured with an upcoming Z-stream, so it does not execute the fallback to `current_z_streams` that it claims to cover. Supply test configuration containing a current Z-stream for a non-maintenance major with no corresponding upcoming Z-stream, then assert that the current-stream variants are returned.

## Issue Context
Keep a matching current Y-stream so `get_maintenance_majors` does not classify the selected major as maintenance. The test configuration must omit an upcoming Z-stream for that major while retaining its current Z-stream.

## Fix Focus Areas
- ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[27-31]
- ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[61-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. No-Z-stream tests hit Jira ✓ Resolved 📘 Rule violation ▣ Testability
Description
Both no-applicable-Z-stream tests invoke helpers without mocking SearchJiraIssuesTool.run, even
though each helper searches Jira before checking stream applicability. The tests can therefore make
real network requests to the fixture’s http://jira URL, making coverage of the privileged Jira
changes unreliable and susceptible to failures unrelated to the behavior under test.
Code

ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[R195-196]

+    not_affected = await _check_zstream_not_affected("CVE-2026-12345", "curl", "RHEL-999", "7")
+    assert not_affected == []
Relevance

●●● Strong

Privileged Jira tests are expected to mock network calls; unmocked requests make regression coverage
unreliable.

PR-#729
PR-#410

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 1589 requires meaningful unit tests for changes under ymir/tools/privileged/. The tests mock
only load_rhel_config, while the production helpers first call the network-backed
SearchJiraIssuesTool.run, which performs an HTTP POST, and the autouse fixture merely configures
Jira as http://jira rather than replacing the request; this omission affects both the
no-applicable-Z-stream and pending-triage no-applicable-stream tests.

Rule 1589: Require unit tests for changes to privileged tools (ymir/tools/privileged/, esp. distgit.py)
ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[189-196]
ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[287-294]
ymir/tools/privileged/jira.py[794-806]
ymir/tools/privileged/jira.py[1699-1707]
ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[188-196]
ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[286-294]
ymir/tools/privileged/jira.py[862-874]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The no-applicable-Z-stream tests call helpers that search Jira before loading and evaluating stream applicability, but neither test mocks `SearchJiraIssuesTool.run`. Add an empty-result async mock to both tests so they cannot contact the configured Jira URL and reliably exercise the intended branch.

## Issue Context
The production helpers invoke `SearchJiraIssuesTool.run` before calling `_get_applicable_zstream_variants`. Mock the search to return an empty `JSONToolOutput` and assert that it is called once in each test, keeping these privileged-tool tests isolated and deterministic.

## Fix Focus Areas
- ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[188-196]
- ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[286-294]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Maintenance branch remains untested ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The maintenance test adds an unused maintenance_versions key and checks major 7, which has no
configured Z-stream, so the helper returns None through the no-stream branch instead of its
maintenance branch. A regression in maintenance-major detection would therefore still pass this
test.
Code

ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[R85-88]

+    config_with_maintenance = {
+        **RHEL_CONFIG,
+        "maintenance_versions": ["7"],
+    }
Relevance

●●● Strong

Recent precedent accepts replacing tautological tests with behavioral coverage; this fixture never
exercises maintenance detection.

PR-#785
PR-#743

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The repository computes maintenance majors as current_z_streams.keys() - current_y_streams.keys()
and never reads maintenance_versions; with the supplied configuration, major 7 reaches the
separate not applicable_z_stream return instead.

ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[82-94]
ymir/common/version_utils.py[229-233]
ymir/tools/privileged/jira.py[757-766]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Update the maintenance test so its configuration satisfies the repository's actual maintenance definition: a major present in `current_z_streams` but absent from `current_y_streams`. Assert behavior using that major rather than adding the unused `maintenance_versions` key.

## Issue Context
`get_maintenance_majors` derives maintenance majors from the difference between current Z-stream and current Y-stream keys. In the existing fixture, major `8` meets this condition while major `7` has no stream at all.

## Fix Focus Areas
- ymir/tools/privileged/tests/unit/test_jira_zstream_status.py[82-94]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (1)
9. Z-stream checks lack tests 📘 Rule violation ▣ Testability
Description
New privileged Jira triage behavior was added without corresponding unit tests. The Z-stream status
queries and resulting eligibility decisions therefore violate the required test coverage for
privileged tools.
Code

ymir/tools/privileged/jira.py[R743-746]

+async def _check_zstream_not_affected(
+    cve_id: str, component: str, exclude_key: str, major_version: str
+) -> list[str]:
+    """Check if any Z-stream clone was triaged as NOT_AFFECTED.
Relevance

●●● Strong

Recent Jira changes were expected to include regression tests; this PR explicitly omits unit tests
despite the privileged-tools rule.

PR-#729
PR-#743

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 1589 requires new or updated unit tests whenever files under
ymir/tools/privileged/ change. The PR adds _check_zstream_not_affected and related eligibility
behavior in ymir/tools/privileged/jira.py, while the provided diff contains no test-file changes.

Rule 1589: Require unit tests for changes to privileged tools (ymir/tools/privileged/, esp. distgit.py)
ymir/tools/privileged/jira.py[743-875]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Z-stream NOT_AFFECTED and pending-triage behavior in the privileged Jira tool has no corresponding unit tests.

## Issue Context
Tests should mock Jira search results and RHEL stream configuration, then assert the eligibility outcomes for NOT_AFFECTED, pending, affected, and Jira-query failure cases.

## Fix Focus Areas
- ymir/tools/privileged/jira.py[743-875]
- ymir/tools/privileged/jira.py[1073-1091]
- ymir/tools/privileged/jira.py[1277-1333]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials
✅ Compliance rules (platform): 8 rules

Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread ymir/tools/privileged/jira.py
Comment thread ymir/tools/privileged/jira.py Outdated
Comment thread ymir/tools/privileged/jira.py
Comment thread ymir/tools/privileged/jira.py Outdated
@majamassarini

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/tools/privileged/tests/unit/test_jira_zstream_status.py Outdated
Comment thread ymir/tools/privileged/tests/unit/test_jira_zstream_status.py Outdated
@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 7cef65d

@majamassarini

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/tools/privileged/tests/unit/test_jira_zstream_status.py Outdated
@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 8cd0470

@majamassarini
majamassarini force-pushed the fix/PACKIT-5281-ystream-not-affected-check branch from 432c327 to 094880c Compare September 1, 2026 12:44
@majamassarini

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/tools/privileged/jira.py Outdated
@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 094880c

@majamassarini
majamassarini force-pushed the fix/PACKIT-5281-ystream-not-affected-check branch from 094880c to b594774 Compare September 1, 2026 13:27
@majamassarini

Copy link
Copy Markdown
Member Author

/agentic_review

@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit e1d1866

Fixes PACKIT-5281: Y-stream CVEs were incorrectly skipped or postponed when
Z-stream clones were not affected. The CVE eligibility check would say "fix
is handled via Z-stream CentOS path" or "waiting for Z-stream to ship"
without checking if the Z-streams were actually triaged as NOT_AFFECTED.

This caused maintainers to manually close Y-stream issues that should have
been automatically triaged and marked as not affected.

Changes:
- Add _check_zstream_not_affected(): searches for Z-stream clones with
  ymir_triaged_not_affected label
- Add _check_zstream_pending_triage(): searches for Z-stream clones without
  any terminal ymir_triaged* labels
- Modify _check_lowmod_ystream_eligibility(): for Low/Moderate Y-stream CVEs
  with CS_FIRST approach detected:
  * First check if Z-stream was NOT_AFFECTED → return IMMEDIATELY (triage Y-stream)
  * Then check if Z-stream pending triage → return PENDING_DEPENDENCIES (wait)
  * Otherwise → return NEVER (existing behavior - skip Y-stream)
- Modify _check_for_dependency_blocker(): for Important/Critical Y-stream CVEs:
  * Check if Z-stream was NOT_AFFECTED before postponing
  * If yes → return None (proceed with triage, same as if clone had shipped)

Example scenarios fixed:
- RHEL-214038 (rhel-9.9, Moderate): was told "CentOS Stream path", now will
  be triaged when Z-stream is not affected
- RHEL-224798, RHEL-224847 (rhel-10.3/9.9, Important): were postponed waiting
  for Z-stream, now will be triaged when Z-stream is not affected

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
…ing check

The _check_zstream_pending_triage() function was missing terminal labels
for clarification-needed and exhausted-error states, causing Y-stream CVEs
to wait indefinitely for Z-stream clones that are actually blocked or
completed in a terminal error state.

Changes:
- Add exclusion for ymir_needs_attention (CLARIFICATION_NEEDED resolution)
- Add exclusion for ymir_triage_errored (exhausted triage retries)
- Update comment to accurately describe which labels are terminal vs non-terminal

This ensures that Z-stream clones in blocked or terminal error states are
not incorrectly reported as "pending triage", which would cause Y-stream
issues to be postponed indefinitely.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The Z-stream status check functions were combining current and upcoming
streams into one set, allowing an older current-stream clone to override
the applicable upcoming clone's triage result.

Changes:
- Add _get_applicable_zstream_variants() helper that implements version
  mapper precedence: upcoming Z-stream first, falls back to current
- Refactor _check_zstream_not_affected() to use the helper, eliminating
  duplicated logic
- Refactor _check_zstream_pending_triage() to use the helper, eliminating
  duplicated logic
- Add early returns when major version is in maintenance or no applicable
  Z-stream exists

This ensures Y-stream CVEs check the correct/latest Z-stream clone for
triage status, preventing incorrect eligibility decisions based on older
Z-stream versions.

Example: For a rhel-9.9 Y-stream CVE, if both rhel-9.8.z (current) and
rhel-9.9.z (upcoming) Z-stream clones exist, only rhel-9.9.z results are
considered, matching the version mapper behavior.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Failures in NOT_AFFECTED and pending-triage lookups were silently treated
as negative results (empty lists), causing permanent eligibility skips
instead of retryable operational errors.

The triage workflow uses the `error` field in CVEEligibilityResult to
select the retry path. Without it, transient Jira API failures become
terminal NEVER decisions that permanently skip Y-stream CVEs.

Changes:
- In _check_lowmod_ystream_eligibility():
  * NOT_AFFECTED check failure → return NEVER with error field (retryable)
  * Pending triage check failure → return NEVER with error field (retryable)
- In _check_for_dependency_blocker():
  * NOT_AFFECTED check failure → return NEVER with error field (retryable)

This matches the error handling pattern from _check_zstream_fix_approach(),
ensuring that operational failures trigger retries rather than permanent
skips.

Example: If Jira API is temporarily unavailable when checking Z-stream
NOT_AFFECTED status, the task will be retried rather than permanently
marking the Y-stream as ineligible.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Adds comprehensive unit tests for the new Z-stream status check functions
introduced in PACKIT-5281.

Test coverage:
- _get_applicable_zstream_variants():
  * Upcoming Z-stream takes precedence over current
  * Falls back to current when no upcoming exists
  * Returns None for non-existent or maintenance versions

- _check_zstream_not_affected():
  * Finds Z-stream clones with ymir_triaged_not_affected label
  * Filters by applicable Z-stream version (upcoming > current)
  * Ignores old current Z-stream when upcoming exists
  * Returns empty list when no applicable clones found

- _check_zstream_pending_triage():
  * Finds Z-stream clones without terminal labels
  * Excludes clones with terminal labels (handled by JQL)
  * Filters by applicable Z-stream version
  * Ignores old current Z-stream when upcoming exists
  * Returns empty list when no applicable clones found

Test patterns follow existing conventions:
- Uses flexmock for mocking external dependencies
- Mocks SearchJiraIssuesTool.run() and load_rhel_config()
- Uses RHEL_CONFIG fixture matching production structure
- Tests both positive and edge cases

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes test failures caused by incorrect expectations and missing mocks
for the new Z-stream status check functions.

Changes to NEW tests (test_jira_zstream_status.py):
- test_get_applicable_zstream_variants_upcoming_wins: Expect both Y and Z
  forms ({"rhel-9.7", "rhel-9.7.z"}) from get_fix_version_variants
- test_get_applicable_zstream_variants_current_fallback: Use version 10
  instead of 8 (version 8 is maintenance, has Z but no Y-stream)
- test_get_applicable_zstream_variants_maintenance: Test version 8 which
  correctly returns None (maintenance = Z-stream but no Y-stream)
- test_check_zstream_not_affected_no_applicable_zstream: Mock SearchJira
  to verify it's NOT called (function returns early)
- test_check_zstream_pending_triage_no_applicable_zstream: Mock SearchJira
  to verify it's NOT called (function returns early)

Changes to EXISTING tests (test_jira.py):
- test_eligibility_ystream_clones_pending: Mock _check_zstream_not_affected
  to return [] (no NOT_AFFECTED clones found)
- test_eligibility_ystream_low_moderate_cs_first: Mock both new functions
  to return [] (no NOT_AFFECTED, no pending clones)

These mocks ensure existing tests continue to test their original behavior
without being affected by the new Z-stream status checks.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
…age eligibility checks

Add comprehensive integration tests for the complete CVE triage eligibility flow
when Z-stream clones are NOT_AFFECTED or pending triage:

CS_FIRST path (Low/Moderate severity):
- Z-stream NOT_AFFECTED → IMMEDIATELY (Y-stream should be triaged)
- Z-stream pending triage → PENDING_DEPENDENCIES (wait for Z-stream results)
- NOT_AFFECTED check fails → NEVER with error (retryable)
- Pending triage check fails → NEVER with error (retryable)

Dependency blocker path (Important/Critical severity):
- Z-stream NOT_AFFECTED → IMMEDIATELY (proceed despite pending clones)
- NOT_AFFECTED check fails → NEVER with error (retryable)

These tests verify the full eligibility check flow (CheckCveTriageEligibilityTool)
rather than just the helper functions, ensuring the integration works correctly.

Related: PACKIT-5281

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…fallback

The test claimed to verify fallback to current_z_streams when no upcoming
exists, but used major version 10 which has an upcoming Z-stream configured,
so it was actually testing the upcoming path, not the fallback.

Added major version 11 to test config with:
- Current Y-stream (rhel-11.2) to avoid maintenance classification
- Current Z-stream (rhel-11.1.z) for the fallback case
- NO upcoming Z-stream entry

Updated test to use version 11 and assert current Z-stream variants are
returned, properly exercising the fallback code path.

Related: PACKIT-5281

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…eturn

The NOT_AFFECTED and pending-triage check functions were calling Jira search
even when no applicable Z-stream exists, wasting API calls. Moved the
`_get_applicable_zstream_variants` check to happen BEFORE the Jira search,
enabling early return when major version has no Z-stream configured.

Also fixed integration test mocks to pass correct number of arguments to
`_check_zstream_clones_shipped` (3 args: cve_id, component, exclude_key).

Related: PACKIT-5281

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…NDENCIES

When _check_zstream_fix_approach returns PENDING (open Z-stream clone without
Fixed in Build), the code was immediately returning PENDING_DEPENDENCIES without
checking if the clone was actually triaged as NOT_AFFECTED.

A NOT_AFFECTED clone has:
- Status: Open (not closed)
- Label: ymir_triaged_not_affected
- Fixed in Build: Empty (no build)

This makes it look "pending" to _check_zstream_fix_approach, but it's actually
complete. The Y-stream should proceed to triage (IMMEDIATELY) rather than wait.

Changes:
- Combined PENDING and CS_FIRST handling to check NOT_AFFECTED first
- After NOT_AFFECTED and pending-triage checks, differentiate final behavior:
  - PENDING: return PENDING_DEPENDENCIES (waiting for Fixed in Build)
  - CS_FIRST: return NEVER (CS-first applies, skip Y-stream)
- Added regression test for PENDING + NOT_AFFECTED case

Related: PACKIT-5281

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…PENDING test

The test_eligibility_ystream_low_moderate_pending test was failing because it
didn't mock the new _check_zstream_not_affected and _check_zstream_pending_triage
functions that are now called in the PENDING path.

Without these mocks, the functions tried to call load_rhel_config multiple times,
exceeding the mock's .once() expectation and causing the test to fail.

Added mocks for both functions returning empty lists (no NOT_AFFECTED clones,
no pending-triage clones) so the test properly exercises the PENDING path and
returns PENDING_DEPENDENCIES as expected.

Related: PACKIT-5281

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Added comprehensive unit test coverage for edge cases:

NOT_AFFECTED function tests:
- Clone exists but is affected (no ymir_triaged_not_affected label)
- Jira API search failure (connection timeout)

Pending-triage function tests:
- Clone exists but has terminal label (completed as affected)
- Jira API search failure (server unavailable)

These tests complete the unit test coverage for Z-stream NOT_AFFECTED and
pending-triage behavior, covering all outcomes: NOT_AFFECTED, pending,
affected, and Jira-query failure cases.

Total unit tests: 18 (was 15, added 3)
- _get_applicable_zstream_variants: 4 tests
- _check_zstream_not_affected: 8 tests (was 6)
- _check_zstream_pending_triage: 6 tests (was 5)

Related: PACKIT-5281

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
@majamassarini
majamassarini force-pushed the fix/PACKIT-5281-ystream-not-affected-check branch from 834473b to a580a44 Compare September 2, 2026 07:38
@majamassarini

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/tools/privileged/jira.py
@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit a580a44

@majamassarini

Copy link
Copy Markdown
Member Author

/agentic_review

@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit ea5afea

…nation

Modular trackers can share CVE ID, component, and fix version while representing
different module streams. Without filtering, a NOT_AFFECTED or pending status in
one module stream (e.g., postgresql:16) could incorrectly affect eligibility for
another module stream (e.g., postgresql:15).

Changes:
- Updated _check_zstream_not_affected and _check_zstream_pending_triage to accept
  summary parameter
- Parse module stream from current issue using parse_module_stream
- Request summary field from Jira search results
- Filter clones to only match when:
  - Both are modular with the exact same (module, stream) tuple, OR
  - Both are non-modular (None module stream)
- Updated all 3 call sites to pass summary parameter
- Updated all existing tests to pass summary parameter
- Added 5 regression tests for modular tracker scenarios:
  - NOT_AFFECTED: modular match, modular mismatch, modular vs non-modular
  - Pending triage: modular match, modular mismatch

Example: postgresql:15/postgis and postgresql:16/postgis both in component postgis
now correctly tracked separately - NOT_AFFECTED in :16 doesn't affect :15 eligibility.

Related: PACKIT-5281

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
@majamassarini
majamassarini force-pushed the fix/PACKIT-5281-ystream-not-affected-check branch from ea5afea to 8bdab2e Compare September 2, 2026 08:27
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.

1 participant