2424 Retrieve issues
2525 -g, --generate-graphs
2626 Generate graphs with vulnerabilities info
27+ -svg, --svg-output Generate graphs in SVG format
28+ -png, --png-output Generate graphs in PNG format
2729 -p, --generate-page Generate page with vulnerabilities info
2830 -c, --comparison COMPARISON [COMPARISON ...]
2931 Compare two or more repositories and generate
4042
4143from dateutil import parser
4244
45+ import matplotlib .pyplot as plt
46+ from matplotlib .figure import Figure
47+
4348type DependabotAlert = dict [str , Any ]
4449type DependabotAlerts = list [DependabotAlert ]
4550
@@ -94,6 +99,22 @@ def create_argument_parser() -> ArgumentParser:
9499 help = "Generate graphs with vulnerabilities info" ,
95100 )
96101
102+ cli_parser .add_argument (
103+ "-svg" ,
104+ "--svg-output" ,
105+ action = "store_true" ,
106+ default = False ,
107+ help = "Generate graphs in SVG format" ,
108+ )
109+
110+ cli_parser .add_argument (
111+ "-png" ,
112+ "--png-output" ,
113+ action = "store_true" ,
114+ default = False ,
115+ help = "Generate graphs in PNG format" ,
116+ )
117+
97118 cli_parser .add_argument (
98119 "-p" ,
99120 "--generate-page" ,
@@ -115,6 +136,22 @@ def create_argument_parser() -> ArgumentParser:
115136 return cli_parser
116137
117138
139+ def check_args (args : Namespace ) -> None :
140+ """Check the validity of all command line arguments.
141+
142+ Validate command-line argument consistency.
143+
144+ Ensures that if graph generation is enabled, at least one output format (SVG or PNG) is specified.
145+
146+ Raises:
147+ ValueError: If graph generation is requested but neither SVG nor PNG output is selected.
148+ """
149+ if args .generate_graphs :
150+ # when graphs should be generated, output format need to be specified too
151+ if not args .svg_output and not args .png_output :
152+ raise "Graphs generation was requested: you need to select the PNG and/or SVG output."
153+
154+
118155def dependabot_file_name (args : Namespace ) -> str :
119156 """Construct file name containing Dependabot alerts.
120157
@@ -326,7 +363,7 @@ def fill_in_days_statistic(source_data: DependabotAlerts) -> dict[str, Any]:
326363 days_stat : dict [str , Any ] = {}
327364 days_stat ["days" ] = days
328365 # avoid division by zero
329- if not days :
366+ if days :
330367 days_stat ["avg" ] = sum (days ) / len (days )
331368 days_stat ["median" ] = median (days )
332369 else :
@@ -361,6 +398,18 @@ def fill_in_cve_created_dates(source_data: DependabotAlerts) -> Counter[datetime
361398def process_dependabot_file (dependabot_file : str ) -> dict [str , Any ]:
362399 """
363400 Compute vulnerability statistics from Dependabot alerts stored in a JSON file.
401+
402+ Loads alerts from the specified file and calculates statistics including total count,
403+ state breakdown (open/fixed), severity distribution, fix latency metrics, vulnerable
404+ package frequencies, and CVE detection timeline.
405+
406+ Parameters:
407+ dependabot_file (str): Path to the JSON file containing Dependabot alerts.
408+ prefix (str): Unused; retained for interface compatibility.
409+
410+ Returns:
411+ dict[str, Any]: A dictionary with keys: "count", "state", "severity", "severities",
412+ "days", "packages", and "dates", each containing the corresponding computed metrics.
364413 """
365414 source_data = load_dependabot_file (dependabot_file )
366415
@@ -378,6 +427,70 @@ def process_dependabot_file(dependabot_file: str) -> dict[str, Any]:
378427 return stat
379428
380429
430+ def save_graph (
431+ fig : Figure , prefix : str , postfix : str , svg_output : bool , png_output : bool
432+ ) -> None :
433+ """
434+ Save a figure in SVG and/or PNG formats based on output flags.
435+
436+ Parameters:
437+ prefix (str): Filename prefix
438+ postfix (str): Filename suffix appended after the prefix and underscore
439+ """
440+ if svg_output :
441+ filename = f"{ prefix } _{ postfix } .svg"
442+ plt .savefig (filename , format = "svg" )
443+ if png_output :
444+ filename = f"{ prefix } _{ postfix } .png"
445+ plt .savefig (filename , format = "png" )
446+
447+
448+ def generate_overal_state_graph (
449+ stat : dict [str , Any ], prefix : str , svg_output : bool , png_output : bool
450+ ) -> None :
451+ """
452+ Generate a bar chart showing the distribution of vulnerability states.
453+
454+ Parameters:
455+ stat (dict[str, Any]): Statistics dictionary with a "state" key containing alert counts.
456+ prefix (str): Filename prefix for the saved graph.
457+ svg_output (bool): Whether to save in SVG format.
458+ png_output (bool): Whether to save in PNG format.
459+ """
460+ fig , ax = plt .subplots ()
461+ D = stat ["state" ]
462+ ax .bar (
463+ range (len (D )), list (D .values ()), align = "center" , color = ["#c00000" , "#00c000" ]
464+ )
465+ ax .set_ylim (top = 400 )
466+ ax .set_xticks (range (len (D )), list (D .keys ()))
467+ save_graph (fig , prefix , "state" , svg_output , png_output )
468+
469+
470+ def generate_severity_graph (
471+ stat : dict [str , Any ], prefix : str , svg_output : bool , png_output : bool
472+ ) -> None :
473+ """
474+ Generate a bar chart of vulnerability severity distribution and save it in the specified format.
475+
476+ Parameters:
477+ stat (dict[str, Any]): Statistics dictionary
478+ prefix (str): Prefix for the output file name.
479+ svg_output (bool): If true, save the graph as SVG.
480+ png_output (bool): If true, save the graph as PNG.
481+ """
482+ fig , ax = plt .subplots ()
483+ D = stat ["severity" ]
484+ ax .bar (
485+ range (len (D )),
486+ list (D .values ()),
487+ align = "center" ,
488+ color = ["#c00000" , "orange" , "#e0e000" , "#00c000" ],
489+ )
490+ ax .set_xticks (range (len (D )), list (D .keys ()))
491+ save_graph (fig , prefix , "severity" , svg_output , png_output )
492+
493+
381494def main () -> int :
382495 """
383496 CLI entry point that retrieves Dependabot issues and produces Vulnerability report.
@@ -397,6 +510,7 @@ def main() -> int:
397510 """
398511 cli_parser = create_argument_parser ()
399512 args = cli_parser .parse_args ()
513+ check_args (args )
400514 dependabot_file = dependabot_file_name (args )
401515 prefix = args .repository
402516 stat = process_dependabot_file (dependabot_file )
0 commit comments