From 825566b74780b0140b75ab372e8636b49f35a5f1 Mon Sep 17 00:00:00 2001 From: Jamie Strandboge Date: Wed, 28 Jan 2026 09:57:23 -0600 Subject: [PATCH] feat: show alert number in alert list --- cvelib/wizard.py | 15 ++++++++++----- tests/test_wizard.py | 46 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/cvelib/wizard.py b/cvelib/wizard.py index 2ef3d80..c309db6 100644 --- a/cvelib/wizard.py +++ b/cvelib/wizard.py @@ -68,7 +68,7 @@ def _checklistItemSortKey(item: str) -> Tuple[str, Tuple[str, int]]: Sorts first by display name, then by URL with numeric suffix handling. - Example: '- [ ] [axios](https://github.com/.../10) (low)' + Example: '- [ ] [axios (#10)](https://github.com/.../10) (low)' Returns: ('axios', ('https://github.com/...', 10)) """ import re @@ -78,6 +78,9 @@ def _checklistItemSortKey(item: str) -> Tuple[str, Tuple[str, int]]: name_match = re.search(r"\] \[([^\]]+)\]", item) display_name = name_match.group(1) if name_match else "" + # Strip (#number) suffix from display name for sorting purposes + display_name = re.sub(r" \(#\d+\)$", "", display_name) + # Extract and sort URL url = _extractUrlFromChecklistItem(item) url_key = _naturalSortKey(url) @@ -373,10 +376,12 @@ def _generateIssueDescription(alerts: List[Dict[str, Any]], org: str, repo: str) ): clauses.append(gh_template_clause_text[alert["type"]]) - # Create checklist item - item: str = ( - f"- [ ] [{alert['display_name']}]({alert['url']}) ({alert['severity']})" - ) + # Create checklist item - extract number from URL if present + display_text: str = alert["display_name"] + url_parts: List[str] = alert["url"].rstrip("/").split("/") + if url_parts and url_parts[-1].isdigit(): + display_text = f"{alert['display_name']} (#{url_parts[-1]})" + item: str = f"- [ ] [{display_text}]({alert['url']}) ({alert['severity']})" if item not in checklist_items: checklist_items.append(item) diff --git a/tests/test_wizard.py b/tests/test_wizard.py index 2901048..3cba9ec 100644 --- a/tests/test_wizard.py +++ b/tests/test_wizard.py @@ -861,7 +861,7 @@ def test__generateIssueDescription(self): # Check key components self.assertIn("The following alerts were issued:", description) - self.assertIn("- [ ] [github.com/uptrace/bun]", description) + self.assertIn("- [ ] [github.com/uptrace/bun (#86)]", description) self.assertIn("(medium)", description) self.assertIn("(low)", description) self.assertIn("security/medium", description) @@ -915,6 +915,40 @@ def test__generateIssueDescription_comprehensive(self): self.assertIn("(low)", description) self.assertIn("security/critical", description) + def test__generateIssueDescription_with_number(self): + """Test generate_issue_description extracts alert number from URL""" + alerts = [ + { + "display_name": "lodash", + "severity": "medium", + "type": "dependabot", + "url": "https://github.com/org/repo/security/dependabot/63", + }, + { + "display_name": "axios", + "severity": "high", + "type": "dependabot", + "url": "https://github.com/org/repo/security/dependabot/42", + }, + { + "display_name": "no-number-pkg", + "severity": "low", + "type": "dependabot", + "url": "https://example.com/no-number-here", + }, + ] + + description = cvelib.wizard._generateIssueDescription( + alerts, "test-org", "test-repo" + ) + + # Check that numbers extracted from URLs are included in display names + self.assertIn("- [ ] [lodash (#63)]", description) + self.assertIn("- [ ] [axios (#42)]", description) + # URL without numeric suffix should not have (#...) in display + self.assertIn("- [ ] [no-number-pkg]", description) + self.assertNotIn("no-number-pkg (#", description) + def test__generateIssueDescription_sorting(self): """Test that checklist items are sorted by display name then URL""" # Create alerts with various display names and URLs to test sorting @@ -968,11 +1002,11 @@ def test__generateIssueDescription_sorting(self): # 3. Then lodash # 4. Then zlib expected_order = [ - "- [ ] [axios](https://github.com/org/repo/security/dependabot/1) (medium)", - "- [ ] [axios](https://github.com/org/repo/security/dependabot/2) (low)", - "- [ ] [axios](https://github.com/org/repo/security/dependabot/10) (high)", - "- [ ] [lodash](https://github.com/org/repo/security/dependabot/4) (critical)", - "- [ ] [zlib](https://github.com/org/repo/security/dependabot/3) (high)", + "- [ ] [axios (#1)](https://github.com/org/repo/security/dependabot/1) (medium)", + "- [ ] [axios (#2)](https://github.com/org/repo/security/dependabot/2) (low)", + "- [ ] [axios (#10)](https://github.com/org/repo/security/dependabot/10) (high)", + "- [ ] [lodash (#4)](https://github.com/org/repo/security/dependabot/4) (critical)", + "- [ ] [zlib (#3)](https://github.com/org/repo/security/dependabot/3) (high)", ] self.assertEqual(checklist_lines, expected_order)