Fix: Triage Y-stream CVEs when Z-stream clones are NOT_AFFECTED - #792
Fix: Triage Y-stream CVEs when Z-stream clones are NOT_AFFECTED#792majamassarini wants to merge 6 commits into
Conversation
PR Summary by QodoTriage Y-stream CVEs when Z-stream clones are not affected
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo
1.
|
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 7cef65d |
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 8cd0470 |
432c327 to
094880c
Compare
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 094880c |
094880c to
b594774
Compare
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit e1d1866 |
834473b to
a580a44
Compare
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit a580a44 |
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit ea5afea |
65254ad to
2193bad
Compare
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 2193bad |
2193bad to
025f3bd
Compare
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 025f3bd |
abd09c9 to
259f0ed
Compare
|
One general note that's been bothering me for some time. Could you please follow the contributing guidelines and, to quote, use common sense when creating commits, not too big, not too small? I think in this case 2 or 3 commits would be enough. It would make the PR(s) easier to review and the git history cleaner. |
nforro
left a comment
There was a problem hiding this comment.
A couple of things Claude flagged:
Finding 1 (high confidence, real bug): _check_zstream_pending_triage misses the current postponed-triage labels
ymir/tools/privileged/jira.py, new function _check_zstream_pending_triage, excludes these labels as "terminal/already-triaged":
ymir_triaged_backport, ymir_triaged_rebase, ymir_triaged_rebuild,
ymir_triaged_postponed, ymir_triaged_not_affected, ymir_triaged,
ymir_backported, ymir_rebased, ymir_rebuilt,
ymir_needs_attention, ymir_triage_errored
ymir_triaged_postponed is the label the comment relies on for "postponed" — but per ymir/common/constants.py:202-204, that label is deprecated ("replaced by labels with reason for postponement") and is only ever referenced by label_postponed_issues in triage_agent.py for removal of legacy labels, never applied by current code. The actual labels a postponed triage decision gets today come from _RESOLUTION_TO_LABEL (triage_agent.py:141-152):
Resolution.POSTPONED_DEPENDENCY → ymir_postponed_dependency
Resolution.POSTPONED_NO_PATCH → ymir_postponed_no_patch
Resolution.POSTPONED_PR_PENDING → ymir_postponed_pr_pending
None of these are excluded. So a Z-stream clone that has already been triaged and landed on "postponed — waiting on a dependency/patch/PR" (a real, terminal triage decision, just like backport/rebase/rebuild) still matches the "pending triage" JQL. The Y-stream CVE then gets PENDING_DEPENDENCIES, "waiting for Z-stream clone triage results" — results that already exist — and will wait indefinitely, since nothing in the normal sweep flow adds one of the excluded labels to resolve that state. This reproduces the same class of bug PACKIT-5281 was filed to fix, just via a different path.
Fix: also exclude ymir_postponed_dependency, ymir_postponed_no_patch, ymir_postponed_pr_pending (no need for ymir_postponed_y_stream — that's Y-stream-only).
Finding 2 (medium confidence): new Makefile targets break the JIRA_ISSUE convention
Makefile's new triage-issue and process targets read $(ISSUE):
triage-issue:
$(COMPOSE_AGENTS) run --rm -e JIRA_ISSUE=$(ISSUE) ...
process:
$(COMPOSE_AGENTS) run --rm -e JIRA_ISSUE=$(ISSUE) ...Every other target in the file (8+ call sites, including the pre-existing process-issue, trigger-pipeline, trigger-reproducer) takes JIRA_ISSUE= from the invoker. A user running make triage-issue JIRA_ISSUE=RHEL-123 out of habit gets an empty $(ISSUE) with no error — trigger-pipeline/trigger-reproducer both guard with @if [ -z "$(JIRA_ISSUE)" ]; then ... exit 1; fi, but the two new targets have no such guard, so this fails silently inside the container instead of at the make invocation.
Finding 3 (lower confidence, pre-existing but adjacent): unquoted >= in Containerfile.c10s / Containerfile.c9s
This PR's own fix commit quotes sentry-sdk>=2.13.0 and GitPython>=3.1.0 in Containerfile.supervisor specifically because unquoted >= is parsed by the shell as output redirection (pkg>=1.0 → runs pkg, writes a file named =1.0). Verified empirically. The exact same pattern already exists two lines above the typer line this PR adds in Containerfile.c10s/Containerfile.c9s:
PyYAML>=5.1 \
sentry-sdk>=2.13.0 \
+typer \
These are pre-existing (not introduced by this PR), so lower priority, but since the PR is touching this exact list and just fixed the identical bug in a sibling file, it's worth a follow-up: both version pins are silently being dropped and a stray =5.1/=2.13.0 file is left in the build context.
I am always in doubt here, I used to squash commits in the past, because describing them "manually" was too much work and my english is not so good, however now that AI writes commit messages for me, I don't squash them any longer, because I think it is easier, in this way, to review things and decide if something completely wrong has been done. However I squashed them here. |
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>
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>
…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>
Container fixes: - Containerfile.supervisor: - Added git binary (fixes GitPython "Bad git executable" error) - Added sentry-sdk>=2.13.0 (fixes import error in ymir.agents.observability) - Added GitPython>=3.1.0 (fixes ModuleNotFoundError: No module named 'git') - Containerfile.c10s (triage-agent): - Added typer (fixes import error in ymir.cli.main) - Containerfile.c9s: - Added typer for consistency with c10s Makefile targets: - Added `triage-issue`: Run triage agent only (AUTO_CHAIN=false) Usage: make triage-issue ISSUE=RHEL-252788 - Added `process`: Run full pipeline without supervisor (AUTO_CHAIN=true) Usage: make process ISSUE=RHEL-252788 Runs triage → backport/rebase/rebuild chain - Kept `process-issue`: Run supervisor-managed pipeline Usage: make process-issue ISSUE=RHEL-252788 These changes fix ModuleNotFoundError and restore the old pipeline workflow. Related: PACKIT-5281 Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
After rebasing onto upstream/main (commit 0276b38), the _check_zstream_clones_shipped function signature changed from returning tuple[bool, list[str]] to returning ZStreamDependencyResult object. Updated test mocks to match the new API: - test_eligibility_dependency_blocker_zstream_not_affected - test_eligibility_dependency_blocker_zstream_not_affected_error These tests were using the old tuple format (False, ["RHEL-777"]) which caused TypeError when the code tried to access the ZStreamDependencyResult attributes. Also fixed _check_dependency_blocker return statements to properly return tuples matching the function signature tuple[JSONToolOutput | None, list[ShippedZStreamCandidate]]: - Line 1217 (NOT_AFFECTED check exception): Now returns (JSONToolOutput(...), []) - Line 1232 (NOT_AFFECTED clones found): Now returns (None, []) These were returning single values, causing 'cannot unpack non-iterable' errors. Related: PACKIT-5281 Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
1. _check_zstream_pending_triage(): Exclude SUCCESS labels only (backported/rebased/rebuilt).
Failed/errored labels NOT excluded (Z-stream path blocked, Y-stream may be needed).
2. Containerfile shell redirection: Quote pip requirements with >= operators.
Fixed: PyYAML>=5.1, sentry-sdk>=2.13.0, GitPython>=3.1.0
(in Containerfile.supervisor, .c10s, .c9s)
3. NOT_AFFECTED reason: Return specific 'Z-stream clone RHEL-XXX was NOT_AFFECTED' message.
4. Pagination fix: Add fixVersion filter to JQL (SearchJiraIssuesTool max 50 results, no pagination).
5. Duplicate preservation: Pass duplicate_of through _check_for_dependency_blocker().
6. Postponed labels: Exclude ymir_postponed_{dependency,no_patch,pr_pending} from pending-triage JQL.
7. Makefile: Change $(ISSUE) to $(JIRA_ISSUE) in triage-issue/process targets, add guards.
Related: PACKIT-5281
Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
259f0ed to
9dc981a
Compare
Thanks. Though the last two fixup commits should be squashed as well. I'm completely fine with doing that just before merging the PR though, if it makes reviews easier. Although that probably requires re-approvals. |
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:
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
Added
_check_zstream_not_affected()ymir_triaged_not_affectedlabelAdded
_check_zstream_pending_triage()Modified
_check_lowmod_ystream_eligibility()IMMEDIATELY(triage Y-stream)PENDING_DEPENDENCIES(wait for results)NEVER(skip Y-stream - existing behavior)Modified
_check_for_dependency_blocker()Nonewith specific reason (proceed with triage)PENDING_DEPENDENCIES(postpone - existing behavior)Code Review Fixes
After code review by Claude, fixed seven additional issues:
Pending-triage logic: Only exclude SUCCESS labels (ymir_backported/rebased/rebuilt) from pending search. Failed/errored labels are NOT excluded because the Z-stream path is blocked and Y-stream might be needed as fallback.
Shell redirection fix: Quoted pip requirements with
>=operators in three Containerfiles (supervisor, c10s, c9s) to prevent shell redirection interpretation. Unquotedpkg>=1.0is parsed as "run pkg, redirect output to file =1.0".NOT_AFFECTED-specific reason: Return explicit message "Z-stream clone RHEL-XXX was NOT_AFFECTED, checking if Y-stream is also not affected" instead of generic message.
Pagination limit fix: Added fixVersion filter directly to JQL queries. SearchJiraIssuesTool returns only first 50 results with no pagination. Without version filtering, broad CVE/component queries could hit the limit and miss applicable Z-stream clones.
Duplicate preservation fix: The NOT_AFFECTED result path was dropping the
duplicate_offield even though duplicate detection already ran. The triage workflow needs this field to notify maintainers about older rejected trackers. Fixed by passingduplicate_ofthrough_check_for_dependency_blocker()and including it in the NOT_AFFECTED result. Added regression test combining rejected duplicate with NOT_AFFECTED Z-stream.Postponed labels fix:
_check_zstream_pending_triage()excluded deprecatedymir_triaged_postponedbut not the actual labels applied by current code (ymir_postponed_dependency,ymir_postponed_no_patch,ymir_postponed_pr_pending). These are terminal triage decisions (Z-stream was triaged and postponed with a reason), but without exclusion they match "pending triage" JQL. Y-stream then waits indefinitely for Z-stream results that already exist. Fixed by excluding the three active postponed labels. Added parameterized test verifying all three are excluded.Makefile consistency fix: The
triage-issueandprocesstargets used$(ISSUE)instead of$(JIRA_ISSUE), inconsistent with all other targets (process-issue, trigger-pipeline, trigger-reproducer). Users runningmake triage-issue JIRA_ISSUE=RHEL-123got silent failures inside the container. Fixed by changing$(ISSUE)to$(JIRA_ISSUE)and adding guards to fail fast with usage message (like trigger-pipeline has).Related
🤖 Generated with Claude Code