From 29d82bf86efbdc59f5a8eaba8079aa684fef7302 Mon Sep 17 00:00:00 2001
From: "gioacchino.vino" <gioacchino.vino@infn.it>
Date: Fri, 29 Sep 2023 11:37:51 +0200
Subject: [PATCH] Enhanced report summary

---
 files/script.py | 140 ++++++++++++++++++++++++++++++------------------
 1 file changed, 87 insertions(+), 53 deletions(-)

diff --git a/files/script.py b/files/script.py
index 605d64b..0e3c421 100644
--- a/files/script.py
+++ b/files/script.py
@@ -24,7 +24,7 @@ def create_connection():
             gmp.authenticate(auth_name, auth_passwd)
             return gmp
         except:
-            print("WARNING: Connection error with the gmp endpoint. Remaining {retry} retries")
+            logging.warning(f"Connection error with the gmp endpoint. Remaining {retry} retries")
             retry -= 1
             sleep(0.5)
     raise Exception("Impossible connect to the gmp endpoint even after 5 retries")
@@ -32,7 +32,7 @@ def create_connection():
 def get_version():
     gmp = create_connection()
     res = gmp.get_version()
-    return res
+    return res.xpath('version/text()')[0]
 
 ########## PORT LIST ##################################
 
@@ -45,6 +45,7 @@ def create_port_list(port_list_name, ports):
         id = res.xpath('@id')[0]
         return {'name': port_list_name, 'id': id}
     else:
+        logging.error(f"ERROR during Port list creation. Status code: {status}, msg: {status_text}")
         msg = f"ERROR during Port list creation. Status code: {status}, msg: {status_text}"
         raise Exception(msg) 
 
@@ -66,9 +67,9 @@ def delete_port_list(port_list):
     status = res.xpath('@status')[0]
     status_text = res.xpath('@status_text')[0]
     if status == "200":
-        print(f"Port_list with id: {port_list['id']} and name: {port_list['name']} DELETED") 
+        logging.info(f"Port_list with id: {port_list['id']} and name: {port_list['name']} DELETED") 
     else:
-        print(f"ERROR {status}: {status_text}")
+        logging.error(f"ERROR {status}: {status_text}")
 
 def get_or_create_port_list(port_list_name, ports):
     res = get_port_lists(port_list_name)
@@ -78,7 +79,7 @@ def get_or_create_port_list(port_list_name, ports):
     elif len(res) == 1:
         return res[0]
     else:
-        print(f"WARNING Found {len(res)} results.")
+        logging.warning(f"Found {len(res)} results.")
         return res
 
 ############## TARGET  ##################################
@@ -121,9 +122,9 @@ def delete_target(target):
     status = res.xpath('@status')[0]
     status_text = res.xpath('@status_text')[0]
     if status == "200":
-        print(f"Port_list with id: {target['id']} and name: {target['name']} DELETED") 
+        logging.info(f"Port_list with id: {target['id']} and name: {target['name']} DELETED") 
     else:
-        print(f"ERROR {status}: {status_text}")
+        logging.error(f"ERROR {status}: {status_text}")
 
 def get_or_create_target(target_name,ip,port_list,ovs_ssh_credential):
     res = get_targets(target_name)
@@ -238,9 +239,9 @@ def delete_task(task):
     status = res.xpath('@status')[0]
     status_text = res.xpath('@status_text')[0]
     if status == "200":
-        print(f"Target with id: {task['id']} and name: {task['name']} DELETED") 
+        logging.info(f"Target with id: {task['id']} and name: {task['name']} DELETED") 
     else:
-        print(f"ERROR {status}: {status_text}")
+        logging.error(f"ERROR {status}: {status_text}")
 
 ############## REPORTS #####################################3
 
@@ -273,17 +274,21 @@ def get_progress(task):
 
 def wait_for_task_ending(task, timeout=3600):
     start_time = time()
+    logging.info("Waiting for scans ends the task")
     while True:
         status, progress = get_progress(task)
         if status not in ["New","Requested","Queued","Running","Done"]: # ["Interrupted", ...]
+            logging.warning(f"Waiting for scans ends the task. Status: {status}")
             return False
         if status == "Done" and progress == -1:
+            logging.info(f"Waiting for scans ends the task. Status: {status}")
             return True
         if time() - start_time > timeout:
-                print("TIMEOUT during waiting for task ending")
+                logging.error("TIMEOUT during waiting for task ending")
                 return False
-        sleep(60)
-
+        logging.debug(f"Waiting for the task ends. Now {int(time() - start_time)}s from start. Status: {status}")
+        sleep(10)
+    
 def save_report(task,report_format_id, report_filename ):
     gmp = create_connection()
     res = gmp.get_report(task['report_id'],
@@ -311,21 +316,30 @@ def save_severity_report(task, severity_filename):
     with open(severity_filename, "w") as f:
         f.write(severity)
 
-def get_severity(task):
-    dict_severity = {"Log": 0, "Low": 1, "Medium": 2, "High": 3}
+def get_report_info(task):
+    report = dict()
     gmp = create_connection()
     res = gmp.get_report(task['report_id'],
-                        report_format_id=report_formats.anonymous_xml, 
+                        report_format_id=report_formats.anonymous_xml,
                         ignore_pagination=True,
                         details="1")
-    severities = res.xpath('report/report/ports/port/threat/text()')
-    old_num_severity = 0
-    severity = "Log"
-    for sev in severities:
-        if dict_severity[sev] > old_num_severity:
-            old_num_severity = dict_severity[sev]
-            severity = sev
-    return severity
+    threats = res.xpath('report/report/ports/port/threat/text()')
+    ports = res.xpath('report/report/ports/port/text()')
+    severities = res.xpath('report/report/ports/port/severity/text()')
+    severities = list(map(lambda a : float(a), severities))
+    for p,t,s in zip(ports, threats, severities):
+        report[p] = {'severity': s, 'threat': t}
+    glob_severity = -1 # returned severities are null or positive
+    glob_threat = 'Log'
+    for threat,severity in zip(threats,severities):
+        if severity > glob_severity:
+            glob_severity = severity
+            glob_threat = threat
+            glob_severity = severity
+
+    report['global'] = {'threat': glob_threat, 'severity': glob_severity}
+    return report
+
             
 def get_reports(filter_str="rows=-1"):
     lo = []
@@ -362,18 +376,21 @@ def get_severity_from_number(num):
     else:
         return "Undefined"
 
-def process_global_severity(severities):
-    max_num_severity = 0
-    for _,sev in severities.items():
-        numeric_severity = get_numeric_severity(sev)
-        if numeric_severity > max_num_severity:
-            max_num_severity = numeric_severity
-    severities['global_severity'] = get_severity_from_number(max_num_severity)
-    if max_num_severity < 2:
-        severities['global'] = "OK"
+def process_global_reports_info(reports):
+    glob_severity = -1
+    glob_threat = 'Log'
+    for host in reports:
+        host_glob_severity = reports[host]['global']['severity']
+        if host_glob_severity > glob_severity:
+            glob_severity = host_glob_severity
+            glob_threat = reports[host]['global']['threat']
+    reports['deployment'] = {'severity': glob_severity, 
+                             'threat': glob_threat}
+    if reports['deployment']['severity'] < 2:
+        reports['global'] = "OK"
     else:
-        severities['global'] = "NOK"
-    return severities
+        reports['global'] = "NOK"
+    return reports
 
 def pretty_json(j):
     return json.dumps(j,sort_keys=True,indent=4)
@@ -397,7 +414,7 @@ def import_dep_info(file_path, endpoints_to_scan):
                     port = '80'
                 else:
                     raise Exception(f"Impossible to parse the endpoint port. Endpoint: {endpoint}")
-            print(f"Endpoint: {host}:{port}")
+            logging.info(f"Endpoint: {host}:{port}")
             if host not in endpoints:
                 endpoints[host] = {"22"}
             endpoints[host].add(port)
@@ -405,10 +422,28 @@ def import_dep_info(file_path, endpoints_to_scan):
                 
 ################ MAIN #######################################
 
-auth_name = os.getenv('GMP_USER')
-auth_passwd = os.getenv('GMP_PASSWORD')
-print(auth_name, auth_passwd)
-logging.basicConfig(filename='debug.log', level=logging.DEBUG)
+logging.basicConfig(
+    filename='debug.log', 
+    level=logging.DEBUG,
+    format='%(asctime)s %(levelname)-8s %(message)s',
+    datefmt='%Y-%m-%d %H:%M:%S',
+    filemode='w')
+logging.info("\n\nStart scan application")
+
+if os.environ.get('GMP_USER') is not None and \
+        os.environ.get('GMP_USER') != '':
+    auth_name = os.getenv('GMP_USER')
+else:
+    logging.error("GMP_USER env var is not defined\nexit")
+    raise Exception("GMP_USER env var is not defined")
+
+if os.environ.get('GMP_PASSWORD') is not None and \
+        os.environ.get('GMP_PASSWORD') != '':
+    auth_passwd = os.getenv('GMP_PASSWORD')
+else:
+    logging.error("GMP_PASSWORD env var is not defined\nexit")
+    raise Exception("GMP_PASSWORD env var is not defined")
+
 local_ip = "127.0.0.1"
 connection = TLSConnection(hostname=local_ip)
 transform = EtreeTransform()
@@ -434,20 +469,20 @@ logging.info(f"dep_json: {dep_json}")
 logging.info(f"output_dir: {output_dir}")
 
 endpoints = import_dep_info(dep_json, endpoints_to_scan)
-logging.info(f"endpoints\n{pretty_json(endpoints)}")
+logging.info(f"endpoints: {endpoints}")
 
 # test gmp connection
 logging.info(f"gvm version: {get_version()}")
 
-severities = dict()
+reports = dict()
 for host,ports in endpoints.items():
-    logging.info(host,ports)
+    logging.info(f"endpoint: {host}:{ports}")
     
     target_name = f"{auth_name}_target_{host}"
     task_name = f"{auth_name}_task_{host}"
     port_list_name = f"{auth_name}_pl_{host}"
     report_filename = f"{output_dir}/{host}-report.txt"
-    severity_filename = f"{output_dir}/severity.json"
+    summary_filename = f"{output_dir}/summary-report.json"
 
     port_list = get_or_create_port_list(port_list_name,ports)
     logging.info(f"Port list:\n {pretty_json(port_list)}")
@@ -462,15 +497,14 @@ for host,ports in endpoints.items():
         task = start_task(task)
     if wait_for_task_ending(task,wait_timeout):
         save_report(task,report_formats.txt, report_filename)
-        severities[host] = get_severity(task)
+        reports[host] = get_report_info(task)
     else:
-        severities[host] = f"ERROR Task: {task['id']}"
+        reports[host] = f"ERROR Task: {task['id']}"
     
-    delete_task(task)
-    delete_target(target)
-    delete_port_list(port_list)
-
-severities = process_global_severity(severities)
-with open(severity_filename, "w") as f:
-    f.write(json.dumps(severities))
-    
\ No newline at end of file
+    #delete_task(task)
+    #delete_target(target)
+    #delete_port_list(port_list)
+
+reports = process_global_reports_info(reports)
+with open(summary_filename, "w") as f:
+    f.write(json.dumps(reports))  
\ No newline at end of file
-- 
GitLab