From e183249bc562f0b7f24b4d4f6e6a4703677b1441 Mon Sep 17 00:00:00 2001 From: Pavel Tisnovsky Date: Mon, 22 Jun 2026 09:16:22 +0200 Subject: [PATCH] LCORE-2631: Vulnerability report script: processing part --- scripts/vulnerability_report.py | 121 +++++++++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 9 deletions(-) diff --git a/scripts/vulnerability_report.py b/scripts/vulnerability_report.py index 8e1ae096b..89c1b21e9 100644 --- a/scripts/vulnerability_report.py +++ b/scripts/vulnerability_report.py @@ -5,9 +5,39 @@ Rretrieves Dependabot issues, analyses issues, generates graphs, and generates HTML page with Vulnerability report. Is is also possible to compare two repositories. + +# Usage: +usage: vulnerability_report.py [-h] [-v] --organization ORGANIZATION + --repository REPOSITORY [-r] [-g] [-p] + [-c COMPARISON [COMPARISON ...]] + +Vulnerability report tool + +options: + -h, --help show this help message and exit + -v, --verbose make it verbose + --organization ORGANIZATION + GitHub organization. + --repository REPOSITORY + GitHub repository. + -r, --retrieve-issues + Retrieve issues + -g, --generate-graphs + Generate graphs with vulnerabilities info + -p, --generate-page Generate page with vulnerabilities info + -c, --comparison COMPARISON [COMPARISON ...] + Compare two or more repositories and generate + comparison report. Multiple JSON files with Dependabot + alerts needs to be provided """ -from argparse import ArgumentParser +from argparse import ArgumentParser, Namespace +import json + +from typing import Any + +type DependabotAlert = dict[str, Any] +type DependabotAlerts = list[DependabotAlert] def create_argument_parser() -> ArgumentParser: @@ -46,35 +76,107 @@ def create_argument_parser() -> ArgumentParser: parser.add_argument( "-r", "--retrieve-issues", - default=True, + action="store_true", + default=False, help="Retrieve issues", ) parser.add_argument( "-g", "--generate-graphs", - default=True, + action="store_true", + default=False, help="Generate graphs with vulnerabilities info", ) parser.add_argument( "-p", "--generate-page", - default=True, + action="store_true", + default=False, help="Generate page with vulnerabilities info", ) parser.add_argument( "-c", "--comparison", - default=False, - help="Compare two repositories and generate comparison report. " - "Need to be used with --data1 and --data2 options", + required=False, + nargs="+", + default=[], + help="Compare two or more repositories and generate comparison report. " + "Multiple JSON files with Dependabot alerts needs to be provided", ) return parser +def dependabot_file_name(args: Namespace) -> str: + """Construct file name containing Dependabot alerts.""" + return f"{args.organization}__{args.repository}.json" + + +def load_dependabot_file(filename: str) -> Any: + """Load JSON file containing Dependabot alerts.""" + with open(filename, "r") as fin: + return json.load(fin) + + +def has_attribute_with_value(item: DependabotAlert, attribute: str, value: str) -> bool: + """Check if dictionary has attribute with given value.""" + return bool(item[attribute] == value) + + +def has_deep_attribute_with_value( + item: DependabotAlert, selector: str, attribute: str, value: str +) -> bool: + """Check if dictionary has deep attribute with given value.""" + return bool(item[selector][attribute] == value) + + +def count_attribute_with_value( + items: DependabotAlerts, attribute: str, value: str +) -> int: + """Count all attributes with given value.""" + cnt: int = 0 + for item in items: + if has_attribute_with_value(item, attribute, value): + cnt += 1 + return cnt + + +def count_deep_attribute_with_value( + items: DependabotAlerts, selector: str, attribute: str, value: str +) -> int: + """Count all deep attributes with given value.""" + cnt: int = 0 + for item in items: + if has_deep_attribute_with_value(item, selector, attribute, value): + cnt += 1 + return cnt + + +def opened_cves(source_data: DependabotAlerts) -> int: + """Compute how many CVEs are opened.""" + return count_attribute_with_value(source_data, "state", "open") + + +def fixed_cves(source_data: DependabotAlerts) -> int: + """Compute how many CVEs has been fixed opened.""" + return count_attribute_with_value(source_data, "state", "fixed") + + +def with_severity(severity: str, source_data: DependabotAlerts) -> int: + """Count number of CVE having specified severity.""" + return count_deep_attribute_with_value( + source_data, "security_advisory", "severity", severity + ) + + +def process_dependabot_file(dependabot_file: str, prefix: str) -> dict[str, Any]: + """Read Dependabot alerts and prepare statistic info.""" + return {} + + def main() -> int: """ CLI entry point that retrieves Dependabot issues and produces Vulnerability report. @@ -94,8 +196,9 @@ def main() -> int: """ parser = create_argument_parser() args = parser.parse_args() - - print(args) + dependabot_file = dependabot_file_name(args) + prefix = args.repository + process_dependabot_file(dependabot_file, prefix) return 0