diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index 12b3360..d7e9b41 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -226,32 +226,44 @@ def check_vuln_already_exists(self, vuln): def ticket_get_unique_fields(self, ticket): title = ticket.raw.get('fields', {}).get('summary').encode("ascii").strip() ticketid = ticket.key.encode("ascii") - assets = [] - try: - affected_assets_section = ticket.raw.get('fields', {}).get('description').encode("ascii").split("{panel:title=Affected Assets}")[1].split("{panel}")[0] - assets = list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", affected_assets_section))) - - except Exception as e: - self.logger.error("Ticket IPs regex failed. Ticket ID: {}. Reason: {}".format(ticketid, e)) - assets = [] - - try: - if not assets: - #check if attachment, if so, get assets from attachment - affected_assets_section = self.check_ips_attachment(ticket) - if affected_assets_section: - assets = list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", affected_assets_section))) - except Exception as e: - self.logger.error("Ticket IPs Attachment regex failed. Ticket ID: {}. Reason: {}".format(ticketid, e)) + + assets = self.get_assets_from_description(ticket) + if not assets: + #check if attachment, if so, get assets from attachment + assets = self.get_assets_from_attachment(ticket) return ticketid, title, assets - def check_ips_attachment(self, ticket): - affected_assets_section = [] + def get_assets_from_description(self, ticket, _raw = False): + # Get the assets as a string "host - protocol/port - hostname" separated by "\n" + # structure the text to have the same structure as the assets from the attachment + affected_assets = "" + try: + affected_assets = ticket.raw.get('fields', {}).get('description').encode("ascii").split("{panel:title=Affected Assets}")[1].split("{panel}")[0].replace('\n','').replace(' * ','\n').replace('\n', '', 1) + except Exception as e: + self.logger.error("Unable to process the Ticket's 'Affected Assets'. Ticket ID: {}. Reason: {}".format(ticket, e)) + + if affected_assets: + if _raw: + # from line 406 check if the text in the panel corresponds to having added an attachment + if "added as an attachment" in affected_assets: + return False + return affected_assets + + try: + # if _raw is not true, we return only the IPs of the affected assets + return list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", affected_assets))) + except Exception as e: + self.logger.error("Ticket IPs regex failed. Ticket ID: {}. Reason: {}".format(ticket, e)) + return False + + def get_assets_from_attachment(self, ticket, _raw = False): + # Get the assets as a string "host - protocol/port - hostname" separated by "\n" + affected_assets = [] try: fields = self.jira.issue(ticket.key).raw.get('fields', {}) attachments = fields.get('attachment', {}) - affected_assets_section = "" + affected_assets = "" #we will make sure we get the latest version of the file latest = '' attachment_id = '' @@ -265,12 +277,43 @@ def check_ips_attachment(self, ticket): if latest < item.get('created'): latest = item.get('created') attachment_id = item.get('id') - affected_assets_section = self.jira.attachment(attachment_id).get() + affected_assets = self.jira.attachment(attachment_id).get() except Exception as e: self.logger.error("Failed to get assets from ticket attachment. Ticket ID: {}. Reason: {}".format(ticket, e)) - return affected_assets_section + if affected_assets: + if _raw: + return affected_assets + + try: + # if _raw is not true, we return only the IPs of the affected assets + affected_assets = list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", affected_assets))) + return affected_assets + except Exception as e: + self.logger.error("Ticket IPs Attachment regex failed. Ticket ID: {}. Reason: {}".format(ticket, e)) + + return False + + def parse_asset_to_json(self, asset): + hostname, protocol, port = "", "", "" + asset_info = asset.split(" - ") + ip = asset_info[0] + proto_port = asset_info[1] + # in case there is some case where hostname is not reported at all + if len(asset_info) == 3: + hostname = asset_info[2] + if proto_port != "N/A/N/A": + protocol, port = proto_port.split("/") + + asset_dict = { + "host": ip, + "protocol": protocol, + "port": port, + "hostname": hostname + } + + return asset_dict def clean_old_attachments(self, ticket): fields = ticket.raw.get('fields') @@ -522,7 +565,7 @@ def close_ticket(self, ticketid, resolution, comment): def close_obsolete_tickets(self): # Close tickets older than 12 months, vulnerabilities not solved will get created a new ticket self.logger.info("Closing obsolete tickets older than {} months".format(self.max_time_tracking)) - jql = "labels=vulnerability_management AND created