Repository navigation
LCORE-5401: Ability to generate list of issues #3154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,10 +7,9 @@ | |
| repositories. | ||
|
|
||
| # Usage: | ||
| usage: vulnerability_report.py [-h] [-v] [--organization ORGANIZATION] | ||
| [--repository REPOSITORY] [-r] | ||
| [-s {auto_dismissed,dismissed,fixed,open}] [-a] | ||
| [-g] [-svg] [-png] [-p] | ||
| usage: vulnerability_report.py [-h] [-v] [--organization ORGANIZATION] [--repository REPOSITORY] | ||
| [-r] [-s {auto_dismissed,dismissed,fixed,open}] [-a] [-g] | ||
| [-f FROM_DATE] [-svg] [-png] [-p] [-i] | ||
| [-c COMPARISON [COMPARISON ...]] | ||
|
|
||
| Vulnerability report tool | ||
|
|
@@ -32,6 +31,10 @@ | |
| -svg, --svg-output Generate graphs in SVG format | ||
| -png, --png-output Generate graphs in PNG format | ||
| -p, --generate-page Generate page with vulnerabilities info | ||
| -i, --generate-issues | ||
| Generate list of issues to be send to JIRA | ||
| -f, --from-date FROM_DATE | ||
| Date filter used when --generate-issues operation is enabled | ||
| -c, --comparison COMPARISON [COMPARISON ...] | ||
| Compare two or more repositories and generate | ||
| comparison report. Multiple JSON files with Dependabot | ||
|
|
@@ -41,9 +44,10 @@ | |
| import json | ||
| import os | ||
| import re | ||
| import sys | ||
| from argparse import ArgumentParser, Namespace | ||
| from collections import Counter | ||
| from datetime import datetime | ||
| from datetime import datetime, timezone | ||
| from statistics import median | ||
| from typing import Any, Optional | ||
|
|
||
|
|
@@ -155,6 +159,22 @@ def create_argument_parser() -> ArgumentParser: | |
| help="Generate page with vulnerabilities info", | ||
| ) | ||
|
|
||
| cli_parser.add_argument( | ||
| "-i", | ||
| "--generate-issues", | ||
| action="store_true", | ||
| default=False, | ||
| help="Generate list of issues to be send to JIRA", | ||
| ) | ||
|
|
||
| cli_parser.add_argument( | ||
| "-f", | ||
| "--from-date", | ||
| action="store", | ||
| default=None, | ||
| help="Date filter used when --generate-issues operation is enabled", | ||
| ) | ||
|
|
||
| cli_parser.add_argument( | ||
| "-c", | ||
| "--comparison", | ||
|
|
@@ -727,6 +747,74 @@ def generate_page( | |
| fout.write(output) | ||
|
|
||
|
|
||
| def parse_datetime(value: str) -> datetime: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Document the new functions. Add brief docstrings for
|
||
| """Parse an ISO-format datetime and convert it to UTC. | ||
|
|
||
| A value without a timezone is treated as UTC. Invalid values raise ValueError. | ||
| """ | ||
| dt = datetime.fromisoformat(value) | ||
| if dt.tzinfo is None: | ||
| dt = dt.replace(tzinfo=timezone.utc) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] edge-case
Suggested fix: Parse and validate |
||
| return dt.astimezone(timezone.utc) | ||
|
|
||
|
|
||
| def is_after(date_str: str, from_date: datetime) -> bool: | ||
| """Return whether the parsed date is strictly later than ``from_date``. | ||
|
|
||
| Invalid date strings raise ValueError; a naive ``from_date`` raises TypeError. | ||
| """ | ||
| return parse_datetime(date_str) > from_date | ||
|
|
||
|
|
||
| def generate_list_of_issues( | ||
| dependabot_file: str, from_date_as_string: Optional[str] | ||
| ) -> None: | ||
| """Print Jira issue lines for alerts, optionally filtered by creation time. | ||
|
|
||
| ``from_date_as_string`` is an ISO-format datetime; only alerts created | ||
| strictly after it are printed. Without it, all alerts are considered. Titles | ||
| have quotes removed and are limited to 255 characters; occurrences of | ||
| ``.konflux/`` are removed from the manifest path. Malformed entries raising | ||
| KeyError, TypeError, or ValueError are reported to stderr and skipped. Other errors, such as AttributeError | ||
| for a non-string manifest path, propagate. | ||
|
|
||
| Raises: | ||
| OSError: If the input file cannot be read. | ||
| ValueError: If JSON decoding or parsing the supplied filter date fails. | ||
|
|
||
| """ | ||
| with open(dependabot_file, "r", encoding="utf-8") as fin: | ||
| issues = json.load(fin) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] edge-case json.load(fin) in generate_list_of_issues may return a non-list JSON value (e.g. a dict or scalar). The subsequent for issue in issues would silently iterate over dict keys or fail with a TypeError, producing confusing errors rather than a clear diagnostic. |
||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [medium] edge-case
Suggested fix: Wrap the per-alert logic in a |
||
| from_date: Optional[datetime] = None | ||
|
|
||
| if from_date_as_string is not None: | ||
| from_date = parse_datetime(from_date_as_string) | ||
|
|
||
| for issue in issues: | ||
| try: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] edge-case
Suggested fix: Add |
||
| path = issue["dependency"]["manifest_path"] | ||
| path = path.replace(".konflux/", "") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| title = f"{issue["security_advisory"]["summary"]} [{path}]" | ||
| title = title.replace('"', "") | ||
| title = title.replace("'", "") | ||
|
Comment on lines
+799
to
+800
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove line breaks from issue titles. If an advisory summary contains a newline, quote removal leaves the newline in
|
||
| # Jira Cloud rejects summaries longer than 255 characters. | ||
| title = title[:255] | ||
| url = issue["html_url"] | ||
|
|
||
| if from_date is None: | ||
| # w/o filtering | ||
| print(f'ISSUE "{title}" "{url}"') | ||
| else: | ||
| # perform filtering | ||
| created_at = issue["created_at"] | ||
| if is_after(created_at, from_date): | ||
| print(f'ISSUE "{title}" "{url}"') | ||
| except (KeyError, TypeError, ValueError) as e: | ||
| # skip malformed entries | ||
| print(f"Error retrieving issue: {e}", file=sys.stderr) | ||
|
|
||
|
|
||
| def is_valid_github_org_repo(name: str) -> bool: | ||
| """ | ||
| Check whether a GitHub organization or repository name is valid. | ||
|
|
@@ -766,10 +854,11 @@ def retrieve_issues( | |
| """ | ||
| Retrieve all Dependabot alerts for a GitHub repository and save them to a JSON file. | ||
|
|
||
| The function validates the organization and repository names, checks that the | ||
| GITHUB_TOKEN environment variable is set, then calls the GitHub Dependabot alerts | ||
| API and follows pagination until all alerts are collected. The resulting alerts | ||
| are written to the specified output file in JSON format. | ||
| The function validates the organization and repository names, checks that | ||
| the GITHUB_TOKEN environment variable is set, then calls the GitHub | ||
| Dependabot alerts API and follows pagination until all alerts are | ||
| collected. The resulting alerts are written to the specified output file in | ||
| JSON format. | ||
|
|
||
| Args: | ||
| organization: GitHub organization name. | ||
|
|
@@ -879,19 +968,23 @@ def process_issues_for_repository( | |
| retrieve_issues_operation: bool = True, | ||
| generate_graphs_operation: bool = True, | ||
| generate_page_operation: bool = True, | ||
| generate_issues_operation: bool = True, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [high] logic-error The generate_issues_operation parameter defaults to True, but the --generate-issues CLI flag defaults to False. The --all-actions code path (line 1058) does not pass args.generate_issues, so it silently defaults to True and always emits JIRA issue lines to stdout even when the user did not request -i. This is inconsistent with the single-repository path (line 1072) which correctly passes args.generate_issues. Suggested fix: Either change the default of generate_issues_operation to False, or explicitly pass generate_issues_operation=args.generate_issues in the all-actions branch. |
||
| from_date: Optional[str] = None, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pass the date filter in the all-actions path.
|
||
| ) -> int: | ||
| """Retrieve alerts or process the local file and generate selected outputs. | ||
|
|
||
| Parameters: | ||
| organization (str): name of GitHub organization | ||
| repository (str): name of GitHub repository | ||
| state_filter (str): used to filter issues with specified state | ||
| state_filter (Optional[str]): used to filter issues with specified state | ||
| dependabot_file (str): file to store Dependabot issues | ||
| svg_output (bool): if set to true, CVE statistic is exported into SVG graphs | ||
| png_output (bool): if set to true, CVE statistic is exported into PNG graphs | ||
| retrieve_issues_operation (bool): enable retrieving Dependabot issues | ||
| generate_graphs_operation (bool): enable generating graphs in SVG or PNG format | ||
| generate_page_operation (bool): enable generating page with overall status | ||
| generate_issues_operation (bool): enable generating list of issues to be send into JIRA | ||
| from_date (Optional[str]): date used to filter issues | ||
|
|
||
| Retrieval takes precedence over report generation and returns its status. | ||
| Otherwise return 0 after processing; file, data, and plotting errors propagate. | ||
|
|
@@ -918,6 +1011,9 @@ def process_issues_for_repository( | |
| if generate_page_operation: | ||
| generate_page(stat, prefix, organization, repository) | ||
|
|
||
| if generate_issues_operation: | ||
| generate_list_of_issues(dependabot_file, from_date) | ||
|
|
||
| return 0 | ||
|
|
||
|
|
||
|
|
@@ -965,6 +1061,7 @@ def main() -> int: | |
| dependabot_file, | ||
| args.svg_output, | ||
| args.png_output, | ||
| from_date=args.from_date, | ||
| ) | ||
| return 1 if errors > 0 else 0 | ||
|
|
||
|
|
@@ -981,6 +1078,8 @@ def main() -> int: | |
| args.retrieve_issues, | ||
| args.generate_graphs, | ||
| args.generate_page, | ||
| args.generate_issues, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] pattern-inconsistency args.generate_issues and args.from_date are passed as positional args at the single-repo call site, while the all_actions call passes from_date=args.from_date as a keyword. Mixing positional and keyword style for the same parameter across call sites is inconsistent. Suggested fix: Use keyword arguments (generate_issues_operation=args.generate_issues, from_date=args.from_date) at the single-repo call site. |
||
| args.from_date, | ||
| ) | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[low] missing-test
No unit tests exist for the new functions
parse_datetime,is_after, orgenerate_list_of_issues, nor for the new--generate-issues/--from-dateCLI argument wiring.