1818# along with this program; if not, write to the Free Software Foundation,
1919# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2020#
21- """
21+ """Audits a SonarQube platform"""
2222
23- Audits a SonarQube platform
23+ from __future__ import annotations
2424
25- """
26- import sys
2725import json
2826import csv
29- from typing import TextIO
27+ import re
28+ from typing import TextIO , Optional
3029from threading import Thread
3130from queue import Queue
3231from requests import RequestException
5251 options .WHAT_PORTFOLIOS : portfolios .audit ,
5352}
5453
54+ PROBLEM_KEYS = "problems"
55+
5556
5657def _audit_sif (sysinfo : str , audit_settings : types .ConfigSettings ) -> tuple [str , list [problem .Problem ]]:
5758 """Audits a SIF and return found problems"""
5859 log .info ("Auditing SIF file '%s'" , sysinfo )
5960 try :
60- with open (sysinfo , "r" , encoding = "utf-8" ) as f :
61+ with open (sysinfo , encoding = "utf-8" ) as f :
6162 sysinfo = json .loads (f .read ())
6263 except json .decoder .JSONDecodeError :
6364 log .critical ("File %s does not seem to be a legit JSON file" , sysinfo )
@@ -72,34 +73,48 @@ def _audit_sif(sysinfo: str, audit_settings: types.ConfigSettings) -> tuple[str,
7273 return sif_obj .server_id (), sif_obj .audit (audit_settings )
7374
7475
76+ def __filter_problems (problems : list [problem .Problem ], settings : types .ConfigSettings ) -> list [problem .Problem ]:
77+ """Filters audit problems by severity and/or type and/or problem key"""
78+ if settings .get (options .SEVERITIES , None ):
79+ log .debug ("Filtering audit problems with severities: %s" , settings [options .SEVERITIES ])
80+ problems = [p for p in problems if str (p .severity ) in settings [options .SEVERITIES ]]
81+ if settings .get (options .TYPES , None ):
82+ log .debug ("Filtering audit problems with types: %s" , settings [options .TYPES ])
83+ problems = [p for p in problems if str (p .type ) in settings [options .TYPES ]]
84+ if settings .get (PROBLEM_KEYS , None ):
85+ log .debug ("Filtering audit problems with keys: %s" , settings [PROBLEM_KEYS ])
86+ problems = [p for p in problems if re .match (rf"^{ settings [PROBLEM_KEYS ]} $" , str (p .rule_id ))]
87+ return problems
88+
89+
7590def write_csv (queue : Queue [list [problem .Problem ]], fd : TextIO , settings : types .ConfigSettings ) -> None :
76- """Writes the CSV file of audit problems"""
91+ """Thread callback to write audit problems in a CSV file """
7792 server_id = settings .get ("SERVER_ID" , None )
7893 with_url = settings .get ("WITH_URL" , False )
7994 csvwriter = csv .writer (fd , delimiter = settings .get ("CSV_DELIMITER" , "," ))
8095 header = ["Server Id" ] if server_id else []
81- header += ["Audit Check " , "Category " , "Severity" , "Message" ]
96+ header += ["Problem " , "Type " , "Severity" , "Message" ]
8297 header += ["URL" ] if with_url else []
8398 csvwriter .writerow (header )
8499 while (problems := queue .get ()) is not util .WRITE_END :
100+ problems = __filter_problems (problems , settings )
85101 for p in problems :
86102 json_data = p .to_json (with_url )
87- data = [] if not server_id else [server_id ]
88- data += list ( json_data . values ())
103+ data = [server_id ] if server_id else []
104+ data += [ json_data [ k ] for k in ( "problem" , "type" , "severity" , "message" , "url" ) if k in json_data ]
89105 csvwriter .writerow (data )
90106 queue .task_done ()
91107 queue .task_done ()
92108
93109
94110def write_json (queue : Queue [list [problem .Problem ]], fd : TextIO , settings : types .ConfigSettings ) -> None :
95- """
96- Thread to write problems in a JSON file
97- """
111+ """Thread callback to write problems in a JSON file"""
98112 server_id = settings .get ("SERVER_ID" , None )
99113 with_url = settings .get ("WITH_URL" , False )
100114 comma = ""
101115 print ("[" , file = fd )
102116 while (problems := queue .get ()) is not util .WRITE_END :
117+ problems = __filter_problems (problems , settings )
103118 for p in problems :
104119 json_data = p .to_json (with_url )
105120 if server_id :
@@ -113,7 +128,7 @@ def write_json(queue: Queue[list[problem.Problem]], fd: TextIO, settings: types.
113128
114129
115130def _audit_sq (
116- sq : platform .Platform , settings : types .ConfigSettings , what_to_audit : list [str ] = None , key_list : types .KeyList = None
131+ sq : platform .Platform , settings : types .ConfigSettings , what_to_audit : Optional [ list [str ]] = None , key_list : Optional [ types .KeyList ] = None
117132) -> list [problem .Problem ]:
118133 """Audits a SonarQube/Cloud platform"""
119134 everything = what_to_audit is None
@@ -170,6 +185,24 @@ def __parser_args(desc: str) -> object:
170185 nargs = "*" ,
171186 help = "Pass audit configuration settings on command line (-D<setting>=<value>)" ,
172187 )
188+ parser .add_argument (
189+ f"--{ options .SEVERITIES } " ,
190+ required = False ,
191+ default = None ,
192+ help = "Report only audit problems with the given severities (comma separate values LOW, MEDIUM, HIGH, CRITICAL)" ,
193+ )
194+ parser .add_argument (
195+ f"--{ options .TYPES } " ,
196+ required = False ,
197+ default = None ,
198+ help = "Report only audit problems of the given comma separated problem types" ,
199+ )
200+ parser .add_argument (
201+ f"--{ PROBLEM_KEYS } " ,
202+ required = False ,
203+ default = None ,
204+ help = "Report only audit problems whose type key matches the given regexp" ,
205+ )
173206 args = options .parse_and_check (parser = parser , logger_name = TOOL_NAME , verify_token = False )
174207 if args .sif is None and args .config is None :
175208 util .check_token (args .token )
@@ -178,9 +211,8 @@ def __parser_args(desc: str) -> object:
178211
179212def __check_keys_exist (key_regexp : list [str ], sq : platform .Platform , what : list [str ]) -> None :
180213 """Checks if project keys exist"""
181- if key_regexp and "projects" in what :
182- if len (component_helper .get_components (sq , "projects" , key_regexp )) == 0 :
183- raise options .ArgumentsError (f"No projects found with key matching regexp '{ key_regexp } '" )
214+ if key_regexp and "projects" in what and len (component_helper .get_components (sq , "projects" , key_regexp )) == 0 :
215+ raise options .ArgumentsError (f"No projects found with key matching regexp '{ key_regexp } '" )
184216
185217
186218def main () -> None :
@@ -194,14 +226,14 @@ def main() -> None:
194226 key , value = val [0 ].split ("=" , maxsplit = 1 )
195227 cli_settings [key ] = value
196228 settings = audit_conf .load (TOOL_NAME , cli_settings )
229+ settings |= kwargs
197230 file = ofile = kwargs .pop (options .REPORT_FILE )
198231 fmt = util .deduct_format (kwargs [options .FORMAT ], ofile )
199232 settings .update (
200233 {
201234 "FILE" : file ,
202235 "CSV_DELIMITER" : kwargs [options .CSV_SEPARATOR ],
203236 "WITH_URL" : kwargs [options .WITH_URL ],
204- "threads" : kwargs [options .NBR_THREADS ],
205237 "format" : fmt ,
206238 }
207239 )
@@ -213,8 +245,8 @@ def main() -> None:
213245 file = kwargs ["sif" ]
214246 errcode = errcodes .SIF_AUDIT_ERROR
215247 (settings ["SERVER_ID" ], problems ) = _audit_sif (file , settings )
216- problem . dump_report ( problems , file = ofile , server_id = settings [ "SERVER_ID" ], format = fmt )
217-
248+ problems = __filter_problems ( problems , settings )
249+ problem . dump_report ( problems , file = ofile , server_id = settings [ "SERVER_ID" ], fmt = fmt )
218250 else :
219251 sq = platform .Platform (** kwargs )
220252 sq .verify_connection ()
0 commit comments