Skip to content

Commit 01b00fa

Browse files
ide-developerjiridanekclaude
authored
fix(scripts/cve): set Target Version on CVE tracker issues (#3834)
* fix(scripts/cve): add branch information to CVE tracker content output * fix(scripts/cve): add target version field to CVE tracker creation logic Include the RHAIENG_TARGET_VERSION_FIELD in extra fields when `version` is provided in the CVE info and log its value during tracker creation. * test(cve): adopt inline-snapshot for CVE tracker approval tests Add inline-snapshot dependency and configure ruff as its formatter. Replace verbose 60-line nested dict assertions with readable inline snapshots using an adf_to_text() test printer that projects ADF documents into scannable markdown-like text. New tests: - test_build_description_with_version: snapshot of ADF with branch - test_build_description_no_version: snapshot of ADF without branch - test_create_tracker_issue_api_payload: full create_issue kwargs including Target Version (customfield_10855) - test_create_tracker_issue_no_version_omits_target_version Co-authored-by: Jiri Daněk <jdanek@redhat.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 834375d commit 01b00fa

4 files changed

Lines changed: 169 additions & 1 deletion

File tree

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ dev = [
5050
"pygments",
5151
# rich powers the local live progress table in ci/check-image-availability.py
5252
"rich>=14.3.2",
53+
"inline-snapshot>=0.34.1",
5354
]
5455

5556
[tool.uv]
@@ -111,6 +112,10 @@ skip_empty = true
111112

112113
# inspired from https://github.com/red-hat-data-services/ods-ci/blob/master/pyproject.toml
113114

115+
# https://15r10nk.github.io/inline-snapshot/configuration/
116+
[tool.inline-snapshot]
117+
format-command = "ruff format --stdin-filename {filename}"
118+
114119
# https://microsoft.github.io/pyright/#/configuration
115120
[tool.pyright]
116121
typeCheckingMode = "basic"

scripts/cve/create_cve_trackers.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
# Contributors multi-user picker (changelog text may say "involved users").
5252
RHAIENG_CONTRIBUTORS_FIELD = "customfield_10466"
5353

54+
# Target Version (multi-version picker).
55+
RHAIENG_TARGET_VERSION_FIELD = "customfield_10855"
56+
5457
EMBARGOED_SECURITY_LEVEL = "Embargoed Security Issue"
5558
DEFAULT_SECURITY_LEVEL = "Red Hat Employee"
5659

@@ -239,6 +242,16 @@ def build_description(cve_info: CVEInfo, base_url: str = JIRA_DEFAULT_URL, track
239242
)
240243
)]
241244

245+
branch_suffix = f" (branch: {cve_info.version})" if cve_info.version else " (on the respective release branch)"
246+
content.append(_adf_paragraph(
247+
_adf_text("Fix should be applied to: "),
248+
_adf_link(
249+
"https://github.com/red-hat-data-services/notebooks",
250+
"https://github.com/red-hat-data-services/notebooks",
251+
),
252+
_adf_text(branch_suffix),
253+
))
254+
242255
if child_keys:
243256
content.append(_adf_paragraph(
244257
_adf_text(f"Blocked Issues ({count}): ", marks=[{"type": "strong"}]),
@@ -405,12 +418,15 @@ def create_tracker_issue(
405418

406419
contributor_ids = resolve_tracker_contributors(client, cve_info)
407420
extra_fields: dict[str, Any] = dict(team_extra)
421+
if cve_info.version:
422+
extra_fields[RHAIENG_TARGET_VERSION_FIELD] = [{"name": cve_info.version}]
408423
if contributor_ids:
409424
extra_fields[RHAIENG_CONTRIBUTORS_FIELD] = contributors_field_value(contributor_ids)
410425

411426
print(f"\n{'[DRY RUN] ' if dry_run else ''}Creating tracker for {cve_info.cve_id}:")
412427
print(f" Summary: {summary}")
413428
print(f" Version: {cve_info.version}")
429+
print(f" Target Version field: {extra_fields.get(RHAIENG_TARGET_VERSION_FIELD, '(not set)')}")
414430
print(f" Embargoed: {cve_info.is_embargoed}")
415431
print(f" Security level: {security_level}")
416432
print(f" Child issues: {cve_info.issue_count}")

tests/unit/scripts/cve/test_create_cve_trackers.py

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING, cast
3+
from typing import TYPE_CHECKING, Any, cast
4+
5+
from inline_snapshot import snapshot
46

57
from scripts.cve import create_cve_trackers as cct
68

@@ -10,6 +12,30 @@
1012
from scripts.cve.jira_client import JiraClient
1113

1214

15+
def _render_adf_inline(node: dict) -> str:
16+
text = node.get("text", "")
17+
marks = node.get("marks", [])
18+
for mark in marks:
19+
if mark["type"] == "strong":
20+
text = f"**{text}**"
21+
elif mark["type"] == "link":
22+
href = mark.get("attrs", {}).get("href", "")
23+
text = f"[{text}]({href})"
24+
return text
25+
26+
27+
def adf_to_text(doc: dict) -> str:
28+
parts: list[str] = []
29+
for node in doc.get("content", []):
30+
if node["type"] == "paragraph":
31+
line = "".join(_render_adf_inline(child) for child in node.get("content", []))
32+
parts.append(line)
33+
elif node["type"] == "codeBlock":
34+
code = "".join(child.get("text", "") for child in node.get("content", []))
35+
parts.append(f"`{code}`")
36+
return "\n".join(parts)
37+
38+
1339
def test_extract_cve_id_from_label_and_summary(subtests: Subtests) -> None:
1440
cases = [
1541
("CVE-2026-8643", "CVE-2026-8643"),
@@ -145,3 +171,106 @@ def search_issues(self, jql: str, fields: str, max_results: int = 500) -> list[d
145171
assert info.contributor_account_ids == {"user-a"}
146172
assert info.cve_id == "CVE-2026-8643"
147173
assert info.version == "rhoai-2.25"
174+
175+
176+
def test_build_description_with_version() -> None:
177+
info = cct.CVEInfo(
178+
cve_id="CVE-2026-8643",
179+
version="rhoai-2.25",
180+
description="Path traversal flaw",
181+
issues=[{"key": "RHOAIENG-64025"}, {"key": "RHOAIENG-64026"}],
182+
)
183+
assert adf_to_text(cct.build_description(info)) == snapshot("""\
184+
Tracker for CVE-2026-8643 - Path traversal flaw affecting Notebooks Images components.
185+
Fix should be applied to: [https://github.com/red-hat-data-services/notebooks](https://github.com/red-hat-data-services/notebooks) (branch: rhoai-2.25)
186+
**Blocked Issues (2): **RHOAIENG-64025, RHOAIENG-64026
187+
**JQL Query to View All Blocked Issues: **
188+
[View all 2 blocked issues](https://redhat.atlassian.net/issues/?jql=key%20in%20%28RHOAIENG-64025%2C%20RHOAIENG-64026%29%20ORDER%20BY%20key%20ASC)\
189+
""")
190+
191+
192+
def test_build_description_no_version() -> None:
193+
info = cct.CVEInfo(
194+
cve_id="CVE-2026-8643",
195+
version="",
196+
description="Path traversal flaw",
197+
)
198+
assert adf_to_text(cct.build_description(info)) == snapshot("""\
199+
Tracker for CVE-2026-8643 - Path traversal flaw affecting Notebooks Images components.
200+
Fix should be applied to: [https://github.com/red-hat-data-services/notebooks](https://github.com/red-hat-data-services/notebooks) (on the respective release branch)\
201+
""")
202+
203+
204+
def test_create_tracker_issue_api_payload(monkeypatch: MonkeyPatch) -> None:
205+
captured: dict[str, Any] = {}
206+
207+
class FakeClient:
208+
def create_issue(self, **kwargs: Any) -> dict:
209+
captured.update(kwargs)
210+
return {"key": "RHAIENG-9999"}
211+
212+
def get_current_user(self) -> dict:
213+
return {"accountId": "runner-id-123"}
214+
215+
monkeypatch.delenv("JIRA_RHAIENG_EXTRA_CONTRIBUTORS", raising=False)
216+
monkeypatch.delenv("JIRA_RUNNER_ACCOUNT_ID", raising=False)
217+
218+
info = cct.CVEInfo(
219+
cve_id="CVE-2026-8643",
220+
version="rhoai-3.3",
221+
description="Path traversal flaw",
222+
issues=[{"key": "RHOAIENG-64025"}],
223+
is_embargoed=False,
224+
contributor_account_ids={"child-contrib-id"},
225+
)
226+
227+
result = cct.create_tracker_issue(cast("JiraClient", FakeClient()), info)
228+
assert result == "RHAIENG-9999"
229+
230+
assert adf_to_text(captured.pop("description")) == snapshot("""\
231+
Tracker for CVE-2026-8643 - Path traversal flaw affecting Notebooks Images components.
232+
Fix should be applied to: [https://github.com/red-hat-data-services/notebooks](https://github.com/red-hat-data-services/notebooks) (branch: rhoai-3.3)
233+
**Blocked Issues (1): **RHOAIENG-64025
234+
**JQL Query to View All Blocked Issues: **
235+
[View all 1 blocked issues](https://redhat.atlassian.net/issues/?jql=key%20in%20%28RHOAIENG-64025%29%20ORDER%20BY%20key%20ASC)\
236+
""")
237+
238+
assert captured == snapshot(
239+
{
240+
"project_key": "RHAIENG",
241+
"summary": "CVE-2026-8643 Path traversal flaw [rhoai-3.3]",
242+
"issue_type": "Bug",
243+
"labels": ["CVE", "CVE-2026-8643", "security"],
244+
"components": ["Notebooks"],
245+
"security_level": "Red Hat Employee",
246+
"extra_fields": {
247+
"customfield_10001": "ec74d716-af36-4b3c-950f-f79213d08f71-62",
248+
"customfield_10855": [{"name": "rhoai-3.3"}],
249+
"customfield_10466": [{"accountId": "child-contrib-id"}, {"accountId": "runner-id-123"}],
250+
},
251+
}
252+
)
253+
254+
255+
def test_create_tracker_issue_no_version_omits_target_version(monkeypatch: MonkeyPatch) -> None:
256+
captured: dict[str, Any] = {}
257+
258+
class FakeClient:
259+
def create_issue(self, **kwargs: Any) -> dict:
260+
captured.update(kwargs)
261+
return {"key": "RHAIENG-8888"}
262+
263+
def get_current_user(self) -> dict:
264+
return {"accountId": "runner-id-123"}
265+
266+
monkeypatch.delenv("JIRA_RHAIENG_EXTRA_CONTRIBUTORS", raising=False)
267+
monkeypatch.delenv("JIRA_RUNNER_ACCOUNT_ID", raising=False)
268+
269+
info = cct.CVEInfo(
270+
cve_id="CVE-2026-9999",
271+
version="",
272+
description="Some flaw",
273+
)
274+
275+
cct.create_tracker_issue(cast("JiraClient", FakeClient()), info)
276+
assert cct.RHAIENG_TARGET_VERSION_FIELD not in captured.get("extra_fields", {})

uv.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)