class TestPyCVESearch(unittest.TestCase): def setUp(self): self.cve = CVESearch() def test_browse(self): self.cve.browse('microsoft') def test_search(self): self.cve.search('microsoft/office') def test_id(self): self.cve.id('CVE-2014-0160') def test_last(self): self.cve.last() def test_last_50(self): self.cve.last(50) def test_dbinfo(self): self.cve.dbinfo() def test_cpe22(self): self.cve.cpe22('cpe:2.3:a:microsoft:office:2011:-:mac') def test_cpe23(self): self.cve.cpe23('cpe/a:microsoft:office:2011:-:mac') def test_cvefor(self): self.cve.cvefor('cpe:/a:microsoft:office:2011::mac')
def get_cve_enrichment_new(cve_id): cve = CVESearch(CVESSEARCH_API_URL) result = cve.id(cve_id) cve_enriched = dict() cve_enriched['id'] = cve_id cve_enriched['cvss'] = result['cvss'] cve_enriched['summary'] = result['summary'] return cve_enriched
def parseXML(xml_filename=""): xml_re = re.compile("\.xml") comma_re = re.compile(",") db_filename = xml_re.sub('.db', xml_filename) conn = sqlite3.connect(db_filename) cursor = conn.cursor() create_query = "CREATE TABLE IF NOT EXISTS cve_details (title, published, cve_desc, cvss DOUBLE);" cursor.execute(create_query) create_query = "CREATE TABLE IF NOT EXISTS reference (ref_url, ref_desc, cve_id);" cursor.execute(create_query) create_query = "CREATE TABLE IF NOT EXISTS affected_systems (vendor, product, ver, cve_id);" cursor.execute(create_query) truncate_query = "DELETE FROM cve_details;" cursor.execute(truncate_query) truncate_query = "DELETE FROM reference;" cursor.execute(truncate_query) truncate_query = "DELETE FROM affected_systems;" cursor.execute(truncate_query) conn.commit() conn.close() try: root_node = parse(xml_filename) cve_dict = {} cve = CVESearch() epoch = datetime(1970, 1, 1) benchtime = (datetime.now() - relativedelta.relativedelta(months=3)) benchtimestamp = int((benchtime - epoch).total_seconds()) vulnerabilities = root_node.getElementsByTagName("Vulnerability") for vulnerability in vulnerabilities: title = vulnerability.getElementsByTagName( "Title")[0].firstChild.nodeValue notes = vulnerability.getElementsByTagName("Notes") for note in notes: stored_notes = note.getElementsByTagName("Note") desc = "" published = 0 for stored_note in stored_notes: type = stored_note.getAttribute("Type") if type == "Description": desc = stored_note.firstChild.nodeValue if type == "Other": note_title = stored_note.getAttribute("Title") if note_title == "Published": published_time = stored_note.firstChild.nodeValue published = int(( datetime.strptime(published_time, "%Y-%m-%d") - epoch).total_seconds()) if published >= benchtimestamp: ref_list = [] try: references = vulnerability.getElementsByTagName( "References") stored_references = references[0].getElementsByTagName( "Reference") for reference in stored_references: url = reference.getElementsByTagName( "URL")[0].firstChild.nodeValue ref_desc = reference.getElementsByTagName( "Description")[0].firstChild.nodeValue ref_list.append({"url": url, "desc": ref_desc}) except Exception as e: print(e.with_traceback, end='\r', flush=True) conn = sqlite3.connect(db_filename) cursor = conn.cursor() insert_query = "INSERT INTO cve_details VALUES (?, ?, ?, ?);" insert_values = (title, published, desc, 0.0) cursor.execute(insert_query, insert_values) stored_id = cursor.lastrowid insert_query = "INSERT INTO reference VALUES (?, ?, ?);" for reference in ref_list: insert_values = (reference["url"], reference["desc"], stored_id) cursor.execute(insert_query, insert_values) conn.commit() conn.close() try: result = cve.id(title) affected_systems = result["vulnerable_configuration"] conn = sqlite3.connect(db_filename) cursor = conn.cursor() insert_query = "INSERT INTO affected_systems VALUES (?, ?, ?, ?);" for affected_system in affected_systems: cpe = affected_system["id"] cpe_values = cpe.split(":") vendor = cpe_values[3] product = cpe_values[4] try: version = cpe_values[5] except: version = "" insert_values = (vendor, product, version, stored_id) cursor.execute(insert_query, insert_values) conn.commit() cvss = float(result["cvss"]) update_query = "UPDATE cve_details SET cvss=:cvss WHERE rowid = :rowid;" update_values = {"cvss": cvss, "rowid": stored_id} cursor.execute(update_query, update_values) conn.commit() conn.close() except Exception as e: print(e, end='\r', flush=True) result = {} print("[*] Parsing data for {}".format(title), end='\r', flush=True) output_filename = xml_re.sub(".csv", xml_filename) csv_data = "Sr. No, Title, Description, Published, Severity, Affected System, References\n" srno = 1 confs = readConf() conn = sqlite3.connect(db_filename) cursor_product = conn.cursor() for desc in confs.keys(): product = confs[desc]["product"] vendor = confs[desc]["vendor"] version = confs[desc]["version"] sql_query = "SELECT * FROM affected_systems WHERE product=:product AND vendor=:vendor;" values = {"product": product, "vendor": vendor} results = cursor_product.execute(sql_query, values) cursor_cve = conn.cursor() cursor_ref = conn.cursor() for row in results: (as_vendor, as_product, as_version, cve_id) = row sql_query = "SELECT * FROM cve_details WHERE rowid=:rowid ORDER BY cvss DESC;" values = {"rowid": cve_id} cve_results = cursor_cve.execute(sql_query, values) for cve_details in cve_results: title = cve_details[0] published = datetime.utcfromtimestamp( cve_details[1]).strftime('%Y-%m-%d') cve_desc = unicodedata.normalize( 'NFKD', cve_details[2]).encode('ascii', 'ignore').decode('ascii') cve_desc = comma_re.sub(";", cve_desc) cvss = cve_details[3] references = "" sql_query = "SELECT * FROM reference WHERE cve_id=:rowid;" refs = cursor_ref.execute(sql_query, values) for ref in refs: references += "{} ({});".format(ref[1], ref[0]) csv_data += "{}, {}, \"{}\", {}, {}, {}, {}\n".format( srno, title, cve_desc, published, cvss, desc, references) srno += 1 conn.close() csv_file = open(output_filename, "w") csv_file.write(csv_data) csv_file.close() print("[*] Output written to {}".format(output_filename)) except Exception as e: print("[-] Unable to parse XML file. {}".format(e))
def main(): cve = CVESearch() # Beginning the PyCVESearch api search latex_script_string = "" latex_ip_table_string = "" latex_devices_table_string = "" latex_cve_table = "" cve_severity_average = 0 cve_severity_average_counter = 0 cve_severity_low_total = 0 cve_severity_medium_total = 0 cve_severity_high_total = 0 cve_severity_critical_total = 0 latex_technical_specifics = "" latex_technical_specifics_vulnerable_configs_column_count = 1 time_now = datetime.datetime.now() formatted_time = time_now.strftime( "%Y-%m-%d %H:%M") # Get the time and date for filenames scan_question = input( time_now.strftime("%H:%M") + " Do you have an IP to scan ? (Y/N): " ) # If user wishes to run a simple NMAP scan , if not they can use an existing results file if scan_question == "Y" or scan_question == "y": scan_ip = input( time_now.strftime("%H:%M") + " Enter IP Address to be scanned: ") # Enter IP address for scan nmap_process = subprocess.Popen([ 'nmap', '-v', '-sV', '--script', 'nmap-vulners', '-oX', formatted_time + '.xml', scan_ip ]) # Run the NMAP scan nmap_output = nmap_process.communicate() results_file = formatted_time + '.xml' # Grabs result file created from NMAP scan else: file_extension_check_loop = True while file_extension_check_loop: # loop to make sure an existing file is used results_file = input( time_now.strftime("%H:%M") + " Enter NMAP output .xml file name: " ) # Input .xml file name with or without .xml file extension file_extension_check = results_file[ -4:] # Checks if .xml file extension was added if file_extension_check != ".xml": # Adds .xml to end of input if not results_file = results_file + ".xml" try: file_exist_check = open(results_file, "r") # Checking if file exists file_extension_check_loop = False except FileNotFoundError: print( time_now.strftime("%H:%M") + " ERROR: File not found please input an existing NMAP .xml output file" ) # File doesnt exist, loops back latex_title_input = input( time_now.strftime("%H:%M") + " Please enter organisation title: ") try: # Testing the file is readable untangle_object = untangle.parse( results_file) # Object file created from .xml results except SAXParseException: print(time_now.strftime("%H:%M") + " ERROR: File unreadable") exit() untangle_object_scan_start_time = untangle_object.nmaprun["startstr"] untangle_object_scan_finish_time = untangle_object.nmaprun.runstats.finished[ "timestr"] untangle_object_hosts = untangle_object.nmaprun.host for untangle_object_host in untangle_object_hosts: # Looping through the hosts in NMAP output untangle_object_host_address_ip = "" untangle_object_combined_ports = "" untangle_object_combined_devices = "" latex_technical_specifics_combined = "" cve_severity_low = 0 cve_severity_medium = 0 cve_severity_high = 0 cve_severity_critical = 0 cve_severity_total = 0 for untangle_object_host_addresstype in untangle_object_host.address: if untangle_object_host_addresstype[ "addrtype"] == "ipv4": # Parses out only the ipv4 address, stops crashes with mac address untangle_object_host_address_ip = untangle_object_host_addresstype[ "addr"] print( time_now.strftime("%H:%M") + " PARSING: IP Address: {}".format( untangle_object_host_address_ip)) try: # Testing untangle_object_host_addresstypeif the NMAP has performed OS detection or not untangle_object_vendors = set() untangle_object_osmatch_iterator = untangle_object_host.os.osmatch for untangle_object_osmatch in untangle_object_osmatch_iterator: # Looping through fingerprinted OS for each host untangle_object_device = untangle_object_osmatch["name"] print( time_now.strftime("%H:%M") + " Device: {}".format(untangle_object_device)) untangle_object_osclass_iterator = untangle_object_osmatch.osclass for untangle_object_osclass in untangle_object_osclass_iterator: untangle_object_vendor = untangle_object_osclass["vendor"] if untangle_object_vendor is not None: untangle_object_vendors.add(untangle_object_vendor) print( time_now.strftime("%H:%M") + " PARSING: OS Vendors: {}".format(untangle_object_vendors)) for vendor in untangle_object_vendors: untangle_object_combined_devices = untangle_object_combined_devices + vendor + " " except AttributeError as e: print( time_now.strftime("%H:%M") + " ERROR: No OS fingerprinting detected") untangle_object_combined_devices = untangle_object_combined_devices + "No OS identified" + " " latex_cve_table = latex_cve_table + "\subsection{Critical and high CVE details of host: " + untangle_object_host_address_ip + "}" # Begins string of CVE table for current port untangle_object_ports = untangle_object_host.ports.port for untangle_object_port in untangle_object_ports: # Looping through all open ports discovered for each host untangle_object_combined_scriptoutput = "" untangle_object_combined_cve = "" print( time_now.strftime("%H:%M") + " PARSING: Port: {}".format(untangle_object_port["portid"])) try: untangle_object_scripts = untangle_object_port.script for untangle_object_script in untangle_object_scripts: # Looping through each script on the current port untangle_object_cve_regex = re.findall( r"(CVE-\d*-\d*)\t*(\d*.\d)\t*(https:\/\/vulners.com\/cve\/CVE-\d*-\d*)", untangle_object_script["output"], flags=re.MULTILINE ) # Using Regex to search for the CVE, its severity and URL from the nmap-vulners script if untangle_object_script[ "id"] == "vulners": # CVE information will only be pulled out for scripts that were run by nmap-vulners for untangle_object_cve in untangle_object_cve_regex: latex_technical_specifics_summary = "" latex_technical_specifics_vulnerable_configs = "" latex_technical_specifics_references = "" latex_technical_specifics_cve_cwe_cvss_table = "" cve_severity_average = cve_severity_average + float( untangle_object_cve[1] ) # Calculations for average severity score from each CVE cve_severity_average_counter = cve_severity_average_counter + 1 cve_search_api = cve.id( untangle_object_cve[0] ) # Using PyCVESearch to search the API for current CVE if "id" in cve_search_api: print( time_now.strftime("%H:%M") + " API SEARCH: CVE ID: " + cve_search_api["id"]) latex_technical_specifics_cve_cwe_cvss_table = "\\begin{center}\scalebox{1.5}{\\begin{tabular}{lr}\hline\multicolumn{2}{c}{" + cve_search_api[ "id"] + " Information} \\\\" else: print( time_now.strftime("%H:%M") + " ERROR: No CVE ID available") if "summary" in cve_search_api: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: Summary: " + cve_search_api["summary"]) latex_technical_specifics_summary = latex_technical_specifics_summary + "\paragraph{Summary} \mbox{} \\\\" + "\n" + "\\detokenize{" + cve_search_api[ "summary"] + "}" latex_technical_specifics_combined = latex_technical_specifics_combined + latex_technical_specifics_summary else: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH ERROR: Summary: No information available" ) latex_technical_specifics_summary = latex_technical_specifics_summary + "\paragraph{Summary} \mbox{} \\\\" + "\n" + "Information unavailable" + "}" latex_technical_specifics_combined = latex_technical_specifics_combined + latex_technical_specifics_summary if "cvss" in cve_search_api: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: CVSS Score: " + str(cve_search_api["cvss"])) latex_technical_specifics_cve_cwe_cvss_table = latex_technical_specifics_cve_cwe_cvss_table + "\hline CVSS Score & " + str( cve_search_api["cvss"]) + "\\\\CWE & " else: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH ERROR: CVSS Score: No information available" ) if "cwe" in cve_search_api: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: CWE: " + cve_search_api["cwe"]) latex_technical_specifics_cve_cwe_cvss_table = latex_technical_specifics_cve_cwe_cvss_table + cve_search_api[ "cwe"] + "\\\\\hline" else: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH ERROR: CWE: No information available" ) if "impact" in cve_search_api: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: Confidentiality impact: " + cve_search_api["impact"]["confidentiality"] ) print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: Integrity impact: " + cve_search_api["impact"]["integrity"]) print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: Availability impact: " + cve_search_api["impact"]["availability"]) latex_technical_specifics_cve_cwe_cvss_table = latex_technical_specifics_cve_cwe_cvss_table + " Vulnerability impact \\\\\hline Confidentiality & " + cve_search_api[ "impact"][ "confidentiality"] + "\\\\Integrity & " + cve_search_api[ "impact"][ "integrity"] + "\\\\Availability & " + cve_search_api[ "impact"][ "availability"] + "\\\\\hline" else: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH ERROR: Impact: No information available" ) if "access" in cve_search_api: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: Acess authentication: " + cve_search_api["access"]["authentication"]) print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: Access complexity: " + cve_search_api["access"]["complexity"]) print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH: Access vector: " + cve_search_api["access"]["vector"]) latex_technical_specifics_cve_cwe_cvss_table = latex_technical_specifics_cve_cwe_cvss_table + " Access methodology information \\\\\hline Vector & " + cve_search_api[ "access"]["vector"] + "\\\\Complexity & " + cve_search_api[ "access"][ "complexity"] + "\\\\Authentication & \detokenize{" + cve_search_api[ "access"][ "authentication"] + "}\\\\\hline" else: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH ERROR: Access: No information available" ) latex_technical_specifics_combined = latex_technical_specifics_combined + latex_technical_specifics_cve_cwe_cvss_table + "\end{tabular}}\end{center}" if "vulnerable_configuration" in cve_search_api: latex_technical_specifics_vulnerable_configs = latex_technical_specifics_vulnerable_configs + "\\begin{tiny}\\begin{spacing}{1.0} \n \paragraph{Vulnerable configs} \mbox{} \\\\" + "\n" for vulnerable_config in cve_search_api[ "vulnerable_configuration"]: latex_technical_specifics_vulnerable_configs = latex_technical_specifics_vulnerable_configs + "\\detokenize{" + vulnerable_config[ "title"] + "}" + " \hfill " if latex_technical_specifics_vulnerable_configs_column_count % 3 == 0: # This creates 3 columns of the vulnerable configurations to save room latex_technical_specifics_vulnerable_configs = latex_technical_specifics_vulnerable_configs + r'''\par\noindent''' latex_technical_specifics_vulnerable_configs_column_count = latex_technical_specifics_vulnerable_configs_column_count + 1 latex_technical_specifics_combined = latex_technical_specifics_combined + latex_technical_specifics_vulnerable_configs + r'''\\\\''' + "\\end{spacing}" + "\\end{tiny}" else: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH ERROR: Vulnerable configurations: No information available" ) latex_technical_specifics_vulnerable_configs = latex_technical_specifics_vulnerable_configs + "\paragraph{Vulnerable configs} \mbox{} \\\\" + "No information available" latex_technical_specifics_combined = latex_technical_specifics_combined + latex_technical_specifics_vulnerable_configs if "references" in cve_search_api: latex_technical_specifics_references = latex_technical_specifics_references + "\paragraph{References} \mbox{} \\\\" for reference in cve_search_api["references"]: latex_technical_specifics_references = latex_technical_specifics_references + "\href{" + reference + "}{\\detokenize{" + reference + "}} \\\\" latex_technical_specifics_references_nopercent = re.sub( r"%", "\%", latex_technical_specifics_references) latex_technical_specifics_combined = latex_technical_specifics_combined + latex_technical_specifics_references_nopercent + r'''\\\\''' else: print( time_now.strftime("%H:%M ") + cve_search_api["id"] + " API SEARCH ERROR: References: No information available" ) latex_technical_specifics_references = latex_technical_specifics_references + "\paragraph{References} \mbox{} \\\\" + "No information available" latex_technical_specifics_combined = latex_technical_specifics_combined + latex_technical_specifics_references latex_technical_specifics_combined = latex_technical_specifics_combined + "\\newpage" # Completes the report page for current CVE cve_severity = float(untangle_object_cve[1]) if cve_severity <= 3: # Determines the colour of severity cell in CVE table cve_severity_low = cve_severity_low + 1 cve_severity_total = cve_severity_total + 1 cve_severity_low_total = cve_severity_low_total + 1 else: if cve_severity > 3 and cve_severity <= 6: cve_severity_medium = cve_severity_medium + 1 cve_severity_total = cve_severity_total + 1 cve_severity_medium_total = cve_severity_medium_total + 1 else: if cve_severity > 6 and cve_severity < 9: cve_severity_total = cve_severity_total + 1 cve_severity_high = cve_severity_high + 1 cve_severity_high_total = cve_severity_high_total + 1 else: if cve_severity >= 9: cve_severity_total = cve_severity_total + 1 cve_severity_critical = cve_severity_critical + 1 cve_severity_critical_total = cve_severity_critical_total + 1 latex_cve_table = latex_cve_table + re.sub( r"(REGEXCVETABLEREGEX)", untangle_object_combined_cve, latex_template_cve_table, count=0, flags=0 ) # Creating the table string from table template latex_cve_table = re.sub( r"(REGEXPORTHERE)", untangle_object_port["portid"], latex_cve_table, count=0, flags=0) untangle_object_combined_scriptoutput = untangle_object_combined_scriptoutput + " NMAP Script run: " + untangle_object_script[ "id"] + " NMAP Script Result: " + untangle_object_script[ "output"] # Creating a string of the raw scripts information of every script run except AttributeError as e: print( time_now.strftime("%H:%M") + " ERROR: No scripts detected") untangle_object_combined_ports = untangle_object_combined_ports + untangle_object_port[ "portid"] + " " # Creating a combined string of each host and its discovered ports latex_script_string = latex_script_string + untangle_object_combined_scriptoutput + "\n" # Creating a LaTeX string from template file and combined script string latex_technical_specifics = latex_technical_specifics + "\\newpage\subsection{Vulnerability details of host: " + untangle_object_host_address_ip + "}" # Begins string of CVE table for current port latex_technical_specifics_perhost_severity_string = "Critical\cellcolor{red}&\cellcolor{red} " + str( cve_severity_critical ) + r'\\\\' + "High\cellcolor{orange}&\cellcolor{orange}" + str( cve_severity_high ) + r'''\\\\''' + "Medium\cellcolor{yellow}&\cellcolor{yellow}" + str( cve_severity_medium ) + r'''\\\\''' + "Low \cellcolor{green}&\cellcolor{green}" + str( cve_severity_low) + r'''\\\\\hline Total& ''' + str( cve_severity_total) latex_technical_specifics_perhost_severity = re.sub( r"(REGEXPERHOSTSEVERITYREGEX)", latex_technical_specifics_perhost_severity_string, latex_template_technical_specifics_perhost_severity, count=0, flags=0) latex_technical_specifics = latex_technical_specifics + latex_technical_specifics_perhost_severity + latex_technical_specifics_combined latex_cve_table = latex_cve_table + r'''\newpage''' latex_devices_table_string = latex_devices_table_string + untangle_object_host_address_ip + "&" + untangle_object_combined_devices + r'''\\\\''' # Creating a LaTeX table string from template file and combined devices string latex_ip_table_string = latex_ip_table_string + untangle_object_host_address_ip + " & " + untangle_object_combined_ports + "&" + untangle_object_combined_devices + r'''\\\\''' # Creating a LaTeX table string from template file and combined ports string latex_ip_table_string = latex_ip_table_string + "\hline Scan start time & " + untangle_object_scan_start_time + r'\\\\' + "Scan end time & " + untangle_object_scan_finish_time + r'\\\\' cve_severity_average = cve_severity_average / cve_severity_average_counter # Finishing calculation of severity average latex_title = re.sub( r"(REGEXTITLEHEREREGEX)", latex_title_input, latex_template_title, count=0, flags=0 ) # Using Regex to replace the placeholder title in title LaTeX template latex_executive_summary = re.sub( r"(REGEXTITLEHEREREGEX)", latex_title_input, latex_template_executive_summary, count=0, flags=0 ) # Using Regex to replace the placeholder title in executive summary LaTeX template if cve_severity_average == 0: # Determines what executive summary is chosen, determined by the average severity score latex_executive_summary = re.sub( r"(REGEXRISKSTATEMENTREGEX)", latex_template_executive_summary_risk_none, latex_executive_summary, count=0, flags=0) else: if cve_severity_average > 0 and cve_severity_average <= 3: latex_executive_summary = re.sub( r"(REGEXRISKSTATEMENTREGEX)", latex_template_executive_summary_risk_low, latex_executive_summary, count=0, flags=0) else: if cve_severity_average > 3 and cve_severity_average <= 6: latex_executive_summary = re.sub( r"(REGEXRISKSTATEMENTREGEX)", latex_template_executive_summary_risk_medium, latex_executive_summary, count=0, flags=0) else: if cve_severity_average > 6: latex_executive_summary = re.sub( r"(REGEXRISKSTATEMENTREGEX)", latex_template_executive_summary_risk_high, latex_executive_summary, count=0, flags=0) latex_overall_severity_results_string = "Critical\cellcolor{red}&\cellcolor{red} " + str( cve_severity_critical_total ) + r'\\\\' + "High\cellcolor{orange}&\cellcolor{orange}" + str( cve_severity_high_total ) + r'''\\\\''' + "Medium\cellcolor{yellow}&\cellcolor{yellow}" + str( cve_severity_medium_total ) + r'''\\\\''' + "Low \cellcolor{green}&\cellcolor{green}" + str( cve_severity_low_total) + r'''\\\\\hline Total& ''' + str( cve_severity_average_counter) latex_overall_severity_results = re.sub( r"(REGEXSEVERITYOVERALLHEREREGEX)", latex_overall_severity_results_string, latex_template_technical_specifics_overall_severity_results, count=0, flags=0) latex_results_table = re.sub( r"(REGEXIPPORTTABLEHEREREGEX)", latex_ip_table_string, latex_template_results_table, count=0, flags=0 ) # Using Regex to replace the placeholder table in results table LaTeX template with the host and ports latex_raw_results = re.sub( r"(REGEXSCRIPTSTUFFREGEX)", latex_script_string, latex_template_raw_results, count=0, flags=0 ) # Using Regex to replace the raw scripts placeholder in LateX template file_latex = open(latex_title_input + 'GeneratedReport.tex', "w+") # Creating the LaTeX .tex file latex_template = latex_template_packages + latex_title + latex_executive_summary + latex_template_technical_specifics + latex_results_table + latex_overall_severity_results + latex_technical_specifics + latex_raw_results # Combining the LaTeX template strings to create the entire template file_latex.write( latex_template) # Writing the LaTeX template to the .tex file file_latex.close() pdflatex_process = subprocess.Popen( ['pdflatex', latex_title_input + 'GeneratedReport.tex'] ) # Generate PDF report from LaTex .tex file (LaTeX only produces contents file on second report generation) pdflatex_process.communicate() pdflatex_process = subprocess.Popen( ['pdflatex', latex_title_input + 'GeneratedReport.tex'] ) # Run a second time as pdfLatex produces contents .toc file after starting pdf generation pdflatex_process.communicate()