From b0caccdc8954b128f5940d5ba60087929ff09321 Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 12 Feb 2019 16:25:28 +0100 Subject: [PATCH 01/96] fixed issues plus jira comment formatting --- docker-compose.v6.yml | 4 ++-- docker-compose.yml | 2 +- vulnwhisp/reporting/jira_api.py | 18 +++++++++++++----- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/docker-compose.v6.yml b/docker-compose.v6.yml index 205ce1d..58daac9 100644 --- a/docker-compose.v6.yml +++ b/docker-compose.v6.yml @@ -6,7 +6,7 @@ services: environment: - cluster.name=vulnwhisperer - bootstrap.memory_lock=true - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - "ES_JAVA_OPTS=-Xms1g -Xmx1g" - xpack.security.enabled=false ulimits: @@ -21,7 +21,7 @@ services: - esdata1:/usr/share/elasticsearch/data ports: - 9200:9200 - restart: always + #restart: always networks: esnet: aliases: diff --git a/docker-compose.yml b/docker-compose.yml index 4f26266..61cdae3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,7 @@ services: - 9200:9200 environment: - xpack.security.enabled=false - restart: always + #restart: always networks: esnet: aliases: diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index 3c76b12..49fd0be 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -39,8 +39,8 @@ def create_ticket(self, title, desc, project="IS", components=[], tags=[]): for tag in tags: labels.append(str(tag)) - self.logger.info("creating ticket for project {} title[20] {}".format(project, title[:20])) - self.logger.info("project {} has a component requirement: {}".format(project, self.PROJECT_COMPONENT_TABLE[project])) + self.logger.info("creating ticket for project {} title: {}".format(project, title[:20])) + self.logger.info("project {} has a component requirement: {}".format(project, components)) project_obj = self.jira.project(project) components_ticket = [] for component in components: @@ -205,13 +205,21 @@ def ticket_update_assets(self, vuln, ticketid, ticket_assets): difference = list(set(assets).symmetric_difference(ticket_assets)) comment = '' + added = '' + removed = '' #put a comment with the assets that have been added/removed for asset in difference: if asset in assets: - comment += "Asset {} have been added to the ticket as vulnerability *has been newly detected*.\n".format(asset) + if not added: + added = 'The following assets *have been newly detected*:\n' + added += '* {}\n'.format(asset) elif asset in ticket_assets: - comment += "Asset {} have been removed from the ticket as vulnerability *has been resolved*.\n".format(asset) - + if not removed: + removed= 'The following assets *have been resolved*:\n' + removed += '* {}\n'.format(asset) + + comment = added + removed + try: ticket_obj.update(description=tpl, comment=comment, fields={"labels":ticket_obj.fields.labels}) self.logger.info("Ticket {} updated successfully".format(ticketid)) From ccf2e4b1d17facafd9140aa594140d3b0df028f0 Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 12 Feb 2019 16:51:26 +0100 Subject: [PATCH 02/96] fix #147 --- vulnwhisp/vulnwhisp.py | 43 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index a728896..fd7c1aa 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -838,32 +838,29 @@ def whisper_reports(self, else: self.logger.info('Processing report ID: {}'.format(report_id)) vuln_ready = self.qualys_scan.process_data(scan_id=report_id) - if not vuln_ready.empty: - vuln_ready['scan_name'] = scan_name - vuln_ready['scan_reference'] = report_id - vuln_ready.rename(columns=self.COLUMN_MAPPING, inplace=True) + vuln_ready['scan_name'] = scan_name + vuln_ready['scan_reference'] = report_id + vuln_ready.rename(columns=self.COLUMN_MAPPING, inplace=True) - record_meta = ( - scan_name, - scan_reference, - launched_date, - report_name, - time.time(), - vuln_ready.shape[0], - self.CONFIG_SECTION, - report_id, - 1, - ) - self.record_insert(record_meta) + record_meta = ( + scan_name, + scan_reference, + launched_date, + report_name, + time.time(), + vuln_ready.shape[0], + self.CONFIG_SECTION, + report_id, + 1, + ) + self.record_insert(record_meta) - if output_format == 'json': - with open(relative_path_name, 'w') as f: - f.write(vuln_ready.to_json(orient='records', lines=True)) - f.write('\n') + if output_format == 'json': + with open(relative_path_name, 'w') as f: + f.write(vuln_ready.to_json(orient='records', lines=True)) + f.write('\n') - self.logger.info('Report written to {}'.format(report_name)) - else: - return False + self.logger.info('Report written to {}'.format(report_name)) except Exception as e: self.logger.error('Could not process {}: {}'.format(report_id, str(e))) From 8c5398727025bcdb9da933e7d4a8d81822604a65 Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 12 Feb 2019 16:56:00 +0100 Subject: [PATCH 03/96] tracking of processing was in debug instead of info logging --- vulnwhisp/vulnwhisp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index fd7c1aa..edd6e11 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -625,7 +625,7 @@ def process_web_assets(self): for app in self.scans_to_process.iterrows(): counter += 1 r = app[1] - self.logger.debug('Processing {}/{}'.format(counter, len(self.scans_to_process))) + self.logger.info('Processing {}/{}'.format(counter, len(self.scans_to_process))) self.whisper_reports(report_id=r['id'], launched_date=r['launchedDate'], scan_name=r['name'], @@ -884,7 +884,7 @@ def process_vuln_scans(self): for app in self.scans_to_process.iterrows(): counter += 1 r = app[1] - self.logger.debug('Processing {}/{}'.format(counter, len(self.scans_to_process))) + self.logger.info('Processing {}/{}'.format(counter, len(self.scans_to_process))) self.whisper_reports(report_id=r['id'], launched_date=r['date'], scan_name=r['name'], From bc3367e3109c7f2fe41adc9092c09c52f0df6d35 Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 12 Feb 2019 18:01:46 +0100 Subject: [PATCH 04/96] exception of empty scans --- vulnwhisp/vulnwhisp.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index edd6e11..9d6da74 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -1041,8 +1041,12 @@ def parse_qualys_vuln_vulnerabilities(self, fullpath, source, scan_name, min_cri risks = ['info', 'low', 'medium', 'high', 'critical'] # +1 as array is 0-4, but score is 1-5 min_risk = int([i for i,x in enumerate(risks) if x == min_critical][0])+1 - - data=[json.loads(line) for line in open(fullpath).readlines()] + + try: + data=[json.loads(line) for line in open(fullpath).readlines()] + except Exception as e: + self.logger.warn("Scan has no vulnerabilities, skipping.") + return vulnerabilities #qualys fields we want - [] for index in range(len(data)): @@ -1142,8 +1146,8 @@ def jira_sync(self, source, scan_name): self.jira.sync(vulnerabilities, project, components) else: - self.logger.info("Vulnerabilities from {source} has not been parsed! Exiting...".format(source=source)) - sys.exit(0) + self.logger.info("[{source}.{scan_name}] No vulnerabilities or vulnerabilities not parsed.".format(source=source, scan_name=scan_name)) + return False return True From 177c2548baeabb0b5ff90856c94970ad2ab5eb2d Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 12 Feb 2019 18:18:24 +0100 Subject: [PATCH 05/96] allow jira sync module to run after the rest --- configs/frameworks_example.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/frameworks_example.ini b/configs/frameworks_example.ini index 2f778ca..3d7d0ae 100755 --- a/configs/frameworks_example.ini +++ b/configs/frameworks_example.ini @@ -89,6 +89,7 @@ verbose=true #proxy_password = proxypass [jira] +enabled = false hostname = jira-host username = username password = password From 587546a72646ffef5af66f51a3932d3f68ac9a6d Mon Sep 17 00:00:00 2001 From: Quim Date: Thu, 14 Feb 2019 14:16:31 +0100 Subject: [PATCH 06/96] fix typo --- vulnwhisp/reporting/jira_api.py | 6 +++--- vulnwhisp/reporting/resources/ticket.tpl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index 49fd0be..b6bf9f3 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -295,8 +295,8 @@ def is_risk_accepted(self, ticket_obj): if "risk_accepted" in labels: self.logger.warn("Ticket {} accepted risk, will be ignored".format(ticket_obj)) return True - elif "server_decomission" in labels: - self.logger.warn("Ticket {} server decomissioned, will be ignored".format(ticket_obj)) + elif "server_decommission" in labels: + self.logger.warn("Ticket {} server decommissioned, will be ignored".format(ticket_obj)) return True self.logger.info("Ticket {} risk has not been accepted".format(ticket_obj)) return False @@ -312,7 +312,7 @@ def reopen_ticket(self, ticketid): if self.is_ticket_reopenable(ticket_obj): comment = '''This ticket has been reopened due to the vulnerability not having been fixed (if multiple assets are affected, all need to be fixed; if the server is down, lastest known vulnerability might be the one reported). In the case of the team accepting the risk and wanting to close the ticket, please add the label "*risk_accepted*" to the ticket before closing it. - If server has been decomissioned, please add the label "*server_decomission*" to the ticket before closing it. + If server has been decommissioned, please add the label "*server_decommission*" to the ticket before closing it. If you have further doubts, please contact the Security Team.''' error = self.jira.transition_issue(issue=ticketid, transition=self.JIRA_REOPEN_ISSUE, comment = comment) self.logger.info("Ticket {} reopened successfully".format(ticketid)) diff --git a/vulnwhisp/reporting/resources/ticket.tpl b/vulnwhisp/reporting/resources/ticket.tpl index f00d267..675a560 100644 --- a/vulnwhisp/reporting/resources/ticket.tpl +++ b/vulnwhisp/reporting/resources/ticket.tpl @@ -29,4 +29,4 @@ Please do not delete or modify the ticket assigned tags or title, as they are us In the case of the team accepting the risk and wanting to close the ticket, please add the label "*risk_accepted*" to the ticket before closing it. -If server has been decomissioned, please add the label "*server_decomission*" to the ticket before closing it. +If server has been decommissioned, please add the label "*server_decommission*" to the ticket before closing it. From c2d80c7fced3ddf6dc96af39c30198b7f597e2c2 Mon Sep 17 00:00:00 2001 From: Quim Date: Fri, 15 Feb 2019 16:24:52 +0100 Subject: [PATCH 07/96] made host resolution optional from the config file with dns_resolv var --- configs/frameworks_example.ini | 1 + elk6/vulnwhisperer.ini | 1 + vulnwhisp/vulnwhisp.py | 52 ++++++++++++++++++++-------------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/configs/frameworks_example.ini b/configs/frameworks_example.ini index 3d7d0ae..77d283c 100755 --- a/configs/frameworks_example.ini +++ b/configs/frameworks_example.ini @@ -96,6 +96,7 @@ password = password write_path = /opt/VulnWhisperer/data/jira/ db_path = /opt/VulnWhisperer/data/database verbose = true +dns_resolv = False #Sample jira report scan, will automatically be created for existent scans #[jira.qualys_vuln.test_scan] diff --git a/elk6/vulnwhisperer.ini b/elk6/vulnwhisperer.ini index 12c2d7c..2b92761 100644 --- a/elk6/vulnwhisperer.ini +++ b/elk6/vulnwhisperer.ini @@ -95,6 +95,7 @@ password = password write_path = /opt/vulnwhisperer/data/jira/ db_path = /opt/vulnwhisperer/data/database verbose = true +dns_resolv = False #Sample jira report scan, will automatically be created for existent scans #[jira.qualys_vuln.test_scan] diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index 9d6da74..287e919 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -983,8 +983,17 @@ def get_env_variables(self, source, scan_name): if not fullpath: self.logger.error('Scan file path "{scan_name}" for source "{source}" has not been found.'.format(scan_name=scan_name, source=source)) sys.exit(1) + + dns_resolv = self.config.get('jira','dns_resolv') + if dns_resolv in ('False', 'false', ''): + dns_resolv = False + elif dns_resolv in ('True', 'true'): + dns_resolv = True + else: + self.logger.error("dns_resolv variable not setup in [jira] section; will not do dns resolution") + dns_resolv = False - return project, components, fullpath, min_critical + return project, components, fullpath, min_critical, dns_resolv def parse_nessus_vulnerabilities(self, fullpath, source, scan_name, min_critical): @@ -1033,7 +1042,7 @@ def parse_nessus_vulnerabilities(self, fullpath, source, scan_name, min_critical return vulnerabilities - def parse_qualys_vuln_vulnerabilities(self, fullpath, source, scan_name, min_critical): + def parse_qualys_vuln_vulnerabilities(self, fullpath, source, scan_name, min_critical, dns_resolv = False): #parsing of the qualys vulnerabilities schema #parse json vulnerabilities = [] @@ -1070,7 +1079,7 @@ def parse_qualys_vuln_vulnerabilities(self, fullpath, source, scan_name, min_cri vuln['ips'] = [] #TODO ADDED DNS RESOLUTION FROM QUALYS! \n SEPARATORS INSTEAD OF \\n! - vuln['ips'].append("{ip} - {protocol}/{port} - {dns}".format(**self.get_asset_fields(data[index]))) + vuln['ips'].append("{ip} - {protocol}/{port} - {dns}".format(**self.get_asset_fields(data[index], dns_resolv))) #different risk system than Nessus! vuln['risk'] = risks[int(data[index]['risk'])-1] @@ -1085,31 +1094,32 @@ def parse_qualys_vuln_vulnerabilities(self, fullpath, source, scan_name, min_cri # grouping assets by vulnerability to open on single ticket, as each asset has its own nessus entry for vuln in vulnerabilities: if vuln['title'] == data[index]['plugin_name']: - vuln['ips'].append("{ip} - {protocol}/{port} - {dns}".format(**self.get_asset_fields(data[index]))) + vuln['ips'].append("{ip} - {protocol}/{port} - {dns}".format(**self.get_asset_fields(data[index], dns_resolv))) return vulnerabilities - def get_asset_fields(self, vuln): + def get_asset_fields(self, vuln, dns_resolv): values = {} values['ip'] = vuln['ip'] values['protocol'] = vuln['protocol'] values['port'] = vuln['port'] values['dns'] = '' - if vuln['dns']: - values['dns'] = vuln['dns'] - else: - if values['ip'] in self.host_resolv_cache.keys(): - self.logger.debug("Hostname from {ip} cached, retrieving from cache.".format(ip=values['ip'])) - values['dns'] = self.host_resolv_cache[values['ip']] + if dns_resolv: + if vuln['dns']: + values['dns'] = vuln['dns'] else: - self.logger.debug("No hostname, trying to resolve {ip}'s hostname.".format(ip=values['ip'])) - try: - values['dns'] = socket.gethostbyaddr(vuln['ip'])[0] - self.host_resolv_cache[values['ip']] = values['dns'] - self.logger.debug("Hostname found: {hostname}.".format(hostname=values['dns'])) - except: - self.host_resolv_cache[values['ip']] = '' - self.logger.debug("Hostname not found for: {ip}.".format(ip=values['ip'])) + if values['ip'] in self.host_resolv_cache.keys(): + self.logger.debug("Hostname from {ip} cached, retrieving from cache.".format(ip=values['ip'])) + values['dns'] = self.host_resolv_cache[values['ip']] + else: + self.logger.debug("No hostname, trying to resolve {ip}'s hostname.".format(ip=values['ip'])) + try: + values['dns'] = socket.gethostbyaddr(vuln['ip'])[0] + self.host_resolv_cache[values['ip']] = values['dns'] + self.logger.debug("Hostname found: {hostname}.".format(hostname=values['dns'])) + except: + self.host_resolv_cache[values['ip']] = '' + self.logger.debug("Hostname not found for: {ip}.".format(ip=values['ip'])) for key in values.keys(): if not values[key]: @@ -1127,7 +1137,7 @@ def parse_vulnerabilities(self, fullpath, source, scan_name, min_critical): def jira_sync(self, source, scan_name): self.logger.info("Jira Sync triggered for source '{source}' and scan '{scan_name}'".format(source=source, scan_name=scan_name)) - project, components, fullpath, min_critical = self.get_env_variables(source, scan_name) + project, components, fullpath, min_critical, dns_resolv = self.get_env_variables(source, scan_name) vulnerabilities = [] @@ -1137,7 +1147,7 @@ def jira_sync(self, source, scan_name): #***Qualys VM parsing*** if source == "qualys_vuln": - vulnerabilities = self.parse_qualys_vuln_vulnerabilities(fullpath, source, scan_name, min_critical) + vulnerabilities = self.parse_qualys_vuln_vulnerabilities(fullpath, source, scan_name, min_critical, dns_resolv) #***JIRA sync*** if vulnerabilities: From 521184d079a58bfbccbb58dc80284cf908470508 Mon Sep 17 00:00:00 2001 From: Quim Montal Date: Thu, 21 Feb 2019 22:20:19 +0100 Subject: [PATCH 08/96] Update bug_report.md added debug trail request --- .github/ISSUE_TEMPLATE/bug_report.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a540ec1..b4c7dd4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,7 +11,10 @@ assignees: '' A clear and concise description of what the bug is. **Affected module** -Which one is the module that is not working as expected, e.g. Nessus, Qualys WAS, Qualys VM, OpenVAS, ELK, Jira...) +Which one is the module that is not working as expected, e.g. Nessus, Qualys WAS, Qualys VM, OpenVAS, ELK, Jira...). + +**VulnWhisperer debug trail** +If applicable, paste the VulnWhisperer debug trail of the execution for further detail (execute with '-d' flag). **To Reproduce** Steps to reproduce the behavior: @@ -27,8 +30,9 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **System in which VulnWhisperer runs (please complete the following information):** - - OS: [e.g. iOS] - - Version [e.g. 22] + - OS: [e.g. Ubuntu Server] + - Version: [e.g. 18.04.2 LTS] + - VulnWhisperer Version: [e.g. 1.7.1] **Additional context** Add any other context about the problem here. From 2c7965d2d9ff06e15b13990d72026b2cc8c1f895 Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 25 Feb 2019 12:08:04 +0100 Subject: [PATCH 09/96] fix #151 --- vulnwhisp/reporting/jira_api.py | 120 +++++++++++++++++++++++++++----- 1 file changed, 103 insertions(+), 17 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index b6bf9f3..04afe36 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -29,18 +29,20 @@ def __init__(self, hostname=None, username=None, password=None, path="", debug=F self.JIRA_RESOLUTION_FIXED = "Fixed" self.clean_obsolete = clean_obsolete self.template_path = 'vulnwhisp/reporting/resources/ticket.tpl' + self.max_ips_ticket = 30 + self.attachment_filename = "vulnerable_assets.txt" if path: self.download_tickets(path) else: self.logger.warn("No local path specified, skipping Jira ticket download.") - def create_ticket(self, title, desc, project="IS", components=[], tags=[]): + def create_ticket(self, title, desc, project="IS", components=[], tags=[], attachment_contents = []): labels = ['vulnerability_management'] for tag in tags: labels.append(str(tag)) self.logger.info("creating ticket for project {} title: {}".format(project, title[:20])) - self.logger.info("project {} has a component requirement: {}".format(project, components)) + self.logger.debug("project {} has a component requirement: {}".format(project, components)) project_obj = self.jira.project(project) components_ticket = [] for component in components: @@ -60,8 +62,12 @@ def create_ticket(self, title, desc, project="IS", components=[], tags=[]): issuetype={'name': 'Bug'}, labels=labels, components=components_ticket) - + self.logger.info("Ticket {} created successfully".format(new_issue)) + + if attachment_contents: + self.add_content_as_attachment(new_issue, attachment_contents) + return new_issue #Basic JIRA Metrics @@ -108,13 +114,18 @@ def sync(self, vulnerabilities, project, components=[]): self.ticket_update_assets(vuln, ticketid, ticket_assets) self.add_label(ticketid, vuln['risk']) continue - + attachment_contents = [] + # if assets >30, add as attachment + # create local text file with assets, attach it to ticket + if len(vuln['ips']) > self.max_ips_ticket: + attachment_contents = vuln['ips'] + vuln['ips'] = ["Affected hosts ({assets}) exceed Jira's allowed character limit, added as an attachment.".format(assets = len(attachment_contents))] try: tpl = template(self.template_path, vuln) except Exception as e: self.logger.error('Exception templating: {}'.format(str(e))) return 0 - self.create_ticket(title=vuln['title'], desc=tpl, project=project, components=components, tags=[vuln['source'], vuln['scan_name'], 'vulnerability', vuln['risk']]) + self.create_ticket(title=vuln['title'], desc=tpl, project=project, components=components, tags=[vuln['source'], vuln['scan_name'], 'vulnerability', vuln['risk']], attachment_contents = attachment_contents) self.close_fixed_tickets(vulnerabilities) # we reinitialize so the next sync redoes the query with their specific variables @@ -159,12 +170,72 @@ def ticket_get_unique_fields(self, ticket): 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: - self.logger.error("Ticket IPs regex failed. Ticket ID: {}".format(ticketid)) + + 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 regex failed. Ticket ID: {}. Reason: {}".format(ticketid, e)) assets = [] return ticketid, title, assets + def check_ips_attachment(self, ticket): + affected_assets_section = [] + try: + fields = self.jira.issue(ticket.key).raw.get('fields') + attachments = fields.get('attachment') + affected_assets_section = "" + #we will make sure we get the latest version of the file + latest = '' + attachment_id = '' + if attachments: + for item in attachments: + if item.get('filename') == self.attachment_filename: + if not latest: + latest = item.get('created') + attachment_id = item.get('id') + else: + if latest < item.get('created'): + latest = item.get('created') + attachment_id = item.get('id') + affected_assets_section = 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 + + def clean_old_attachments(self, ticket): + fields = ticket.raw.get('fields') + attachments = fields.get('attachment') + if attachments: + for item in attachments: + if item.get('filename') == self.attachment_filename: + self.jira.delete_attachment(item.get('id')) + + def add_content_as_attachment(self, issue, contents): + try: + #Create the file locally with the data + attachment_file = open(self.attachment_filename, "w") + attachment_file.write("\n".join(contents)) + attachment_file.close() + #Push the created file to the ticket + attachment_file = open(self.attachment_filename, "rb") + self.jira.add_attachment(issue, attachment_file, self.attachment_filename) + attachment_file.close() + #remove the temp file + os.remove(self.attachment_filename) + self.logger.info("Added attachment successfully.") + except: + self.logger.error("Error while attaching file to ticket.") + return False + + return True + def get_ticket_reported_assets(self, ticket): #[METRICS] return a list with all the affected assets for that vulnerability (including already resolved ones) return list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b",str(self.jira.issue(ticket).raw)))) @@ -193,12 +264,8 @@ def ticket_update_assets(self, vuln, ticketid, ticket_assets): if self.is_ticket_resolved(self.jira.issue(ticketid)): self.reopen_ticket(ticketid) - try: - tpl = template(self.template_path, vuln) - except Exception as e: - self.logger.error('Exception updating assets: {}'.format(str(e))) - return 0 - + + #First will do the comparison of assets ticket_obj = self.jira.issue(ticketid) ticket_obj.update() assets = list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", ",".join(vuln['ips'])))) @@ -211,21 +278,40 @@ def ticket_update_assets(self, vuln, ticketid, ticket_assets): for asset in difference: if asset in assets: if not added: - added = 'The following assets *have been newly detected*:\n' + added = '\nThe following assets *have been newly detected*:\n' added += '* {}\n'.format(asset) elif asset in ticket_assets: if not removed: - removed= 'The following assets *have been resolved*:\n' + removed= '\nThe following assets *have been resolved*:\n' removed += '* {}\n'.format(asset) comment = added + removed + + #then will check if assets are too many that need to be added as an attachment + attachment_contents = [] + if len(vuln['ips']) > self.max_ips_ticket: + attachment_contents = vuln['ips'] + vuln['ips'] = ["Affected hosts ({assets}) exceed Jira's allowed character limit, added as an attachment.".format(assets = len(attachment_contents))] + + #fill the ticket description template + try: + tpl = template(self.template_path, vuln) + except Exception as e: + self.logger.error('Exception updating assets: {}'.format(str(e))) + return 0 + #proceed checking if it requires adding as an attachment try: + #update attachment with hosts and delete the old versions + if attachment_contents: + self.clean_old_attachments(ticket_obj) + self.add_content_as_attachment(ticket_obj, attachment_contents) + ticket_obj.update(description=tpl, comment=comment, fields={"labels":ticket_obj.fields.labels}) self.logger.info("Ticket {} updated successfully".format(ticketid)) self.add_label(ticketid, 'updated') - except: - self.logger.error("Error while trying up update ticket {}".format(ticketid)) + except Exception as e: + self.logger.error("Error while trying up update ticket {ticketid}.\nReason: {e}".format(ticketid = ticketid, e=e)) return 0 def add_label(self, ticketid, label): From f170dcb05f626cee74dbc7d65b0154f75392a1df Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 25 Feb 2019 12:27:30 +0100 Subject: [PATCH 10/96] reorg resources files --- .gitignore | 1 + docker-compose.v6.yml | 6 +- elk6/vulnwhisperer.ini | 109 ------------------ .../docker-compose_ELK5_unsupported.yml | 0 .../logstash-vulnwhisperer-template.json | 0 .../filebeat}/filebeat.yml | 0 .../1000_vulnWhispererBaseVisuals.json | 0 ...hisperer_ReportingMitigationDashboard.json | 0 ...alysVisuals (required with Dashboard).json | 0 ...eportingMitigationDashboardQualysRisk.json | 0 .../9000_vulnWhisperer_SavedSearch.json | 0 .../logstash}/0001_input_beats.conf | 0 .../logstash}/1000_nessus_process_file.conf | 0 .../logstash}/2000_qualys_web_scans.conf | 0 .../logstash}/3000_openvas.conf | 0 .../logstash}/4000_jira.conf | 0 .../logstash}/9998_input_broker_rabbitmq.conf | 0 .../9998_output_broker_rabbitmq.conf | 0 {elk6 => resources/elk6}/filebeat.yml | 0 {elk6 => resources/elk6}/kibana.json | 0 {elk6 => resources/elk6}/logstash.yml | 0 .../pipeline/1000_nessus_process_file.conf | 0 .../elk6}/pipeline/2000_qualys_web_scans.conf | 0 .../elk6}/pipeline/3000_openvas.conf | 0 .../elk6}/pipeline/4000_jira.conf | 0 25 files changed, 4 insertions(+), 112 deletions(-) delete mode 100644 elk6/vulnwhisperer.ini rename docker-compose.yml => resources/elk5-old_compatibility/docker-compose_ELK5_unsupported.yml (100%) rename {elasticsearch => resources/elk5-old_compatibility/elasticsearch}/logstash-vulnwhisperer-template.json (100%) rename {filebeat => resources/elk5-old_compatibility/filebeat}/filebeat.yml (100%) rename {kibana => resources/elk5-old_compatibility/kibana}/vuln_whisp_kibana/1000_vulnWhispererBaseVisuals.json (100%) rename {kibana => resources/elk5-old_compatibility/kibana}/vuln_whisp_kibana/1001_vulnWhisperer_ReportingMitigationDashboard.json (100%) rename {kibana => resources/elk5-old_compatibility/kibana}/vuln_whisp_kibana/2000_vulnWhisperer_QualysVisuals (required with Dashboard).json (100%) rename {kibana => resources/elk5-old_compatibility/kibana}/vuln_whisp_kibana/2001_vulnWhisperer_ReportingMitigationDashboardQualysRisk.json (100%) rename {kibana => resources/elk5-old_compatibility/kibana}/vuln_whisp_kibana/9000_vulnWhisperer_SavedSearch.json (100%) rename {logstash => resources/elk5-old_compatibility/logstash}/0001_input_beats.conf (100%) rename {logstash => resources/elk5-old_compatibility/logstash}/1000_nessus_process_file.conf (100%) rename {logstash => resources/elk5-old_compatibility/logstash}/2000_qualys_web_scans.conf (100%) rename {logstash => resources/elk5-old_compatibility/logstash}/3000_openvas.conf (100%) rename {logstash => resources/elk5-old_compatibility/logstash}/4000_jira.conf (100%) rename {logstash => resources/elk5-old_compatibility/logstash}/9998_input_broker_rabbitmq.conf (100%) rename {logstash => resources/elk5-old_compatibility/logstash}/9998_output_broker_rabbitmq.conf (100%) rename {elk6 => resources/elk6}/filebeat.yml (100%) rename {elk6 => resources/elk6}/kibana.json (100%) rename {elk6 => resources/elk6}/logstash.yml (100%) rename {elk6 => resources/elk6}/pipeline/1000_nessus_process_file.conf (100%) rename {elk6 => resources/elk6}/pipeline/2000_qualys_web_scans.conf (100%) rename {elk6 => resources/elk6}/pipeline/3000_openvas.conf (100%) rename {elk6 => resources/elk6}/pipeline/4000_jira.conf (100%) diff --git a/.gitignore b/.gitignore index ea26da2..b4a878c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ data/ logs/ elk6/vulnwhisperer.ini +resources/elk6/vulnwhisperer.ini configs/frameworks_example.ini # Byte-compiled / optimized / DLL files diff --git a/docker-compose.v6.yml b/docker-compose.v6.yml index 58daac9..e61e1e9 100644 --- a/docker-compose.v6.yml +++ b/docker-compose.v6.yml @@ -44,8 +44,8 @@ services: image: docker.elastic.co/logstash/logstash:6.6.0 container_name: logstash volumes: - - ./elk6/pipeline/:/usr/share/logstash/pipeline - #- ./elk6/logstash.yml:/usr/share/logstash/config/logstash.yml + - ./resources/elk6/pipeline/:/usr/share/logstash/pipeline + #- ./resources/elk6/logstash.yml:/usr/share/logstash/config/logstash.yml - ./data/:/opt/vulnwhisperer/data environment: - xpack.monitoring.enabled=false @@ -65,7 +65,7 @@ services: ] volumes: - ./data/:/opt/vulnwhisperer/data - - ./elk6/vulnwhisperer.ini:/opt/vulnwhisperer/vulnwhisperer.ini + - ./resources/elk6/vulnwhisperer.ini:/opt/vulnwhisperer/vulnwhisperer.ini network_mode: host volumes: esdata1: diff --git a/elk6/vulnwhisperer.ini b/elk6/vulnwhisperer.ini deleted file mode 100644 index 2b92761..0000000 --- a/elk6/vulnwhisperer.ini +++ /dev/null @@ -1,109 +0,0 @@ -[nessus] -enabled=true -hostname=localhost -port=8834 -username=nessus_username -password=nessus_password -write_path=/opt/vulnwhisperer/data/nessus/ -db_path=/opt/vulnwhisperer/database -trash=false -verbose=true - -[tenable] -enabled=true -hostname=cloud.tenable.com -port=443 -username=tenable.io_username -password=tenable.io_password -write_path=/opt/vulnwhisperer/data/tenable/ -db_path=/opt/VulnWhisperer/data/database -trash=false -verbose=true - -[qualys_web] -#Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API -enabled = true -hostname = qualysapi.qg2.apps.qualys.com -username = exampleuser -password = examplepass -write_path=/opt/vulnwhisperer/data/qualys/ -db_path=/opt/vulnwhisperer/data/database -verbose=true - -# Set the maximum number of retries each connection should attempt. -#Note, this applies only to failed connections and timeouts, never to requests where the server returns a response. -max_retries = 10 -# Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. -template_id = 126024 - -[qualys_vuln] -#Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API -enabled = true -hostname = qualysapi.qg2.apps.qualys.com -username = exampleuser -password = examplepass -write_path=/opt/vulnwhisperer/data/qualys/ -db_path=/opt/vulnwhisperer/data/database -verbose=true - -# Set the maximum number of retries each connection should attempt. -#Note, this applies only to failed connections and timeouts, never to requests where the server returns a response. -max_retries = 10 -# Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. -template_id = 126024 - -[detectify] -#Reference https://developer.detectify.com/ -enabled = false -hostname = api.detectify.com -#username variable used as apiKey -username = exampleuser -#password variable used as secretKey -password = examplepass -write_path =/opt/vulnwhisperer/data/detectify/ -db_path = /opt/vulnwhisperer/data/database -verbose = true - -[openvas] -enabled = false -hostname = localhost -port = 4000 -username = exampleuser -password = examplepass -write_path=/opt/vulnwhisperer/data/openvas/ -db_path=/opt/vulnwhisperer/data/database -verbose=true - -#[proxy] -; This section is optional. Leave it out if you're not using a proxy. -; You can use environmental variables as well: http://www.python-requests.org/en/latest/user/advanced/#proxies - -; proxy_protocol set to https, if not specified. -#proxy_url = proxy.mycorp.com - -; proxy_port will override any port specified in proxy_url -#proxy_port = 8080 - -; proxy authentication -#proxy_username = proxyuser -#proxy_password = proxypass - -[jira] -hostname = jira-host -username = username -password = password -write_path = /opt/vulnwhisperer/data/jira/ -db_path = /opt/vulnwhisperer/data/database -verbose = true -dns_resolv = False - -#Sample jira report scan, will automatically be created for existent scans -#[jira.qualys_vuln.test_scan] -#source = qualys_vuln -#scan_name = Test Scan -#jira_project = PROJECT -; if multiple components, separate by "," = None -#components = -; minimum criticality to report (low, medium, high or critical) = None -#min_critical_to_report = high - diff --git a/docker-compose.yml b/resources/elk5-old_compatibility/docker-compose_ELK5_unsupported.yml similarity index 100% rename from docker-compose.yml rename to resources/elk5-old_compatibility/docker-compose_ELK5_unsupported.yml diff --git a/elasticsearch/logstash-vulnwhisperer-template.json b/resources/elk5-old_compatibility/elasticsearch/logstash-vulnwhisperer-template.json similarity index 100% rename from elasticsearch/logstash-vulnwhisperer-template.json rename to resources/elk5-old_compatibility/elasticsearch/logstash-vulnwhisperer-template.json diff --git a/filebeat/filebeat.yml b/resources/elk5-old_compatibility/filebeat/filebeat.yml similarity index 100% rename from filebeat/filebeat.yml rename to resources/elk5-old_compatibility/filebeat/filebeat.yml diff --git a/kibana/vuln_whisp_kibana/1000_vulnWhispererBaseVisuals.json b/resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/1000_vulnWhispererBaseVisuals.json similarity index 100% rename from kibana/vuln_whisp_kibana/1000_vulnWhispererBaseVisuals.json rename to resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/1000_vulnWhispererBaseVisuals.json diff --git a/kibana/vuln_whisp_kibana/1001_vulnWhisperer_ReportingMitigationDashboard.json b/resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/1001_vulnWhisperer_ReportingMitigationDashboard.json similarity index 100% rename from kibana/vuln_whisp_kibana/1001_vulnWhisperer_ReportingMitigationDashboard.json rename to resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/1001_vulnWhisperer_ReportingMitigationDashboard.json diff --git a/kibana/vuln_whisp_kibana/2000_vulnWhisperer_QualysVisuals (required with Dashboard).json b/resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/2000_vulnWhisperer_QualysVisuals (required with Dashboard).json similarity index 100% rename from kibana/vuln_whisp_kibana/2000_vulnWhisperer_QualysVisuals (required with Dashboard).json rename to resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/2000_vulnWhisperer_QualysVisuals (required with Dashboard).json diff --git a/kibana/vuln_whisp_kibana/2001_vulnWhisperer_ReportingMitigationDashboardQualysRisk.json b/resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/2001_vulnWhisperer_ReportingMitigationDashboardQualysRisk.json similarity index 100% rename from kibana/vuln_whisp_kibana/2001_vulnWhisperer_ReportingMitigationDashboardQualysRisk.json rename to resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/2001_vulnWhisperer_ReportingMitigationDashboardQualysRisk.json diff --git a/kibana/vuln_whisp_kibana/9000_vulnWhisperer_SavedSearch.json b/resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/9000_vulnWhisperer_SavedSearch.json similarity index 100% rename from kibana/vuln_whisp_kibana/9000_vulnWhisperer_SavedSearch.json rename to resources/elk5-old_compatibility/kibana/vuln_whisp_kibana/9000_vulnWhisperer_SavedSearch.json diff --git a/logstash/0001_input_beats.conf b/resources/elk5-old_compatibility/logstash/0001_input_beats.conf similarity index 100% rename from logstash/0001_input_beats.conf rename to resources/elk5-old_compatibility/logstash/0001_input_beats.conf diff --git a/logstash/1000_nessus_process_file.conf b/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf similarity index 100% rename from logstash/1000_nessus_process_file.conf rename to resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf diff --git a/logstash/2000_qualys_web_scans.conf b/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf similarity index 100% rename from logstash/2000_qualys_web_scans.conf rename to resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf diff --git a/logstash/3000_openvas.conf b/resources/elk5-old_compatibility/logstash/3000_openvas.conf similarity index 100% rename from logstash/3000_openvas.conf rename to resources/elk5-old_compatibility/logstash/3000_openvas.conf diff --git a/logstash/4000_jira.conf b/resources/elk5-old_compatibility/logstash/4000_jira.conf similarity index 100% rename from logstash/4000_jira.conf rename to resources/elk5-old_compatibility/logstash/4000_jira.conf diff --git a/logstash/9998_input_broker_rabbitmq.conf b/resources/elk5-old_compatibility/logstash/9998_input_broker_rabbitmq.conf similarity index 100% rename from logstash/9998_input_broker_rabbitmq.conf rename to resources/elk5-old_compatibility/logstash/9998_input_broker_rabbitmq.conf diff --git a/logstash/9998_output_broker_rabbitmq.conf b/resources/elk5-old_compatibility/logstash/9998_output_broker_rabbitmq.conf similarity index 100% rename from logstash/9998_output_broker_rabbitmq.conf rename to resources/elk5-old_compatibility/logstash/9998_output_broker_rabbitmq.conf diff --git a/elk6/filebeat.yml b/resources/elk6/filebeat.yml similarity index 100% rename from elk6/filebeat.yml rename to resources/elk6/filebeat.yml diff --git a/elk6/kibana.json b/resources/elk6/kibana.json similarity index 100% rename from elk6/kibana.json rename to resources/elk6/kibana.json diff --git a/elk6/logstash.yml b/resources/elk6/logstash.yml similarity index 100% rename from elk6/logstash.yml rename to resources/elk6/logstash.yml diff --git a/elk6/pipeline/1000_nessus_process_file.conf b/resources/elk6/pipeline/1000_nessus_process_file.conf similarity index 100% rename from elk6/pipeline/1000_nessus_process_file.conf rename to resources/elk6/pipeline/1000_nessus_process_file.conf diff --git a/elk6/pipeline/2000_qualys_web_scans.conf b/resources/elk6/pipeline/2000_qualys_web_scans.conf similarity index 100% rename from elk6/pipeline/2000_qualys_web_scans.conf rename to resources/elk6/pipeline/2000_qualys_web_scans.conf diff --git a/elk6/pipeline/3000_openvas.conf b/resources/elk6/pipeline/3000_openvas.conf similarity index 100% rename from elk6/pipeline/3000_openvas.conf rename to resources/elk6/pipeline/3000_openvas.conf diff --git a/elk6/pipeline/4000_jira.conf b/resources/elk6/pipeline/4000_jira.conf similarity index 100% rename from elk6/pipeline/4000_jira.conf rename to resources/elk6/pipeline/4000_jira.conf From bdbe31d425e8aa82ddc47f3d67b9540a59cd25b0 Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 25 Feb 2019 12:29:00 +0100 Subject: [PATCH 11/96] resources reorg 2 --- .../elk5-old_compatibility/docker}/1000_nessus_process_file.conf | 0 .../elk5-old_compatibility/docker}/2000_qualys_web_scans.conf | 0 .../elk5-old_compatibility/docker}/3000_openvas.conf | 0 .../elk5-old_compatibility/docker}/4000_jira.conf | 0 {docker => resources/elk5-old_compatibility/docker}/logstash.yml | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {docker => resources/elk5-old_compatibility/docker}/1000_nessus_process_file.conf (100%) rename {docker => resources/elk5-old_compatibility/docker}/2000_qualys_web_scans.conf (100%) rename {docker => resources/elk5-old_compatibility/docker}/3000_openvas.conf (100%) rename {docker => resources/elk5-old_compatibility/docker}/4000_jira.conf (100%) rename {docker => resources/elk5-old_compatibility/docker}/logstash.yml (100%) diff --git a/docker/1000_nessus_process_file.conf b/resources/elk5-old_compatibility/docker/1000_nessus_process_file.conf similarity index 100% rename from docker/1000_nessus_process_file.conf rename to resources/elk5-old_compatibility/docker/1000_nessus_process_file.conf diff --git a/docker/2000_qualys_web_scans.conf b/resources/elk5-old_compatibility/docker/2000_qualys_web_scans.conf similarity index 100% rename from docker/2000_qualys_web_scans.conf rename to resources/elk5-old_compatibility/docker/2000_qualys_web_scans.conf diff --git a/docker/3000_openvas.conf b/resources/elk5-old_compatibility/docker/3000_openvas.conf similarity index 100% rename from docker/3000_openvas.conf rename to resources/elk5-old_compatibility/docker/3000_openvas.conf diff --git a/docker/4000_jira.conf b/resources/elk5-old_compatibility/docker/4000_jira.conf similarity index 100% rename from docker/4000_jira.conf rename to resources/elk5-old_compatibility/docker/4000_jira.conf diff --git a/docker/logstash.yml b/resources/elk5-old_compatibility/docker/logstash.yml similarity index 100% rename from docker/logstash.yml rename to resources/elk5-old_compatibility/docker/logstash.yml From 05420ddfd0621d8b07c9b7d8bae0742bf0ebcfe8 Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 25 Feb 2019 12:32:32 +0100 Subject: [PATCH 12/96] readding docker-compose credentials template --- resources/elk6/vulnwhisperer.ini | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 resources/elk6/vulnwhisperer.ini diff --git a/resources/elk6/vulnwhisperer.ini b/resources/elk6/vulnwhisperer.ini new file mode 100644 index 0000000..2b92761 --- /dev/null +++ b/resources/elk6/vulnwhisperer.ini @@ -0,0 +1,109 @@ +[nessus] +enabled=true +hostname=localhost +port=8834 +username=nessus_username +password=nessus_password +write_path=/opt/vulnwhisperer/data/nessus/ +db_path=/opt/vulnwhisperer/database +trash=false +verbose=true + +[tenable] +enabled=true +hostname=cloud.tenable.com +port=443 +username=tenable.io_username +password=tenable.io_password +write_path=/opt/vulnwhisperer/data/tenable/ +db_path=/opt/VulnWhisperer/data/database +trash=false +verbose=true + +[qualys_web] +#Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API +enabled = true +hostname = qualysapi.qg2.apps.qualys.com +username = exampleuser +password = examplepass +write_path=/opt/vulnwhisperer/data/qualys/ +db_path=/opt/vulnwhisperer/data/database +verbose=true + +# Set the maximum number of retries each connection should attempt. +#Note, this applies only to failed connections and timeouts, never to requests where the server returns a response. +max_retries = 10 +# Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. +template_id = 126024 + +[qualys_vuln] +#Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API +enabled = true +hostname = qualysapi.qg2.apps.qualys.com +username = exampleuser +password = examplepass +write_path=/opt/vulnwhisperer/data/qualys/ +db_path=/opt/vulnwhisperer/data/database +verbose=true + +# Set the maximum number of retries each connection should attempt. +#Note, this applies only to failed connections and timeouts, never to requests where the server returns a response. +max_retries = 10 +# Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. +template_id = 126024 + +[detectify] +#Reference https://developer.detectify.com/ +enabled = false +hostname = api.detectify.com +#username variable used as apiKey +username = exampleuser +#password variable used as secretKey +password = examplepass +write_path =/opt/vulnwhisperer/data/detectify/ +db_path = /opt/vulnwhisperer/data/database +verbose = true + +[openvas] +enabled = false +hostname = localhost +port = 4000 +username = exampleuser +password = examplepass +write_path=/opt/vulnwhisperer/data/openvas/ +db_path=/opt/vulnwhisperer/data/database +verbose=true + +#[proxy] +; This section is optional. Leave it out if you're not using a proxy. +; You can use environmental variables as well: http://www.python-requests.org/en/latest/user/advanced/#proxies + +; proxy_protocol set to https, if not specified. +#proxy_url = proxy.mycorp.com + +; proxy_port will override any port specified in proxy_url +#proxy_port = 8080 + +; proxy authentication +#proxy_username = proxyuser +#proxy_password = proxypass + +[jira] +hostname = jira-host +username = username +password = password +write_path = /opt/vulnwhisperer/data/jira/ +db_path = /opt/vulnwhisperer/data/database +verbose = true +dns_resolv = False + +#Sample jira report scan, will automatically be created for existent scans +#[jira.qualys_vuln.test_scan] +#source = qualys_vuln +#scan_name = Test Scan +#jira_project = PROJECT +; if multiple components, separate by "," = None +#components = +; minimum criticality to report (low, medium, high or critical) = None +#min_critical_to_report = high + From b36e31566ee196e8fd03da94ebbf1484453f6dc6 Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 25 Feb 2019 22:02:20 +0100 Subject: [PATCH 13/96] fix #142 --- vulnwhisp/vulnwhisp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index 287e919..ba30aeb 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -679,6 +679,8 @@ def __init__( if debug: self.logger.setLevel(logging.DEBUG) + + self.directory_check() self.port = int(self.config.get(self.CONFIG_SECTION, 'port')) self.develop = True self.purge = purge From 46ddee391b141b11c30e1354ea7f52affdc47ada Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 25 Feb 2019 22:09:29 +0100 Subject: [PATCH 14/96] confirm openvas 9 works --- configs/frameworks_example.ini | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/configs/frameworks_example.ini b/configs/frameworks_example.ini index 77d283c..cdd215c 100755 --- a/configs/frameworks_example.ini +++ b/configs/frameworks_example.ini @@ -1,5 +1,5 @@ [nessus] -enabled=true +enabled=false hostname=localhost port=8834 username=nessus_username @@ -10,7 +10,7 @@ trash=false verbose=true [tenable] -enabled=true +enabled=false hostname=cloud.tenable.com port=443 username=tenable.io_username @@ -22,7 +22,7 @@ verbose=true [qualys_web] #Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API -enabled = true +enabled = false hostname = qualysapi.qg2.apps.qualys.com username = exampleuser password = examplepass @@ -38,7 +38,7 @@ template_id = 126024 [qualys_vuln] #Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API -enabled = true +enabled = false hostname = qualysapi.qg2.apps.qualys.com username = exampleuser password = examplepass @@ -65,13 +65,14 @@ db_path = /opt/VulnWhisperer/data/database verbose = true [openvas] -enabled = false -hostname = localhost -port = 4000 -username = exampleuser -password = examplepass -write_path=/opt/VulnWhisperer/data/openvas/ -db_path=/opt/VulnWhisperer/data/database +enabled = true +hostname = 192.168.1.49 +port = 443 +#4000 +username = admin +password = admin +write_path=./data/openvas/ +db_path=./data/database verbose=true #[proxy] From a3da41e4870c3c9dd6e66782364a4645e940e7b1 Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 26 Feb 2019 09:59:50 +0100 Subject: [PATCH 15/96] added to readme openvas supported versions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9ffe86..0315a55 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Currently Supports - [X] [Nessus (**v6**/**v7**/**v8**)](https://www.tenable.com/products/nessus/nessus-professional) - [X] [Qualys Web Applications](https://www.qualys.com/apps/web-app-scanning/) - [X] [Qualys Vulnerability Management](https://www.qualys.com/apps/vulnerability-management/) -- [X] [OpenVAS](http://www.openvas.org/) +- [X] [OpenVAS (**v7**/**v8**/**v9**)](http://www.openvas.org/) - [X] [Tenable.io](https://www.tenable.com/products/tenable-io) - [ ] [Detectify](https://detectify.com/) - [ ] [Nexpose](https://www.rapid7.com/products/nexpose/) From 4e94bef2450867d3b8a6bca4b5cf4f5fac5aad64 Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 26 Feb 2019 15:26:14 +0100 Subject: [PATCH 16/96] fix bug not detecting existent label due to string format --- vulnwhisp/reporting/jira_api.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index 04afe36..1bf1720 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -317,14 +317,15 @@ def ticket_update_assets(self, vuln, ticketid, ticket_assets): def add_label(self, ticketid, label): ticket_obj = self.jira.issue(ticketid) - if label not in ticket_obj.fields.labels: - ticket_obj.fields.labels.append(label) + if label not in [x.encode('utf8') for x in ticket_obj.fields.labels]: + ticket_obj.fields.labels.append(label) + + try: + ticket_obj.update(fields={"labels":ticket_obj.fields.labels}) + self.logger.info("Added label {label} to ticket {ticket}".format(label=label, ticket=ticketid)) + except: + self.logger.error("Error while trying to add label {label} to ticket {ticket}".format(label=label, ticket=ticketid)) - try: - ticket_obj.update(fields={"labels":ticket_obj.fields.labels}) - self.logger.info("Added label {label} to ticket {ticket}".format(label=label, ticket=ticketid)) - except: - self.logger.error("Error while trying to add label {label} to ticket {ticket}".format(label=label, ticket=ticketid)) return 0 def close_fixed_tickets(self, vulnerabilities): From 623c881928325b5217bc1c0cfbbe045df907fb6c Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 27 Feb 2019 11:27:44 +0100 Subject: [PATCH 17/96] fix jira issue index when comparing created tickets --- vulnwhisp/reporting/jira_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index 1bf1720..4ed8e3b 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -151,9 +151,9 @@ def check_vuln_already_exists(self, vuln): #WARNING: function IGNORES DUPLICATES, after finding a "duplicate" will just return it exists #it wont iterate over the rest of tickets looking for other possible duplicates/similar issues self.logger.info("Comparing Vulnerabilities to created tickets") - for index in range(len(self.all_tickets)-1): + for index in range(len(self.all_tickets)): checking_ticketid, checking_title, checking_assets = self.ticket_get_unique_fields(self.all_tickets[index]) - if title == checking_title: + if title.encode('ascii') == checking_title.encode('ascii'): difference = list(set(assets).symmetric_difference(checking_assets)) #to check intersection - set(assets) & set(checking_assets) if difference: From a288f416f7f26b7b4eedb143595bdfabb7aedca8 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 27 Feb 2019 18:06:16 +0100 Subject: [PATCH 18/96] added label *false positive* for reporting on jira --- vulnwhisp/reporting/jira_api.py | 9 +++++++-- vulnwhisp/reporting/resources/ticket.tpl | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index 4ed8e3b..a9dee8f 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -385,6 +385,9 @@ def is_risk_accepted(self, ticket_obj): elif "server_decommission" in labels: self.logger.warn("Ticket {} server decommissioned, will be ignored".format(ticket_obj)) return True + elif "false_positive" in labels: + self.logger.warn("Ticket {} flagged false positive, will be ignored".format(ticket_obj)) + return True self.logger.info("Ticket {} risk has not been accepted".format(ticket_obj)) return False @@ -398,8 +401,10 @@ def reopen_ticket(self, ticketid): try: if self.is_ticket_reopenable(ticket_obj): comment = '''This ticket has been reopened due to the vulnerability not having been fixed (if multiple assets are affected, all need to be fixed; if the server is down, lastest known vulnerability might be the one reported). - In the case of the team accepting the risk and wanting to close the ticket, please add the label "*risk_accepted*" to the ticket before closing it. - If server has been decommissioned, please add the label "*server_decommission*" to the ticket before closing it. + - In the case of the team accepting the risk and wanting to close the ticket, please add the label "*risk_accepted*" to the ticket before closing it. + - If server has been decommissioned, please add the label "*server_decommission*" to the ticket before closing it. + - If when checking the vulnerability it looks like a false positive, _+please elaborate in a comment+_ and add the label "*false_positive*" before closing it; we will review it and report it to the vendor. + If you have further doubts, please contact the Security Team.''' error = self.jira.transition_issue(issue=ticketid, transition=self.JIRA_REOPEN_ISSUE, comment = comment) self.logger.info("Ticket {} reopened successfully".format(ticketid)) diff --git a/vulnwhisp/reporting/resources/ticket.tpl b/vulnwhisp/reporting/resources/ticket.tpl index 675a560..dc03b38 100644 --- a/vulnwhisp/reporting/resources/ticket.tpl +++ b/vulnwhisp/reporting/resources/ticket.tpl @@ -30,3 +30,5 @@ Please do not delete or modify the ticket assigned tags or title, as they are us In the case of the team accepting the risk and wanting to close the ticket, please add the label "*risk_accepted*" to the ticket before closing it. If server has been decommissioned, please add the label "*server_decommission*" to the ticket before closing it. + +If when checking the vulnerability it looks like a false positive, _+please elaborate in a comment+_ and add the label "*false_positive*" before closing it; we will review it and report it to the vendor. From 86e792f5aafa3b03b547feba0cb3817da9541787 Mon Sep 17 00:00:00 2001 From: Quim Date: Fri, 1 Mar 2019 15:18:49 +0100 Subject: [PATCH 19/96] workaround regarding ignoring ticket updates after risk accepted --- vulnwhisp/reporting/jira_api.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index a9dee8f..1039b91 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -262,11 +262,19 @@ def ticket_update_assets(self, vuln, ticketid, ticket_assets): # correct description will always be in the vulnerability to report, only needed to update description to new one self.logger.info("Ticket {} exists, UPDATE requested".format(ticketid)) - if self.is_ticket_resolved(self.jira.issue(ticketid)): + #for now, if a vulnerability has been accepted ('accepted_risk'), ticket is completely ignored and not updated (no new assets) + + #TODO when vulnerability accepted, create a new ticket with only the non-accepted vulnerable assets + #this would require go through the downloaded tickets, check duplicates/accepted ones, and if so, + #check on their assets to exclude them from the new ticket + risk_accepted = False + ticket_obj = self.jira.issue(ticketid) + if self.is_ticket_resolved(ticket_obj): + if self.is_risk_accepted(ticket_obj): + return 0 self.reopen_ticket(ticketid) #First will do the comparison of assets - ticket_obj = self.jira.issue(ticketid) ticket_obj.update() assets = list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", ",".join(vuln['ips'])))) difference = list(set(assets).symmetric_difference(ticket_assets)) From 401dfec2c8ba578962ae7faa431d9e049c0351cf Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 4 Mar 2019 15:10:51 +0100 Subject: [PATCH 20/96] fix #143, added a temporary container to upload through kibana API --- docker-compose.v6.yml | 14 +- resources/elk6/init_kibana.sh | 33 +++ resources/elk6/kibana_APIonly.json | 428 +++++++++++++++++++++++++++++ 3 files changed, 474 insertions(+), 1 deletion(-) create mode 100755 resources/elk6/init_kibana.sh create mode 100755 resources/elk6/kibana_APIonly.json diff --git a/docker-compose.v6.yml b/docker-compose.v6.yml index e61e1e9..b5a833e 100644 --- a/docker-compose.v6.yml +++ b/docker-compose.v6.yml @@ -40,13 +40,24 @@ services: esnet: aliases: - kibana.local + kibana-config: + image: alpine + container_name: kibana-config + volumes: + - ./resources/elk6/init_kibana.sh:/opt/init_kibana.sh + - ./resources/elk6/kibana_APIonly.json:/opt/kibana_APIonly.json + command: sh -c "apk add --no-cache curl bash && chmod +x /opt/init_kibana.sh && chmod +r /opt/kibana_APIonly.json && cd /opt/ && /bin/bash /opt/init_kibana.sh" # /opt/kibana_APIonly.json" + networks: + esnet: + aliases: + - kibana-config.local logstash: image: docker.elastic.co/logstash/logstash:6.6.0 container_name: logstash volumes: - ./resources/elk6/pipeline/:/usr/share/logstash/pipeline - #- ./resources/elk6/logstash.yml:/usr/share/logstash/config/logstash.yml - ./data/:/opt/vulnwhisperer/data + #- ./resources/elk6/logstash.yml:/usr/share/logstash/config/logstash.yml environment: - xpack.monitoring.enabled=false depends_on: @@ -64,6 +75,7 @@ services: "/opt/vulnwhisperer/vulnwhisperer.ini" ] volumes: + - /opt/vulnwhisperer/data/:/opt/vulnwhisperer/data - ./data/:/opt/vulnwhisperer/data - ./resources/elk6/vulnwhisperer.ini:/opt/vulnwhisperer/vulnwhisperer.ini network_mode: host diff --git a/resources/elk6/init_kibana.sh b/resources/elk6/init_kibana.sh new file mode 100755 index 0000000..e9c6075 --- /dev/null +++ b/resources/elk6/init_kibana.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +#kibana_url="localhost:5601" +kibana_url="kibana.local:5601" +add_saved_objects="curl -u elastic:changeme -k -XPOST 'http://"$kibana_url"/api/saved_objects/_bulk_create' -H 'Content-Type: application/json' -H \"kbn-xsrf: true\" -d @" + +#Create all saved objects - including index pattern +saved_objects_file="kibana_APIonly.json" + +#if [ `curl -I localhost:5601/status | head -n1 |cut -d$' ' -f2` -eq '200' ]; then echo "Loading VulnWhisperer Saved Objects"; eval $(echo $add_saved_objects$saved_objects_file); else echo "waiting for kibana"; fi + +until [ "`curl -I "$kibana_url"/status | head -n1 |cut -d$' ' -f2`" == "200" ]; do + curl -I "$kibana_url"/status + echo "Waiting for Kibana" + sleep 5 +done + +echo "Loading VulnWhisperer Saved Objects" +echo $add_saved_objects$saved_objects_file +eval $(echo $add_saved_objects$saved_objects_file) + +#set "*" as default index +#id_default_index="87f3bcc0-8b37-11e8-83be-afaed4786d8c" +#os.system("curl -X POST -H \"Content-Type: application/json\" -H \"kbn-xsrf: true\" -d '{\"value\":\""+id_default_index+"\"}' http://elastic:changeme@"+kibana_url+"kibana/settings/defaultIndex") + +#Create vulnwhisperer index pattern +#index_name = "logstash-vulnwhisperer-*" +#os.system(add_index+index_name+"' '-d{\"attributes\":{\"title\":\""+index_name+"\",\"timeFieldName\":\"@timestamp\"}}'") + +#Create jira index pattern, separated for not fill of crap variables the Discover tab by default +#index_name = "logstash-jira-*" +#os.system(add_index+index_name+"' '-d{\"attributes\":{\"title\":\""+index_name+"\",\"timeFieldName\":\"@timestamp\"}}'") + diff --git a/resources/elk6/kibana_APIonly.json b/resources/elk6/kibana_APIonly.json new file mode 100755 index 0000000..7a76a29 --- /dev/null +++ b/resources/elk6/kibana_APIonly.json @@ -0,0 +1,428 @@ +[ + { + "id": "AWCUqesWib22Ai8JwW3u", + "type": "dashboard", + "attributes": { + "title": "VulnWhisperer - Risk Mitigation", + "hits": 0, + "description": "", + "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"20\",\"w\":8,\"x\":40,\"y\":15},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"21\",\"w\":12,\"x\":0,\"y\":35},\"id\":\"852816e0-3eb1-11e7-90cb-918f9cb01e3d\",\"panelIndex\":\"21\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"27\",\"w\":12,\"x\":12,\"y\":35},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"27\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"28\",\"w\":8,\"x\":32,\"y\":15},\"id\":\"35b6d320-3f7f-11e7-bd24-6903e3283192\",\"panelIndex\":\"28\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"30\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"31\",\"w\":8,\"x\":24,\"y\":35},\"id\":\"de1a5f40-3f85-11e7-97f9-3777d794626d\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"37\",\"w\":16,\"x\":16,\"y\":25},\"id\":\"5093c620-44e9-11e7-8014-ede06a7e69f8\",\"panelIndex\":\"37\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"columns\":[\"host\",\"risk\",\"risk_score\",\"cve\",\"plugin_name\",\"solution\",\"plugin_output\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":30,\"i\":\"38\",\"w\":48,\"x\":0,\"y\":65},\"id\":\"54648700-3f74-11e7-852e-69207a3d0726\",\"panelIndex\":\"38\",\"type\":\"search\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"39\",\"w\":16,\"x\":16,\"y\":15},\"id\":\"fb6eb020-49ab-11e7-8f8c-57ad64ec48a6\",\"panelIndex\":\"39\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"legendOpen\":true}},\"gridData\":{\"h\":20,\"i\":\"46\",\"w\":16,\"x\":0,\"y\":15},\"id\":\"56f0f5f0-3ebe-11e7-a192-93f36fbd9d05\",\"panelIndex\":\"46\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"47\",\"w\":9,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"47\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"48\",\"w\":10,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"48\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#E5AC0E\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"50\",\"w\":10,\"x\":20,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"50\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"51\",\"w\":10,\"x\":10,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"51\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", + "version": 1, + "timeRestore": false, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" + } + } + }, + { + "id": "72051530-448e-11e7-a818-f5f80dfc3590", + "type": "dashboard", + "attributes": { + "title": "VulnWhisperer - Reporting", + "hits": 0, + "description": "", + "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"legendOpen\":false}},\"gridData\":{\"h\":20,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":56},\"id\":\"2f979030-44b9-11e7-a818-f5f80dfc3590\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"12\",\"w\":24,\"x\":0,\"y\":36},\"id\":\"8d9592d0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"12\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":16},\"id\":\"67d432e0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"14\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"15\",\"w\":12,\"x\":36,\"y\":36},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"15\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"20\",\"w\":12,\"x\":24,\"y\":36},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"22\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"22\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"29\",\"w\":24,\"x\":0,\"y\":16},\"id\":\"479deab0-8a39-11e7-a58a-9bfcb3761a3d\",\"panelIndex\":\"29\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"30\",\"w\":10,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#EAB839\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"31\",\"w\":9,\"x\":21,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"10 - 20\":\"#890F02\"},\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"32\",\"w\":11,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"32\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"33\",\"w\":10,\"x\":11,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"33\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", + "version": 1, + "timeRestore": true, + "timeTo": "now", + "timeFrom": "now-7d", + "refreshInterval": { + "pause": true, + "value": 0 + }, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" + } + } + }, + { + "id": "4a6d9090-f66e-11e8-8f42-af2e41422cf8", + "type": "index-pattern", + "attributes": { + "title": "logstash-vulnwhisperer-*", + "timeFieldName": "@timestamp", + "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"IpPort\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"asset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"asset.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"assign_ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cve\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"history_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"history_id.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"last_updated\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"message_error\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"opcode\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"path.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"plugin_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_output\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"plugin_output.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"port\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"protocol.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"record_number\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_number\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"risk_number.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"risk_score_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"scan_id.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"scan_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"see_also\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"solution\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"synopsis\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_fingerprint\",\"type\":\"string\",\"count\":1,\"scripted\":true,\"script\":\"doc['asset.keyword']+'_'+doc['plugin_id']\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", + "fieldFormatMap": "{\"plugin_id\":{\"id\":\"number\",\"params\":{\"pattern\":\"00.[000]\"}}}" + } + }, + { + "id": "159d2500-f773-11e8-8f42-af2e41422cf8", + "type": "search", + "attributes": { + "title": "VulnWhisperer - High Risk", + "description": "", + "hits": 0, + "columns": [ + "host", + "risk", + "risk_score", + "cve", + "plugin_name", + "solution", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}}},\"filter\":[{\"meta\":{\"negate\":false,\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"type\":\"phrase\",\"key\":\"risk\",\"value\":\"High\",\"params\":{\"query\":\"High\",\"type\":\"phrase\"},\"disabled\":false,\"alias\":null},\"query\":{\"match\":{\"risk\":{\"query\":\"High\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647},\"highlightAll\":true,\"version\":true}" + } + } + }, + { + "id": "54648700-3f74-11e7-852e-69207a3d0726", + "type": "search", + "attributes": { + "title": "VulnWhisperer - Saved Search", + "description": "", + "hits": 0, + "columns": [ + "host", + "risk", + "risk_score", + "cve", + "plugin_name", + "solution", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647}}" + } + } + }, + { + "id": "41a7e430-fdb5-11e8-8f42-af2e41422cf8", + "type": "search", + "attributes": { + "title": "VulnWhisperer - Compliance", + "description": "", + "hits": 0, + "columns": [ + "plugin_id", + "cve", + "cvss", + "risk", + "asset", + "protocol", + "port", + "plugin_name", + "synopsis", + "description", + "solution", + "see_also", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" + } + } + }, + { + "id": "465c5820-8977-11e7-857e-e1d56b17746d", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Critical Assets", + "visState": "{\"title\":\"VulnWhisperer - Critical Assets\",\"type\":\"heatmap\",\"params\":{\"addTooltip\":true,\"addLegend\":true,\"enableHover\":true,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Green to Red\",\"setColorRange\":true,\"colorsRange\":[{\"from\":0,\"to\":3},{\"from\":3,\"to\":7},{\"from\":7,\"to\":9},{\"from\":9,\"to\":11}],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"color\":\"white\"}}],\"type\":\"heatmap\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"risk_score\",\"customLabel\":\"Residual Risk Score\"}},{\"id\":\"2\",\"enabled\":false,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"risk_score\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"Date\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"asset.keyword\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Critical Asset\"}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 3\":\"rgb(0,104,55)\",\"3 - 7\":\"rgb(135,203,103)\",\"7 - 9\":\"rgb(255,255,190)\",\"9 - 11\":\"rgb(249,142,82)\"},\"colors\":{\"8 - 10\":\"#BF1B00\",\"9 - 11\":\"#BF1B00\",\"7 - 9\":\"#EF843C\",\"3 - 7\":\"#EAB839\",\"0 - 3\":\"#7EB26D\"},\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"index\":\"logstash-vulnwhisperer-*\",\"negate\":false,\"disabled\":false,\"alias\":\"Critical Asset\",\"type\":\"phrase\",\"key\":\"tags\",\"value\":\"critical_asset\"},\"query\":{\"match\":{\"tags\":{\"query\":\"critical_asset\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" + } + } + }, + { + "id": "56f0f5f0-3ebe-11e7-a192-93f36fbd9d05", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-RiskOverTime", + "visState": "{\"title\":\"VulnWhisperer-RiskOverTime\",\"type\":\"line\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per 12 hours\"},\"type\":\"category\"}],\"defaultYExtents\":false,\"drawLinesBetweenPoints\":true,\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"},\"valueAxis\":\"ValueAxis-1\"},\"interpolate\":\"linear\",\"legendPosition\":\"right\",\"orderBucketsBySum\":false,\"radiusRatio\":9,\"scale\":\"linear\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"interpolate\":\"linear\",\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"setYExtents\":false,\"showCircles\":true,\"times\":[],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"type\":\"line\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:info\"}}},\"label\":\"Info\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:low\"}}},\"label\":\"Low\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:medium\"}}},\"label\":\"Medium\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:high\"}}},\"label\":\"High\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:critical\"}}},\"label\":\"Critical\"}]}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"colors\":{\"Critical\":\"#962D82\",\"High\":\"#BF1B00\",\"Low\":\"#629E51\",\"Medium\":\"#EAB839\",\"Info\":\"#65C5DB\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "5093c620-44e9-11e7-8014-ede06a7e69f8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Mitigation Readme", + "visState": "{\"title\":\"VulnWhisperer - Mitigation Readme\",\"type\":\"markdown\",\"params\":{\"markdown\":\"** Legend **\\n\\n* [Common Vulnerability Scoring System (CVSS)](https://nvd.nist.gov/vuln-metrics/cvss) is the NIST vulnerability scoring system\\n* Risk Number is residual risk score calculated from CVSS, which is adjusted to be specific to Heartland which accounts for services not in use such as Java and Flash\\n* Vulnerabilities by Tag are systems tagged with HIPAA and PCI identification.\\n\\n\\n** Workflow **\\n* Select 10.0 under Risk Number to identify Critical Vulnerabilities. \\n* For more information about a CVE, scroll down and click the CVE link.\\n* To filter by tags, use one of the following filters:\\n** tags:has_hipaa_data, tags:pci_asset, tags:hipaa_asset, tags:critical_asset**\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "471a3580-3f6b-11e7-88e7-df1abe6547fb", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Vulnerabilities by Tag", + "visState": "{\"title\":\"VulnWhisperer - Vulnerabilities by Tag\",\"type\":\"table\",\"params\":{\"perPage\":3,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"bucket\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:has_hipaa_data\",\"analyze_wildcard\":true}}},\"label\":\"Systems with HIPAA data\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:pci_asset\",\"analyze_wildcard\":true}}},\"label\":\"PCI Systems\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:hipaa_asset\",\"analyze_wildcard\":true}}},\"label\":\"HIPAA Systems\"}]}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "1de9e550-3df1-11e7-a44e-c79ca8efb780", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-Description", + "visState": "{\"title\":\"VulnWhisperer-Description\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"description.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Description\"}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "fb6eb020-49ab-11e7-8f8c-57ad64ec48a6", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Critical Risk Score for Tagged Assets", + "visState": "{\"title\":\"VulnWhisperer - Critical Risk Score for Tagged Assets\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:hipaa_asset').label(\\\"HIPAA Assets\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:pci_asset').label(\\\"PCI Systems\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:has_hipaa_data').label(\\\"Has HIPAA Data\\\")\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "479deab0-8a39-11e7-a58a-9bfcb3761a3d", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - TL - TaggedAssetsPluginNames", + "visState": "{\"title\":\"VulnWhisperer - TL - TaggedAssetsPluginNames\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*', q='tags:critical_asset OR tags:hipaa_asset OR tags:pci_asset', split=\\\"plugin_name.keyword:10\\\").bars(width=4).label(regex=\\\".*:(.+)>.*\\\",label=\\\"$1\\\")\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "13c7d4e0-3df3-11e7-a44e-c79ca8efb780", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-Solution", + "visState": "{\n \"title\": \"VulnWhisperer-Solution\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showMeticsAtAllLevels\": false,\n \"showPartialRows\": false,\n \"showTotal\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"totalFunc\": \"sum\"\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"count\",\n \"schema\": \"metric\",\n \"params\": {}\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"solution\",\n \"size\": 50,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"customLabel\": \"Solution\"\n }\n }\n ],\n \"listeners\": {}\n}", + "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"analyze_wildcard\": true,\n \"query\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" + } + } + }, + { + "id": "e6b5b920-f77a-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Risk: Low", + "visState": "{\"title\":\"VulnWhisperer - Risk: Low\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Greens\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":100}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Low\"},\"label\":\"Low Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "61b43c00-f77b-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Risk: Medium", + "visState": "{\"title\":\"VulnWhisperer - Risk: Medium\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Blues\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10}],\"invertColors\":true,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Medium\"},\"label\":\"Medium Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "f9b68640-fda5-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - AggTest", + "visState": "{\"title\":\"VulnWhisperer - AggTest\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"top_hits\",\"schema\":\"metric\",\"params\":{\"field\":\"@timestamp\",\"aggregate\":\"concat\",\"size\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":1000,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"plugin_id\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "2f979030-44b9-11e7-a818-f5f80dfc3590", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - ScanBarChart", + "visState": "{\"title\":\"VulnWhisperer - ScanBarChart\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[],\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Unique count of scan_fingerprint\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Unique count of scan_fingerprint\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"plugin_name.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Scan Name\"}}]}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "8c9c9430-f77b-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Risk: Critical", + "visState": "{\"title\":\"VulnWhisperer - Risk: Critical\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10},{\"from\":10,\"to\":20}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Critical\"},\"label\":\"Critical Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" + } + } + }, + { + "id": "c533c120-fe8c-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Risk: High", + "visState": "{\"title\":\"VulnWhisperer - Risk: High\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":1,\"to\":5},{\"from\":5,\"to\":19999}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Risk: High\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:High\"},\"label\":\"risk: High\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"1 - 5\":\"rgb(255,245,240)\",\"5 - 19999\":\"rgb(103,0,13)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "852816e0-3eb1-11e7-90cb-918f9cb01e3d", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-CVSS", + "visState": "{\"title\":\"VulnWhisperer-CVSS\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Unique Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"cvss\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"CVSS Score\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"asset.keyword\",\"customLabel\":\"# of Assets\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "35b6d320-3f7f-11e7-bd24-6903e3283192", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Residual Risk", + "visState": "{\"title\":\"VulnWhisperer - Residual Risk\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Count\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"risk_score\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Risk Number\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "de1a5f40-3f85-11e7-97f9-3777d794626d", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - ScanName", + "visState": "{\"title\":\"VulnWhisperer - ScanName\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"scan_name.keyword\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Scan Name\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "995e2280-3df3-11e7-a44e-c79ca8efb780", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-Asset", + "visState": "{\"title\":\"VulnWhisperer-Asset\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Asset\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "297df800-3f7e-11e7-bd24-6903e3283192", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Plugin Name", + "visState": "{\"title\":\"VulnWhisperer - Plugin Name\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Count\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"plugin_name.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Plugin Name\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "67d432e0-44ec-11e7-a05f-d9719b331a27", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - TL-Critical Risk", + "visState": "{\"title\":\"VulnWhisperer - TL-Critical Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + }, + { + "id": "8d9592d0-44ec-11e7-a05f-d9719b331a27", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - TL-High Risk", + "visState": "{\"title\":\"VulnWhisperer - TL-High Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" + } + } + } + ] From e7bd4d2a55c3b300be7ee0ad04544319c1515e0b Mon Sep 17 00:00:00 2001 From: Quim Date: Fri, 15 Mar 2019 12:03:02 +0100 Subject: [PATCH 21/96] deleting dependency and pulling qualysapi official library, vulnwhisperer compatible --- .gitmodules | 3 --- deps/qualysapi | 1 - requirements.txt | 1 + 3 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 deps/qualysapi diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e6bb589..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "qualysapi"] - path = deps/qualysapi - url = https://github.com/austin-taylor/qualysapi.git diff --git a/deps/qualysapi b/deps/qualysapi deleted file mode 160000 index 42c3b43..0000000 --- a/deps/qualysapi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 42c3b43ac1e6657be32f7a11d211e3157af4143b diff --git a/requirements.txt b/requirements.txt index 9ecc043..a49e39d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ bs4 jira bottle coloredlogs +qualysapi>=5.1.0 From 936c4a3e1bc510827adad43d3e46c7bed58de3db Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 19 Mar 2019 12:58:38 +0100 Subject: [PATCH 22/96] added automatic jira server_decommission label removal after x time --- vulnwhisp/reporting/jira_api.py | 94 ++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index 1039b91..8a083d7 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -9,7 +9,7 @@ import re class JiraAPI(object): - def __init__(self, hostname=None, username=None, password=None, path="", debug=False, clean_obsolete=True, max_time_window=12): + def __init__(self, hostname=None, username=None, password=None, path="", debug=False, clean_obsolete=True, max_time_window=12, decommission_time_window=3): self.logger = logging.getLogger('JiraAPI') if debug: self.logger.setLevel(logging.DEBUG) @@ -23,11 +23,8 @@ def __init__(self, hostname=None, username=None, password=None, path="", debug=F self.all_tickets = [] self.JIRA_REOPEN_ISSUE = "Reopen Issue" self.JIRA_CLOSE_ISSUE = "Close Issue" - self.max_time_tracking = max_time_window #in months - # self.JIRA_RESOLUTION_OBSOLETE = "Obsolete" self.JIRA_RESOLUTION_FIXED = "Fixed" - self.clean_obsolete = clean_obsolete self.template_path = 'vulnwhisp/reporting/resources/ticket.tpl' self.max_ips_ticket = 30 self.attachment_filename = "vulnerable_assets.txt" @@ -35,6 +32,20 @@ def __init__(self, hostname=None, username=None, password=None, path="", debug=F self.download_tickets(path) else: self.logger.warn("No local path specified, skipping Jira ticket download.") + self.max_time_tracking = max_time_window #in months + self.max_decommission_time = decommission_time_window #in months + # [HIGIENE] close tickets older than 12 months as obsolete (max_time_window defined) + if clean_obsolete: + self.close_obsolete_tickets() + # deletes the tag "server_decommission" from those tickets closed <=3 months ago + self.decommission_cleanup() + + self.jira_still_vulnerable_comment = '''This ticket has been reopened due to the vulnerability not having been fixed (if multiple assets are affected, all need to be fixed; if the server is down, lastest known vulnerability might be the one reported). + - In the case of the team accepting the risk and wanting to close the ticket, please add the label "*risk_accepted*" to the ticket before closing it. + - If server has been decommissioned, please add the label "*server_decommission*" to the ticket before closing it. + - If when checking the vulnerability it looks like a false positive, _+please elaborate in a comment+_ and add the label "*false_positive*" before closing it; we will review it and report it to the vendor. + + If you have further doubts, please contact the Security Team.''' def create_ticket(self, title, desc, project="IS", components=[], tags=[], attachment_contents = []): labels = ['vulnerability_management'] @@ -88,11 +99,6 @@ def sync(self, vulnerabilities, project, components=[]): #JIRA structure of each vulnerability: [source, scan_name, title, diagnosis, consequence, solution, ips, risk, references] self.logger.info("JIRA Sync started") - # [HIGIENE] close tickets older than 12 months as obsolete - # Higiene clean up affects to all tickets created by the module, filters by label 'vulnerability_management' - if self.clean_obsolete: - self.close_obsolete_tickets() - for vuln in vulnerabilities: # JIRA doesn't allow labels with spaces, so making sure that the scan_name doesn't have spaces # if it has, they will be replaced by "_" @@ -107,7 +113,7 @@ def sync(self, vulnerabilities, project, components=[]): if exists: # If ticket "resolved" -> reopen, as vulnerability is still existent - self.reopen_ticket(ticketid) + self.reopen_ticket(ticketid=ticketid, comment=self.jira_still_vulnerable_comment) self.add_label(ticketid, vuln['risk']) continue elif to_update: @@ -251,7 +257,6 @@ def get_resolution_time(self, ticket): start = datetime(created[0],created[1],created[2],created[3],created[4],created[5]) end = datetime(resolved[0],resolved[1],resolved[2],resolved[3],resolved[4],resolved[5]) - return (end-start).days else: self.logger.error("Ticket {ticket} is not resolved, can't calculate resolution time".format(ticket=ticket)) @@ -272,7 +277,7 @@ def ticket_update_assets(self, vuln, ticketid, ticket_assets): if self.is_ticket_resolved(ticket_obj): if self.is_risk_accepted(ticket_obj): return 0 - self.reopen_ticket(ticketid) + self.reopen_ticket(ticketid=ticketid, comment=self.jira_still_vulnerable_comment) #First will do the comparison of assets ticket_obj.update() @@ -336,8 +341,27 @@ def add_label(self, ticketid, label): return 0 + def remove_label(self, ticketid, label): + ticket_obj = self.jira.issue(ticketid) + + if label in [x.encode('utf8') for x in ticket_obj.fields.labels]: + ticket_obj.fields.labels.remove(label) + + try: + ticket_obj.update(fields={"labels":ticket_obj.fields.labels}) + self.logger.info("Removed label {label} from ticket {ticket}".format(label=label, ticket=ticketid)) + except: + self.logger.error("Error while trying to remove label {label} to ticket {ticket}".format(label=label, ticket=ticketid)) + else: + self.logger.error("Error: label {label} not in ticket {ticket}".format(label=label, ticket=ticketid)) + + return 0 + def close_fixed_tickets(self, vulnerabilities): - # close tickets which vulnerabilities have been resolved and are still open + ''' + Close tickets which vulnerabilities have been resolved and are still open. + Higiene clean up affects to all tickets created by the module, filters by label 'vulnerability_management' + ''' found_vulns = [] for vuln in vulnerabilities: found_vulns.append(vuln['title']) @@ -399,24 +423,19 @@ def is_risk_accepted(self, ticket_obj): self.logger.info("Ticket {} risk has not been accepted".format(ticket_obj)) return False - def reopen_ticket(self, ticketid): + def reopen_ticket(self, ticketid, ignore_labels=False, comment=""): self.logger.debug("Ticket {} exists, REOPEN requested".format(ticketid)) # this will reopen a ticket by ticketid ticket_obj = self.jira.issue(ticketid) if self.is_ticket_resolved(ticket_obj): - if not self.is_risk_accepted(ticket_obj): + if (not self.is_risk_accepted(ticket_obj) or ignore_labels): try: if self.is_ticket_reopenable(ticket_obj): - comment = '''This ticket has been reopened due to the vulnerability not having been fixed (if multiple assets are affected, all need to be fixed; if the server is down, lastest known vulnerability might be the one reported). - - In the case of the team accepting the risk and wanting to close the ticket, please add the label "*risk_accepted*" to the ticket before closing it. - - If server has been decommissioned, please add the label "*server_decommission*" to the ticket before closing it. - - If when checking the vulnerability it looks like a false positive, _+please elaborate in a comment+_ and add the label "*false_positive*" before closing it; we will review it and report it to the vendor. - - If you have further doubts, please contact the Security Team.''' error = self.jira.transition_issue(issue=ticketid, transition=self.JIRA_REOPEN_ISSUE, comment = comment) self.logger.info("Ticket {} reopened successfully".format(ticketid)) - self.add_label(ticketid, 'reopened') + if not ignore_labels: + self.add_label(ticketid, 'reopened') return 1 except Exception as e: # continue with ticket data so that a new ticket is created in place of the "lost" one @@ -449,8 +468,8 @@ def close_obsolete_tickets(self): jql = "labels=vulnerability_management AND created Date: Tue, 19 Mar 2019 15:19:27 +0100 Subject: [PATCH 23/96] fix bug --- vulnwhisp/reporting/jira_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index 8a083d7..a209290 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -28,11 +28,11 @@ def __init__(self, hostname=None, username=None, password=None, path="", debug=F self.template_path = 'vulnwhisp/reporting/resources/ticket.tpl' self.max_ips_ticket = 30 self.attachment_filename = "vulnerable_assets.txt" + self.max_time_tracking = max_time_window #in months if path: self.download_tickets(path) else: self.logger.warn("No local path specified, skipping Jira ticket download.") - self.max_time_tracking = max_time_window #in months self.max_decommission_time = decommission_time_window #in months # [HIGIENE] close tickets older than 12 months as obsolete (max_time_window defined) if clean_obsolete: From 70e1d7703f65ef95a9e4bc43d8148a5b51420032 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 20 Mar 2019 08:33:52 +0100 Subject: [PATCH 24/96] fix missing section specification on qualys was connector #156 --- vulnwhisp/frameworks/qualys_web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulnwhisp/frameworks/qualys_web.py b/vulnwhisp/frameworks/qualys_web.py index 74b5122..7212e75 100644 --- a/vulnwhisp/frameworks/qualys_web.py +++ b/vulnwhisp/frameworks/qualys_web.py @@ -43,7 +43,7 @@ def __init__(self, config=None): self.logger.error('Could not connect to Qualys: {}'.format(str(e))) self.headers = { "content-type": "text/xml"} - self.config_parse = qcconf.QualysConnectConfig(config) + self.config_parse = qcconf.QualysConnectConfig(config, 'qualys_web') try: self.template_id = self.config_parse.get_template_id() except: From 9d52596be94ce47d99f50544fa1b13b3ca7e5de4 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 20 Mar 2019 08:49:36 +0100 Subject: [PATCH 25/96] fix xml encoding issue #156 --- vulnwhisp/frameworks/qualys_web.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/vulnwhisp/frameworks/qualys_web.py b/vulnwhisp/frameworks/qualys_web.py index 7212e75..1e7fc1c 100644 --- a/vulnwhisp/frameworks/qualys_web.py +++ b/vulnwhisp/frameworks/qualys_web.py @@ -74,7 +74,7 @@ def get_was_scan_count(self, status): E.filters( E.Criteria({'field': 'status', 'operator': 'EQUALS'}, status)))) xml_output = self.qgc.request(self.COUNT_WASSCAN, parameters) - root = objectify.fromstring(xml_output) + root = objectify.fromstring(xml_output.encode('utf-8')) return root.count.text def get_reports(self): @@ -127,17 +127,21 @@ def get_all_scans(self, limit=1000, offset=1, status='FINISHED'): qualys_api_limit = limit dataframes = [] _records = [] - total = int(self.get_was_scan_count(status=status)) - self.logger.info('Retrieving information for {} scans'.format(total)) - for i in range(0, total): - if i % limit == 0: - if (total - i) < limit: - qualys_api_limit = total - i - self.logger.info('Making a request with a limit of {} at offset {}'.format((str(qualys_api_limit), str(i + 1)))) - scan_info = self.get_scan_info(limit=qualys_api_limit, offset=i + 1, status=status) - _records.append(scan_info) - self.logger.debug('Converting XML to DataFrame') - dataframes = [self.xml_parser(xml) for xml in _records] + try: + total = int(self.get_was_scan_count(status=status)) + self.logger.error('Already have WAS scan count') + self.logger.info('Retrieving information for {} scans'.format(total)) + for i in range(0, total): + if i % limit == 0: + if (total - i) < limit: + qualys_api_limit = total - i + self.logger.info('Making a request with a limit of {} at offset {}'.format((str(qualys_api_limit), str(i + 1)))) + scan_info = self.get_scan_info(limit=qualys_api_limit, offset=i + 1, status=status) + _records.append(scan_info) + self.logger.debug('Converting XML to DataFrame') + dataframes = [self.xml_parser(xml) for xml in _records] + except Exception as e: + self.logger.error("Couldn't process all scans: {}".format(e)) return pd.concat(dataframes, axis=0).reset_index().drop('index', axis=1) From a4420b7df85c6e30ca86a3deb9303035ec155ea3 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 20 Mar 2019 09:11:18 +0100 Subject: [PATCH 26/96] reverse unintended change on frameworks_example.ini --- configs/frameworks_example.ini | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/configs/frameworks_example.ini b/configs/frameworks_example.ini index cdd215c..77d283c 100755 --- a/configs/frameworks_example.ini +++ b/configs/frameworks_example.ini @@ -1,5 +1,5 @@ [nessus] -enabled=false +enabled=true hostname=localhost port=8834 username=nessus_username @@ -10,7 +10,7 @@ trash=false verbose=true [tenable] -enabled=false +enabled=true hostname=cloud.tenable.com port=443 username=tenable.io_username @@ -22,7 +22,7 @@ verbose=true [qualys_web] #Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API -enabled = false +enabled = true hostname = qualysapi.qg2.apps.qualys.com username = exampleuser password = examplepass @@ -38,7 +38,7 @@ template_id = 126024 [qualys_vuln] #Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API -enabled = false +enabled = true hostname = qualysapi.qg2.apps.qualys.com username = exampleuser password = examplepass @@ -65,14 +65,13 @@ db_path = /opt/VulnWhisperer/data/database verbose = true [openvas] -enabled = true -hostname = 192.168.1.49 -port = 443 -#4000 -username = admin -password = admin -write_path=./data/openvas/ -db_path=./data/database +enabled = false +hostname = localhost +port = 4000 +username = exampleuser +password = examplepass +write_path=/opt/VulnWhisperer/data/openvas/ +db_path=/opt/VulnWhisperer/data/database verbose=true #[proxy] From 47df1ee538152d079ec9588591e2f38d6928949a Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 20 Mar 2019 10:55:54 +0100 Subject: [PATCH 27/96] typo --- vulnwhisp/reporting/jira_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index a209290..a13bf9e 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -523,7 +523,7 @@ def decommission_cleanup(self): jql = "labels=vulnerability_management AND labels=server_decommission and resolutiondate <=startOfMonth(-{})".format(self.max_decommission_time) decommissioned_tickets = self.jira.search_issues(jql, maxResults=0) - comment = '''This ticket is having deleted the *server_decommission* tag deleted, as it is more than {} months old and is expected to already have been decommissioned. + comment = '''This ticket is having deleted the *server_decommission* tag, as it is more than {} months old and is expected to already have been decommissioned. If that is not the case and the vulnerability still exists, the vulnerability will be opened again.'''.format(self.max_decommission_time) for ticket in decommissioned_tickets: From 843aac6a83fbd74c0520da1473aef76dddeb1171 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 20 Mar 2019 16:37:50 +0100 Subject: [PATCH 28/96] fixing issue with new vulns of already risk accepted issues not being reported anymore; now, new ticket is raised, excluding all the assets that have been previously considered risk accepted in another ticket --- vulnwhisp/reporting/jira_api.py | 125 +++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 36 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index a13bf9e..e58a5e4 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -21,6 +21,7 @@ def __init__(self, hostname=None, username=None, password=None, path="", debug=F self.jira = JIRA(options={'server': hostname}, basic_auth=(self.username, self.password)) self.logger.info("Created vjira service for {}".format(hostname)) self.all_tickets = [] + self.excluded_tickets = [] self.JIRA_REOPEN_ISSUE = "Reopen Issue" self.JIRA_CLOSE_ISSUE = "Close Issue" self.JIRA_RESOLUTION_OBSOLETE = "Obsolete" @@ -52,7 +53,7 @@ def create_ticket(self, title, desc, project="IS", components=[], tags=[], attac for tag in tags: labels.append(str(tag)) - self.logger.info("creating ticket for project {} title: {}".format(project, title[:20])) + self.logger.info("Creating ticket for project {} title: {}".format(project, title[:20])) self.logger.debug("project {} has a component requirement: {}".format(project, components)) project_obj = self.jira.project(project) components_ticket = [] @@ -105,40 +106,87 @@ def sync(self, vulnerabilities, project, components=[]): if " " in vuln['scan_name']: vuln['scan_name'] = "_".join(vuln['scan_name'].split(" ")) - exists = False - to_update = False - ticketid = "" - ticket_assets = [] - exists, to_update, ticketid, ticket_assets = self.check_vuln_already_exists(vuln) - - if exists: - # If ticket "resolved" -> reopen, as vulnerability is still existent - self.reopen_ticket(ticketid=ticketid, comment=self.jira_still_vulnerable_comment) - self.add_label(ticketid, vuln['risk']) - continue - elif to_update: - self.ticket_update_assets(vuln, ticketid, ticket_assets) - self.add_label(ticketid, vuln['risk']) - continue - attachment_contents = [] - # if assets >30, add as attachment - # create local text file with assets, attach it to ticket - if len(vuln['ips']) > self.max_ips_ticket: - attachment_contents = vuln['ips'] - vuln['ips'] = ["Affected hosts ({assets}) exceed Jira's allowed character limit, added as an attachment.".format(assets = len(attachment_contents))] - try: - tpl = template(self.template_path, vuln) - except Exception as e: - self.logger.error('Exception templating: {}'.format(str(e))) - return 0 - self.create_ticket(title=vuln['title'], desc=tpl, project=project, components=components, tags=[vuln['source'], vuln['scan_name'], 'vulnerability', vuln['risk']], attachment_contents = attachment_contents) + # we exclude from the vulnerabilities to report those assets that already exist with *risk_accepted*/*server_decommission* + vuln = self.exclude_accepted_assets(vuln) + + # make sure after exclusion of risk_accepted assets there are still assets + if vuln['ips']: + exists = False + to_update = False + ticketid = "" + ticket_assets = [] + exists, to_update, ticketid, ticket_assets = self.check_vuln_already_exists(vuln) + + if exists: + # If ticket "resolved" -> reopen, as vulnerability is still existent + self.reopen_ticket(ticketid=ticketid, comment=self.jira_still_vulnerable_comment) + self.add_label(ticketid, vuln['risk']) + continue + elif to_update: + self.ticket_update_assets(vuln, ticketid, ticket_assets) + self.add_label(ticketid, vuln['risk']) + continue + attachment_contents = [] + # if assets >30, add as attachment + # create local text file with assets, attach it to ticket + if len(vuln['ips']) > self.max_ips_ticket: + attachment_contents = vuln['ips'] + vuln['ips'] = ["Affected hosts ({assets}) exceed Jira's allowed character limit, added as an attachment.".format(assets = len(attachment_contents))] + try: + tpl = template(self.template_path, vuln) + except Exception as e: + self.logger.error('Exception templating: {}'.format(str(e))) + return 0 + self.create_ticket(title=vuln['title'], desc=tpl, project=project, components=components, tags=[vuln['source'], vuln['scan_name'], 'vulnerability', vuln['risk']], attachment_contents = attachment_contents) + else: + self.logger.info("Ignoring vulnerability as all assets are already reported in a risk_accepted ticket") self.close_fixed_tickets(vulnerabilities) # we reinitialize so the next sync redoes the query with their specific variables self.all_tickets = [] + self.excluded_tickets = [] return True + + def exclude_accepted_assets(self, vuln): + # we want to check JIRA tickets with risk_accepted/server_decommission or false_positive labels sharing the same source + # will exclude tickets older than 12 months, old tickets will get closed for higiene and recreated if still vulnerable + labels = [vuln['source'], vuln['scan_name'], 'vulnerability_management', 'vulnerability'] + + if not self.excluded_tickets: + jql = "{} AND labels in (risk_accepted,server_decommission, false_positive) AND NOT labels=advisory AND created >=startOfMonth(-{})".format(" AND ".join(["labels={}".format(label) for label in labels]), self.max_time_tracking) + self.excluded_tickets = self.jira.search_issues(jql, maxResults=0) + + title = vuln['title'] + #WARNING: function IGNORES DUPLICATES, after finding a "duplicate" will just return it exists + #it wont iterate over the rest of tickets looking for other possible duplicates/similar issues + self.logger.info("Comparing vulnerability to risk_accepted tickets") + assets_to_exclude = [] + tickets_excluded_assets = [] + for index in range(len(self.excluded_tickets)): + checking_ticketid, checking_title, checking_assets = self.ticket_get_unique_fields(self.excluded_tickets[index]) + if title.encode('ascii') == checking_title.encode('ascii'): + if checking_assets: + #checking_assets is a list, we add to our full list for later delete all assets + assets_to_exclude+=checking_assets + tickets_excluded_assets.append(checking_ticketid) + + if assets_to_exclude: + self.logger.warn("Vulnerable Assets seen on an already existing risk_accepted Jira ticket: {}".format(', '.join(tickets_excluded_assets))) + #assets in vulnerability have the structure "ip - hostname - port", so we need to match by partial + for exclusion in assets_to_exclude: + for asset in vuln['ips']: + if exclusion in asset: + #self.logger.error("Assets before deleting risk_accepted assets: {}".format(vuln['ips'])) + self.logger.debug("Deleting asset {} from vulnerability {}, seen in risk_accepted.".format(asset,title)) + vuln['ips'].remove(asset) + + return vuln def check_vuln_already_exists(self, vuln): + ''' + This function compares a vulnerability with a collection of tickets. + Returns [exists (bool), is equal (bool), ticketid (str), assets (array)] + ''' # we need to return if the vulnerability has already been reported and the ID of the ticket for further processing #function returns array [duplicated(bool), update(bool), ticketid, ticket_assets] title = vuln['title'] @@ -159,7 +207,8 @@ def check_vuln_already_exists(self, vuln): self.logger.info("Comparing Vulnerabilities to created tickets") for index in range(len(self.all_tickets)): checking_ticketid, checking_title, checking_assets = self.ticket_get_unique_fields(self.all_tickets[index]) - if title.encode('ascii') == checking_title.encode('ascii'): + # added "not risk_accepted", as if it is risk_accepted, we will create a new ticket excluding the accepted assets + if title.encode('ascii') == checking_title.encode('ascii') and not self.is_risk_accepted(self.jira.issue(checking_ticketid)): difference = list(set(assets).symmetric_difference(checking_assets)) #to check intersection - set(assets) & set(checking_assets) if difference: @@ -173,27 +222,31 @@ 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 regex failed. Ticket ID: {}. Reason: {}".format(ticketid, e)) - assets = [] - + self.logger.error("Ticket IPs Attachment regex failed. Ticket ID: {}. Reason: {}".format(ticketid, e)) + return ticketid, title, assets def check_ips_attachment(self, ticket): affected_assets_section = [] try: - fields = self.jira.issue(ticket.key).raw.get('fields') - attachments = fields.get('attachment') + fields = self.jira.issue(ticket.key).raw.get('fields', {}) + attachments = fields.get('attachment', {}) affected_assets_section = "" #we will make sure we get the latest version of the file latest = '' From a4b1b9cdd423a79e71b671d8f6dafd1b27b4aab2 Mon Sep 17 00:00:00 2001 From: Quim Date: Thu, 21 Mar 2019 15:52:18 +0100 Subject: [PATCH 29/96] fixed issue where, asset after a removed one, was ignored due to python listing --- vulnwhisp/reporting/jira_api.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/vulnwhisp/reporting/jira_api.py b/vulnwhisp/reporting/jira_api.py index e58a5e4..12b3360 100644 --- a/vulnwhisp/reporting/jira_api.py +++ b/vulnwhisp/reporting/jira_api.py @@ -171,14 +171,18 @@ def exclude_accepted_assets(self, vuln): tickets_excluded_assets.append(checking_ticketid) if assets_to_exclude: + assets_to_remove = [] self.logger.warn("Vulnerable Assets seen on an already existing risk_accepted Jira ticket: {}".format(', '.join(tickets_excluded_assets))) + self.logger.debug("Original assets: {}".format(vuln['ips'])) #assets in vulnerability have the structure "ip - hostname - port", so we need to match by partial for exclusion in assets_to_exclude: - for asset in vuln['ips']: - if exclusion in asset: - #self.logger.error("Assets before deleting risk_accepted assets: {}".format(vuln['ips'])) - self.logger.debug("Deleting asset {} from vulnerability {}, seen in risk_accepted.".format(asset,title)) - vuln['ips'].remove(asset) + # for efficiency, we walk the backwards the array of ips from the scanners, as we will be popping out the matches + # and we don't want it to affect the rest of the processing (otherwise, it would miss the asset right after the removed one) + for index in range(len(vuln['ips']))[::-1]: + if exclusion == vuln['ips'][index].split(" - ")[0]: + self.logger.debug("Deleting asset {} from vulnerability {}, seen in risk_accepted.".format(vuln['ips'][index], title)) + vuln['ips'].pop(index) + self.logger.debug("Modified assets: {}".format(vuln['ips'])) return vuln From 97e4f073bfc371a5b35bbeb6ced95f6cd5970cd6 Mon Sep 17 00:00:00 2001 From: Quim Date: Fri, 22 Mar 2019 10:38:55 +0100 Subject: [PATCH 30/96] added logging to file --- bin/vuln_whisperer | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/bin/vuln_whisperer b/bin/vuln_whisperer index d94b5ce..4621c23 100644 --- a/bin/vuln_whisperer +++ b/bin/vuln_whisperer @@ -41,7 +41,13 @@ def main(): stream=sys.stdout, level=logging.DEBUG if args.debug else logging.INFO ) - logger = logging.getLogger(name='main') + logger = logging.getLogger() + # we set up the logger to log as well to file + fh = logging.FileHandler('vulnwhisperer.log') + fh.setLevel(logging.DEBUG if args.debug else logging.INFO) + fh.setFormatter(logging.Formatter("%(asctime)s - %(name)s:%(levelname)s:%(message)s", "%Y-%m-%d %H:%M:%S")) + logger.addHandler(fh) + if args.fancy: import coloredlogs coloredlogs.install(level='DEBUG' if args.debug else 'INFO') @@ -68,6 +74,7 @@ def main(): vw.whisper_vulnerabilities() # TODO: fix this to NOT be exit 1 unless in error + close_logging_handlers() sys.exit(1) else: @@ -82,6 +89,7 @@ def main(): vw.whisper_vulnerabilities() # TODO: fix this to NOT be exit 1 unless in error + close_logging_handlers() sys.exit(1) except Exception as e: @@ -90,8 +98,15 @@ def main(): logger.error('{}'.format(str(e))) print('ERROR: {error}'.format(error=e)) # TODO: fix this to NOT be exit 2 unless in error + close_logging_handlers() sys.exit(2) + close_logging_handlers() + +def close_logging_handlers(): + for handler in logger.handlers: + handler.close() + logger.removeFilter(handler) if __name__ == '__main__': main() From 3601ace5e15a37cf823a7d2be19b520e9b5c1b12 Mon Sep 17 00:00:00 2001 From: Quim Date: Fri, 22 Mar 2019 10:42:30 +0100 Subject: [PATCH 31/96] improved file logging format --- bin/vuln_whisperer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/vuln_whisperer b/bin/vuln_whisperer index 4621c23..cf10616 100644 --- a/bin/vuln_whisperer +++ b/bin/vuln_whisperer @@ -45,7 +45,7 @@ def main(): # we set up the logger to log as well to file fh = logging.FileHandler('vulnwhisperer.log') fh.setLevel(logging.DEBUG if args.debug else logging.INFO) - fh.setFormatter(logging.Formatter("%(asctime)s - %(name)s:%(levelname)s:%(message)s", "%Y-%m-%d %H:%M:%S")) + fh.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(name)s - %(funcName)s:%(message)s", "%Y-%m-%d %H:%M:%S")) logger.addHandler(fh) if args.fancy: From 383e7f547835cc418f9cc1a4837b8de25681da43 Mon Sep 17 00:00:00 2001 From: pemontto <939704+pemontto@users.noreply.github.com> Date: Mon, 1 Apr 2019 18:07:29 +1100 Subject: [PATCH 32/96] Fix closing logging handlers (#159) --- bin/vuln_whisperer | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/vuln_whisperer b/bin/vuln_whisperer index cf10616..f5273ef 100644 --- a/bin/vuln_whisperer +++ b/bin/vuln_whisperer @@ -74,7 +74,7 @@ def main(): vw.whisper_vulnerabilities() # TODO: fix this to NOT be exit 1 unless in error - close_logging_handlers() + close_logging_handlers(logger) sys.exit(1) else: @@ -89,7 +89,7 @@ def main(): vw.whisper_vulnerabilities() # TODO: fix this to NOT be exit 1 unless in error - close_logging_handlers() + close_logging_handlers(logger) sys.exit(1) except Exception as e: @@ -98,12 +98,12 @@ def main(): logger.error('{}'.format(str(e))) print('ERROR: {error}'.format(error=e)) # TODO: fix this to NOT be exit 2 unless in error - close_logging_handlers() + close_logging_handlers(logger) sys.exit(2) - close_logging_handlers() + close_logging_handlers(logger) -def close_logging_handlers(): +def close_logging_handlers(logger): for handler in logger.handlers: handler.close() logger.removeFilter(handler) From 9619a47d7a909fd98b6d43b11af21117fc66c2c2 Mon Sep 17 00:00:00 2001 From: pemontto <939704+pemontto@users.noreply.github.com> Date: Mon, 1 Apr 2019 19:04:12 +1100 Subject: [PATCH 33/96] Fix Tenable and Nessus scan listing (#162) * Prevent multiple requests to nessus scans endpoint * Remove unnecessary call --- vulnwhisp/frameworks/nessus.py | 3 ++- vulnwhisp/vulnwhisp.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vulnwhisp/frameworks/nessus.py b/vulnwhisp/frameworks/nessus.py index 09ce24e..5423caf 100755 --- a/vulnwhisp/frameworks/nessus.py +++ b/vulnwhisp/frameworks/nessus.py @@ -53,6 +53,7 @@ def __init__(self, hostname=None, port=None, username=None, password=None, verbo } self.login() + self.scans = self.get_scans() self.scan_ids = self.get_scan_ids() def login(self): @@ -113,7 +114,7 @@ def get_scans(self): return scans def get_scan_ids(self): - scans = self.get_scans() + scans = self.scans scan_ids = [scan_id['id'] for scan_id in scans['scans']] if scans['scans'] else [] self.logger.debug('Found {} scan_ids'.format(len(scan_ids))) return scan_ids diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index ba30aeb..0e79437 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -312,7 +312,7 @@ def scan_count(self, scans, completed=False): def whisper_nessus(self): if self.nessus_connect: - scan_data = self.nessus.get_scans() + scan_data = self.nessus.scans folders = scan_data['folders'] scans = scan_data['scans'] if scan_data['scans'] else [] all_scans = self.scan_count(scans) @@ -368,7 +368,6 @@ def whisper_nessus(self): # TODO Create directory sync function which scans the directory for files that exist already and populates the database folder_id = s['folder_id'] - scan_history = self.nessus.get_scan_history(scan_id) if self.CONFIG_SECTION == 'tenable': folder_name = '' else: From e32c9bf55dbfac87038213e95faf99c3e0a024b3 Mon Sep 17 00:00:00 2001 From: pemontto <939704+pemontto@users.noreply.github.com> Date: Mon, 1 Apr 2019 19:06:16 +1100 Subject: [PATCH 34/96] Fix *some* unicode issues for nessus and qualys (#160) * Fix *some* unicode issues for nessus and qualys * More unicode fixes --- vulnwhisp/frameworks/qualys_vuln.py | 2 +- vulnwhisp/vulnwhisp.py | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/vulnwhisp/frameworks/qualys_vuln.py b/vulnwhisp/frameworks/qualys_vuln.py index 4a193c0..43495b1 100644 --- a/vulnwhisp/frameworks/qualys_vuln.py +++ b/vulnwhisp/frameworks/qualys_vuln.py @@ -30,7 +30,7 @@ def __init__(self, config=None): def scan_xml_parser(self, xml): all_records = [] - root = ET.XML(xml) + root = ET.XML(xml.encode("utf-8")) for child in root.find('.//SCAN_LIST'): all_records.append({ 'name': child.find('TITLE').text, diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index 0e79437..ad1e6dc 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -164,11 +164,11 @@ def directory_check(self): if not os.path.exists(self.write_path): os.makedirs(self.write_path) self.logger.info('Directory created at {scan} - Skipping creation'.format( - scan=self.write_path)) + scan=self.write_path.encode('utf8'))) else: os.path.exists(self.write_path) self.logger.info('Directory already exist for {scan} - Skipping creation'.format( - scan=self.write_path)) + scan=self.write_path.encode('utf8'))) def get_latest_results(self, source, scan_name): try: @@ -302,7 +302,6 @@ def scan_count(self, scans, completed=False): scan_records.append(record.copy()) except Exception as e: # Generates error each time nonetype is encountered. - pass if completed: @@ -338,8 +337,7 @@ def whisper_nessus(self): else: os.path.exists(self.path_check(f['name'])) self.logger.info('Directory already exist for {scan} - Skipping creation'.format( - scan=self.path_check(f['name' - ]))) + scan=self.path_check(f['name']).encode('utf8'))) # try download and save scans into each folder the belong to @@ -400,10 +398,9 @@ def whisper_nessus(self): self.nessus.download_scan(scan_id=scan_id, history=history_id, export_format='csv', profile=self.CONFIG_SECTION) clean_csv = \ - pd.read_csv(io.StringIO(file_req.decode('utf-8' - ))) + pd.read_csv(io.StringIO(file_req.decode('utf-8'))) if len(clean_csv) > 2: - self.logger.info('Processing {}/{} for scan: {}'.format(scan_count, len(scan_list), scan_name)) + self.logger.info('Processing {}/{} for scan: {}'.format(scan_count, len(scan_list), scan_name.encode('utf8'))) columns_to_cleanse = ['CVSS','CVE','Description','Synopsis','Solution','See Also','Plugin Output'] for col in columns_to_cleanse: @@ -423,7 +420,7 @@ def whisper_nessus(self): ) self.record_insert(record_meta) self.logger.info('{filename} records written to {path} '.format(filename=clean_csv.shape[0], - path=file_name)) + path=file_name.encode('utf8'))) else: record_meta = ( scan_name, From fa0b3c867be84d2a6f071fd88f94c8b855508c79 Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 1 Apr 2019 15:55:02 +0200 Subject: [PATCH 35/96] added tracking of scans processed by jira, will only process if new scans now (backwards compatibility --- vulnwhisp/vulnwhisp.py | 75 +++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index ad1e6dc..ea1e8ed 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -102,6 +102,7 @@ def __init__( 'source', 'uuid', 'processed', + 'reported', ] self.init() @@ -115,7 +116,7 @@ def create_table(self): 'CREATE TABLE IF NOT EXISTS scan_history (id INTEGER PRIMARY KEY,' ' scan_name TEXT, scan_id INTEGER, last_modified DATE, filename TEXT,' ' download_time DATE, record_count INTEGER, source TEXT,' - ' uuid TEXT, processed INTEGER)' + ' uuid TEXT, processed INTEGER, reported INTEGER)' ) self.conn.commit() @@ -142,10 +143,35 @@ def path_check(self, _data): return data def record_insert(self, record): - self.cur.execute('insert into scan_history({table_columns}) values (?,?,?,?,?,?,?,?,?)'.format( - table_columns=', '.join(self.table_columns)), - record) - self.conn.commit() + #for backwards compatibility with older versions without "reported" field + + try: + #-1 to get the latest column, 1 to get the column name (old version would be "processed", new "reported") + #TODO delete backward compatibility check after some versions + last_column_table = self.cur.execute('PRAGMA table_info(scan_history)').fetchall()[-1][1] + if last_column_table == self.table_columns[-1]: + self.cur.execute('insert into scan_history({table_columns}) values (?,?,?,?,?,?,?,?,?,?)'.format( + table_columns=', '.join(self.table_columns)), record) + + else: + self.cur.execute('insert into scan_history({table_columns}) values (?,?,?,?,?,?,?,?,?)'.format( + table_columns=', '.join(self.table_columns[:-1])), record[:-1]) + self.conn.commit() + except Exception as e: + self.logger.error("Failed to insert record in database. Error: {}".format(e)) + sys.exit(1) + + def set_latest_scan_processed(self, filename): + #the reason to use the filename instead of the source/scan_name is because the filename already belongs to + #that latest scan, and we maintain integrity making sure that it is the exact scan we checked + try: + self.cur.execute('UPDATE scan_history SET processed = 1 WHERE filename="{}";'.format(filename)) + self.logger.debug('Scan {} marked as successfully processed.'.format(filename)) + return True + except Exception as e: + self.logger.error('Failed while setting scan with file {} as processed'.format(filename)) + + return False def retrieve_uuids(self): """ @@ -171,17 +197,26 @@ def directory_check(self): scan=self.write_path.encode('utf8'))) def get_latest_results(self, source, scan_name): + processed = 0 + results = [] + try: self.conn.text_factory = str self.cur.execute('SELECT filename FROM scan_history WHERE source="{}" AND scan_name="{}" ORDER BY last_modified DESC LIMIT 1;'.format(source, scan_name)) #should always return just one filename results = [r[0] for r in self.cur.fetchall()][0] - except: - results = [] - return results - return True + #-1 to get the latest column, 1 to get the column name (old version would be "processed", new "reported") + #TODO delete backward compatibility check after some versions + last_column_table = self.cur.execute('PRAGMA table_info(scan_history)').fetchall()[-1][1] + if results and last_column_table == self.table_columns[-1]: + processed = self.cur.execute('SELECT processed FROM scan_history WHERE filename="{}"'.format(results)).fetchall()[0][0] + if processed: + self.logger.debug("Last downloaded scan from source {source} scan_name {scan_name} has already been processed".format(source=source, scan_name=scan_name)) + except Exception as e: + self.logger.error("Error when getting latest results from {}.{} : {}".format(source, scan_name, e)) + return results, processed def get_scan_profiles(self): # Returns a list of source.scan_name elements from the database @@ -390,6 +425,7 @@ def whisper_nessus(self): self.CONFIG_SECTION, uuid, 1, + 0, ) self.record_insert(record_meta) self.logger.info('File {filename} already exist! Updating database'.format(filename=relative_path_name)) @@ -417,6 +453,7 @@ def whisper_nessus(self): self.CONFIG_SECTION, uuid, 1, + 0, ) self.record_insert(record_meta) self.logger.info('{filename} records written to {path} '.format(filename=clean_csv.shape[0], @@ -432,6 +469,7 @@ def whisper_nessus(self): self.CONFIG_SECTION, uuid, 1, + 0, ) self.record_insert(record_meta) self.logger.warn('{} has no host available... Updating database and skipping!'.format(file_name)) @@ -550,6 +588,7 @@ def whisper_reports(self, self.CONFIG_SECTION, report_id, 1, + 0, ) self.record_insert(record_meta) self.logger.info('File {filename} already exist! Updating database'.format(filename=relative_path_name)) @@ -579,6 +618,7 @@ def whisper_reports(self, self.CONFIG_SECTION, report_id, 1, + 0, ) self.record_insert(record_meta) @@ -712,6 +752,7 @@ def whisper_reports(self, output_format='json', launched_date=None, report_id=No self.CONFIG_SECTION, report_id, 1, + 0, ) self.record_insert(record_meta) self.logger.info('File {filename} already exist! Updating database'.format(filename=relative_path_name)) @@ -829,6 +870,7 @@ def whisper_reports(self, self.CONFIG_SECTION, report_id, 1, + 0, ) self.record_insert(record_meta) self.logger.info('File {filename} already exist! Updating database'.format(filename=relative_path_name)) @@ -850,6 +892,7 @@ def whisper_reports(self, self.CONFIG_SECTION, report_id, 1, + 0, ) self.record_insert(record_meta) @@ -970,7 +1013,7 @@ def get_env_variables(self, source, scan_name): sys.exit(0) #datafile path - filename = self.get_latest_results(source, scan_name) + filename, processed = self.get_latest_results(source, scan_name) fullpath = "" # search data files under user specified directory @@ -978,8 +1021,12 @@ def get_env_variables(self, source, scan_name): if filename in filenames: fullpath = "{}/{}".format(root,filename) + if processed: + self.logger.warn('Last Scan of "{scan_name}" for source "{source}" has already been processed; will be skipped.'.format(scan_name=scan_name, source=source)) + return [False] * 5 + if not fullpath: - self.logger.error('Scan file path "{scan_name}" for source "{source}" has not been found.'.format(scan_name=scan_name, source=source)) + self.logger.error('Scan of "{scan_name}" for source "{source}" has not been found. Please check that the scanner data files are in place.'.format(scan_name=scan_name, source=source)) sys.exit(1) dns_resolv = self.config.get('jira','dns_resolv') @@ -1137,6 +1184,10 @@ def jira_sync(self, source, scan_name): project, components, fullpath, min_critical, dns_resolv = self.get_env_variables(source, scan_name) + if not project: + self.logger.debug("Skipping scan for source '{source}' and scan '{scan_name}'".format(source=source, scan_name=scan_name)) + return False + vulnerabilities = [] #***Nessus parsing*** @@ -1155,8 +1206,10 @@ def jira_sync(self, source, scan_name): self.jira.sync(vulnerabilities, project, components) else: self.logger.info("[{source}.{scan_name}] No vulnerabilities or vulnerabilities not parsed.".format(source=source, scan_name=scan_name)) + self.set_latest_scan_processed(fullpath.split("/")[-1]) return False + self.set_latest_scan_processed(fullpath.split("/")[-1]) return True def sync_all(self): From f33644b81440eb0ca56af53b0543ed4b4814fae8 Mon Sep 17 00:00:00 2001 From: Quim Date: Tue, 2 Apr 2019 11:58:44 +0200 Subject: [PATCH 36/96] fix reported tracking for jira --- bin/vuln_whisperer | 2 ++ vulnwhisp/vulnwhisp.py | 28 +++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/bin/vuln_whisperer b/bin/vuln_whisperer index f5273ef..187abbf 100644 --- a/bin/vuln_whisperer +++ b/bin/vuln_whisperer @@ -39,6 +39,8 @@ def main(): # First setup logging logging.basicConfig( stream=sys.stdout, + #format only applies when not using -F flag for colouring + format='%(levelname)s:%(name)s:%(funcName)s:%(message)s', level=logging.DEBUG if args.debug else logging.INFO ) logger = logging.getLogger() diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index ea1e8ed..8a5af1a 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -161,12 +161,13 @@ def record_insert(self, record): self.logger.error("Failed to insert record in database. Error: {}".format(e)) sys.exit(1) - def set_latest_scan_processed(self, filename): + def set_latest_scan_reported(self, filename): #the reason to use the filename instead of the source/scan_name is because the filename already belongs to #that latest scan, and we maintain integrity making sure that it is the exact scan we checked try: - self.cur.execute('UPDATE scan_history SET processed = 1 WHERE filename="{}";'.format(filename)) - self.logger.debug('Scan {} marked as successfully processed.'.format(filename)) + self.cur.execute('UPDATE scan_history SET reported = 1 WHERE filename="{}";'.format(filename)) + self.conn.commit() + self.logger.info('Scan {} marked as successfully processed.'.format(filename)) return True except Exception as e: self.logger.error('Failed while setting scan with file {} as processed'.format(filename)) @@ -210,13 +211,14 @@ def get_latest_results(self, source, scan_name): #TODO delete backward compatibility check after some versions last_column_table = self.cur.execute('PRAGMA table_info(scan_history)').fetchall()[-1][1] if results and last_column_table == self.table_columns[-1]: - processed = self.cur.execute('SELECT processed FROM scan_history WHERE filename="{}"'.format(results)).fetchall()[0][0] - if processed: - self.logger.debug("Last downloaded scan from source {source} scan_name {scan_name} has already been processed".format(source=source, scan_name=scan_name)) + reported = self.cur.execute('SELECT reported FROM scan_history WHERE filename="{}"'.format(results)).fetchall() + reported = reported[0][0] + if reported: + self.logger.debug("Last downloaded scan from source {source} scan_name {scan_name} has already been reported".format(source=source, scan_name=scan_name)) except Exception as e: self.logger.error("Error when getting latest results from {}.{} : {}".format(source, scan_name, e)) - return results, processed + return results, reported def get_scan_profiles(self): # Returns a list of source.scan_name elements from the database @@ -1013,7 +1015,7 @@ def get_env_variables(self, source, scan_name): sys.exit(0) #datafile path - filename, processed = self.get_latest_results(source, scan_name) + filename, reported = self.get_latest_results(source, scan_name) fullpath = "" # search data files under user specified directory @@ -1021,8 +1023,8 @@ def get_env_variables(self, source, scan_name): if filename in filenames: fullpath = "{}/{}".format(root,filename) - if processed: - self.logger.warn('Last Scan of "{scan_name}" for source "{source}" has already been processed; will be skipped.'.format(scan_name=scan_name, source=source)) + if reported: + self.logger.warn('Last Scan of "{scan_name}" for source "{source}" has already been reported; will be skipped.'.format(scan_name=scan_name, source=source)) return [False] * 5 if not fullpath: @@ -1185,7 +1187,7 @@ def jira_sync(self, source, scan_name): project, components, fullpath, min_critical, dns_resolv = self.get_env_variables(source, scan_name) if not project: - self.logger.debug("Skipping scan for source '{source}' and scan '{scan_name}'".format(source=source, scan_name=scan_name)) + self.logger.debug("Skipping scan for source '{source}' and scan '{scan_name}': vulnerabilities have already been reported.".format(source=source, scan_name=scan_name)) return False vulnerabilities = [] @@ -1206,10 +1208,10 @@ def jira_sync(self, source, scan_name): self.jira.sync(vulnerabilities, project, components) else: self.logger.info("[{source}.{scan_name}] No vulnerabilities or vulnerabilities not parsed.".format(source=source, scan_name=scan_name)) - self.set_latest_scan_processed(fullpath.split("/")[-1]) + self.set_latest_scan_reported(fullpath.split("/")[-1]) return False - self.set_latest_scan_processed(fullpath.split("/")[-1]) + self.set_latest_scan_reported(fullpath.split("/")[-1]) return True def sync_all(self): From a30a22ab9862f3471ae285f3694766c2d87c07ea Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 3 Apr 2019 15:15:31 +0200 Subject: [PATCH 37/96] fix wrong parenthesis on qualys was --- vulnwhisp/frameworks/qualys_web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulnwhisp/frameworks/qualys_web.py b/vulnwhisp/frameworks/qualys_web.py index 1e7fc1c..c687441 100644 --- a/vulnwhisp/frameworks/qualys_web.py +++ b/vulnwhisp/frameworks/qualys_web.py @@ -135,7 +135,7 @@ def get_all_scans(self, limit=1000, offset=1, status='FINISHED'): if i % limit == 0: if (total - i) < limit: qualys_api_limit = total - i - self.logger.info('Making a request with a limit of {} at offset {}'.format((str(qualys_api_limit), str(i + 1)))) + self.logger.info('Making a request with a limit of {} at offset {}'.format((str(qualys_api_limit)), str(i + 1))) scan_info = self.get_scan_info(limit=qualys_api_limit, offset=i + 1, status=status) _records.append(scan_info) self.logger.debug('Converting XML to DataFrame') From 03f7a4cedb41fa5dc3771ae4524555c2c702b399 Mon Sep 17 00:00:00 2001 From: Quim Date: Thu, 4 Apr 2019 11:05:39 +0200 Subject: [PATCH 38/96] fixed line --- vulnwhisp/frameworks/qualys_vuln.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vulnwhisp/frameworks/qualys_vuln.py b/vulnwhisp/frameworks/qualys_vuln.py index 43495b1..5a07b89 100644 --- a/vulnwhisp/frameworks/qualys_vuln.py +++ b/vulnwhisp/frameworks/qualys_vuln.py @@ -25,8 +25,7 @@ def __init__(self, config=None): self.logger.info('Connected to Qualys at {}'.format(self.qgc.server)) except Exception as e: self.logger.error('Could not connect to Qualys: {}'.format(str(e))) - # FIXME: exit(1) does not exist: either it's exit() or sys.exit(CODE) - exit(1) + sys.exit(1) def scan_xml_parser(self, xml): all_records = [] From eae64a745df3343ccababdc801bda70d0c0130f9 Mon Sep 17 00:00:00 2001 From: Quim Date: Thu, 4 Apr 2019 11:24:01 +0200 Subject: [PATCH 39/96] cleanup of unused code and fixes, still breaks --- vulnwhisp/frameworks/qualys_web.py | 412 +++-------------------------- 1 file changed, 37 insertions(+), 375 deletions(-) diff --git a/vulnwhisp/frameworks/qualys_web.py b/vulnwhisp/frameworks/qualys_web.py index c687441..9e9d762 100644 --- a/vulnwhisp/frameworks/qualys_web.py +++ b/vulnwhisp/frameworks/qualys_web.py @@ -42,33 +42,23 @@ def __init__(self, config=None): except Exception as e: self.logger.error('Could not connect to Qualys: {}'.format(str(e))) self.headers = { - "content-type": "text/xml"} + #"content-type": "text/xml"} + "Accept" : "application/json", + "Content-Type": "application/json"} self.config_parse = qcconf.QualysConnectConfig(config, 'qualys_web') try: self.template_id = self.config_parse.get_template_id() except: self.logger.error('Could not retrieve template ID') - def request(self, path, method='get', data=None): - methods = {'get': requests.get, - 'post': requests.post} - base = 'https://' + self.qgc.server + path - req = methods[method](base, auth=self.qgc.auth, data=data, headers=self.headers).content - return req - - def get_version(self): - return self.request(self.VERSION) - - def get_scan_count(self, scan_name): - parameters = ( - E.ServiceRequest( - E.filters( - E.Criteria({'field': 'name', 'operator': 'CONTAINS'}, scan_name)))) - xml_output = self.qgc.request(self.COUNT_WEBAPP, parameters) - root = objectify.fromstring(xml_output) - return root.count.text + #### + #### GET SCANS TO PROCESS + #### def get_was_scan_count(self, status): + """ + Checks number of scans, used to control the api limits + """ parameters = ( E.ServiceRequest( E.filters( @@ -77,8 +67,23 @@ def get_was_scan_count(self, status): root = objectify.fromstring(xml_output.encode('utf-8')) return root.count.text - def get_reports(self): - return self.qgc.request(self.SEARCH_REPORTS) + def generate_scan_result_XML(self, limit=1000, offset=1, status='FINISHED'): + report_xml = E.ServiceRequest( + E.filters( + E.Criteria({'field': 'status', 'operator': 'EQUALS'}, status + ), + ), + E.preferences( + E.startFromOffset(str(offset)), + E.limitResults(str(limit)) + ), + ) + return report_xml + + def get_scan_info(self, limit=1000, offset=1, status='FINISHED'): + """ Returns XML of ALL WAS Scans""" + data = self.generate_scan_result_XML(limit=limit, offset=offset, status=status) + return self.qgc.request(self.SEARCH_WAS_SCAN, data) def xml_parser(self, xml, dupfield=None): all_records = [] @@ -98,31 +103,6 @@ def xml_parser(self, xml, dupfield=None): all_records.append(record) return pd.DataFrame(all_records) - def get_report_list(self): - """Returns a dataframe of reports""" - return self.xml_parser(self.get_reports(), dupfield='user_id') - - def get_web_apps(self): - """Returns webapps available for account""" - return self.qgc.request(self.SEARCH_WEB_APPS) - - def get_web_app_list(self): - """Returns dataframe of webapps""" - return self.xml_parser(self.get_web_apps(), dupfield='user_id') - - def get_web_app_details(self, was_id): - """Get webapp details - use to retrieve app ID tag""" - return self.qgc.request(self.GET_WEBAPP_DETAILS.format(was_id=was_id)) - - def get_scans_by_app_id(self, app_id): - data = self.generate_app_id_scan_XML(app_id) - return self.qgc.request(self.SEARCH_WAS_SCAN, data) - - def get_scan_info(self, limit=1000, offset=1, status='FINISHED'): - """ Returns XML of ALL WAS Scans""" - data = self.generate_scan_result_XML(limit=limit, offset=offset, status=status) - return self.qgc.request(self.SEARCH_WAS_SCAN, data) - def get_all_scans(self, limit=1000, offset=1, status='FINISHED'): qualys_api_limit = limit dataframes = [] @@ -145,11 +125,9 @@ def get_all_scans(self, limit=1000, offset=1, status='FINISHED'): return pd.concat(dataframes, axis=0).reset_index().drop('index', axis=1) - def get_scan_details(self, scan_id): - return self.qgc.request(self.SCAN_DETAILS.format(scan_id=scan_id)) - - def get_report_details(self, report_id): - return self.qgc.request(self.REPORT_DETAILS.format(report_id=report_id)) + #### + #### NEXT THING + #### def get_report_status(self, report_id): return self.qgc.request(self.REPORT_STATUS.format(report_id=report_id)) @@ -157,34 +135,18 @@ def get_report_status(self, report_id): def download_report(self, report_id): return self.qgc.request(self.REPORT_DOWNLOAD.format(report_id=report_id)) - def download_scan_results(self, scan_id): - return self.qgc.request(self.SCAN_DOWNLOAD.format(scan_id=scan_id)) - - def generate_scan_result_XML(self, limit=1000, offset=1, status='FINISHED'): - report_xml = E.ServiceRequest( - E.filters( - E.Criteria({'field': 'status', 'operator': 'EQUALS'}, status - ), - ), - E.preferences( - E.startFromOffset(str(offset)), - E.limitResults(str(limit)) - ), - ) - return report_xml - def generate_scan_report_XML(self, scan_id): """Generates a CSV report for an asset based on template defined in .ini file""" report_xml = E.ServiceRequest( E.data( E.Report( E.name('![CDATA[API Scan Report generated by VulnWhisperer]]>'), - E.description(''), + E.description('![CDATA[CSV Scanning report for VulnWhisperer]]'), E.format('CSV'), E.type('WAS_SCAN_REPORT'), - E.template( - E.id(self.template_id) - ), + #E.template( + # E.id(self.template_id) + #), E.config( E.scanReport( E.target( @@ -228,15 +190,6 @@ def generate_webapp_report_XML(self, app_id): ) return report_xml - def generate_app_id_scan_XML(self, app_id): - report_xml = E.ServiceRequest( - E.filters( - E.Criteria({'field': 'webApp.id', 'operator': 'EQUALS'}, app_id - ), - ), - ) - return report_xml - def create_report(self, report_id, kind='scan'): mapper = {'scan': self.generate_scan_report_XML, 'webapp': self.generate_webapp_report_XML} @@ -244,8 +197,7 @@ def create_report(self, report_id, kind='scan'): data = mapper[kind](report_id) except Exception as e: self.logger.error('Error creating report: {}'.format(str(e))) - - return self.qgc.request(self.REPORT_CREATE, data) + return self.qgc.request(self.REPORT_CREATE, data).encode('utf-8') def delete_report(self, report_id): return self.qgc.request(self.DELETE_REPORT.format(report_id=report_id)) @@ -363,237 +315,6 @@ def cleanser(self, _data): _data = reduce(lambda a, kv: a.replace(*kv), repls, str(_data)) return _data - -class qualysWebAppReport: - # URL Vulnerability Information - WEB_APP_VULN_BLOCK = list(qualysReportFields.VULN_BLOCK) - WEB_APP_VULN_BLOCK.insert(0, 'Web Application Name') - WEB_APP_VULN_BLOCK.insert(WEB_APP_VULN_BLOCK.index('Ignored'), 'Status') - - WEB_APP_VULN_HEADER = list(WEB_APP_VULN_BLOCK) - WEB_APP_VULN_HEADER[WEB_APP_VULN_BLOCK.index(qualysReportFields.CATEGORIES[0])] = \ - 'Vulnerability Category' - - WEB_APP_SENSITIVE_HEADER = list(WEB_APP_VULN_HEADER) - WEB_APP_SENSITIVE_HEADER.insert(WEB_APP_SENSITIVE_HEADER.index('Url' - ), 'Content') - - WEB_APP_SENSITIVE_BLOCK = list(WEB_APP_SENSITIVE_HEADER) - WEB_APP_SENSITIVE_BLOCK[WEB_APP_SENSITIVE_BLOCK.index('Vulnerability Category' - )] = qualysReportFields.CATEGORIES[1] - - WEB_APP_INFO_HEADER = list(qualysReportFields.INFO_HEADER) - WEB_APP_INFO_HEADER.insert(0, 'Web Application Name') - - WEB_APP_INFO_BLOCK = list(qualysReportFields.INFO_BLOCK) - WEB_APP_INFO_BLOCK.insert(0, 'Web Application Name') - - QID_HEADER = list(qualysReportFields.QID_HEADER) - GROUP_HEADER = list(qualysReportFields.GROUP_HEADER) - OWASP_HEADER = list(qualysReportFields.OWASP_HEADER) - WASC_HEADER = list(qualysReportFields.WASC_HEADER) - SCAN_META = list(qualysReportFields.SCAN_META) - CATEGORY_HEADER = list(qualysReportFields.CATEGORY_HEADER) - - def __init__( - self, - config=None, - file_in=None, - file_stream=False, - delimiter=',', - quotechar='"', - ): - self.logger = logging.getLogger('qualysWebAppReport') - self.file_in = file_in - self.file_stream = file_stream - self.report = None - self.utils = qualysUtils() - - if config: - try: - self.qw = qualysWhisperAPI(config=config) - except Exception as e: - self.logger.error('Could not load config! Please check settings. Error: {}'.format(str(e))) - - if file_stream: - self.open_file = file_in.splitlines() - elif file_in: - - self.open_file = open(file_in, 'rb') - - self.downloaded_file = None - - def get_hostname(self, report): - host = '' - with open(report, 'rb') as csvfile: - q_report = csv.reader(csvfile, delimiter=',', quotechar='"') - for x in q_report: - - if 'Web Application Name' in x[0]: - host = q_report.next()[0] - return host - - def get_scanreport_name(self, report): - scan_name = '' - with open(report, 'rb') as csvfile: - q_report = csv.reader(csvfile, delimiter=',', quotechar='"') - for x in q_report: - - if 'Scans' in x[0]: - scan_name = x[1] - return scan_name - - def grab_sections(self, report): - all_dataframes = [] - dict_tracker = {} - with open(report, 'rb') as csvfile: - dict_tracker['WEB_APP_VULN_BLOCK'] = pd.DataFrame(self.utils.grab_section(report, - self.WEB_APP_VULN_BLOCK, - end=[self.WEB_APP_SENSITIVE_BLOCK, - self.WEB_APP_INFO_BLOCK], - pop_last=True), columns=self.WEB_APP_VULN_HEADER) - dict_tracker['WEB_APP_SENSITIVE_BLOCK'] = pd.DataFrame(self.utils.grab_section(report, - self.WEB_APP_SENSITIVE_BLOCK, - end=[self.WEB_APP_INFO_BLOCK, - self.WEB_APP_SENSITIVE_BLOCK], - pop_last=True), columns=self.WEB_APP_SENSITIVE_HEADER) - dict_tracker['WEB_APP_INFO_BLOCK'] = pd.DataFrame(self.utils.grab_section(report, - self.WEB_APP_INFO_BLOCK, - end=[self.QID_HEADER], - pop_last=True), columns=self.WEB_APP_INFO_HEADER) - dict_tracker['QID_HEADER'] = pd.DataFrame(self.utils.grab_section(report, - self.QID_HEADER, - end=[self.GROUP_HEADER], - pop_last=True), columns=self.QID_HEADER) - dict_tracker['GROUP_HEADER'] = pd.DataFrame(self.utils.grab_section(report, - self.GROUP_HEADER, - end=[self.OWASP_HEADER], - pop_last=True), columns=self.GROUP_HEADER) - dict_tracker['OWASP_HEADER'] = pd.DataFrame(self.utils.grab_section(report, - self.OWASP_HEADER, - end=[self.WASC_HEADER], - pop_last=True), columns=self.OWASP_HEADER) - dict_tracker['WASC_HEADER'] = pd.DataFrame(self.utils.grab_section(report, - self.WASC_HEADER, end=[['APPENDIX']], - pop_last=True), columns=self.WASC_HEADER) - dict_tracker['CATEGORY_HEADER'] =pd.DataFrame(self.utils.grab_section(report, - self.CATEGORY_HEADER), columns=self.CATEGORY_HEADER) - all_dataframes.append(dict_tracker) - - return all_dataframes - - def data_normalizer(self, dataframes): - """ - Merge and clean data - :param dataframes: - :return: - """ - df_dict = dataframes[0] - merged_df = pd.concat([df_dict['WEB_APP_VULN_BLOCK'], df_dict['WEB_APP_SENSITIVE_BLOCK'], - df_dict['WEB_APP_INFO_BLOCK']], axis=0, - ignore_index=False) - - merged_df = pd.merge(merged_df, df_dict['QID_HEADER'], left_on='QID', - right_on='Id') - - merged_df = pd.concat([dataframes[0], dataframes[1], - dataframes[2]], axis=0, - ignore_index=False) - merged_df = pd.merge(merged_df, dataframes[3], left_on='QID', - right_on='Id') - - if 'Content' not in merged_df: - merged_df['Content'] = '' - - columns_to_cleanse = ['Payload #1', 'Request Method #1', 'Request URL #1', - 'Request Headers #1', 'Response #1', 'Evidence #1', - 'Description', 'Impact', 'Solution', 'Url', 'Content'] - - for col in columns_to_cleanse: - merged_df[col] = merged_df[col].astype(str).apply(self.utils.cleanser) - - merged_df = pd.merge(merged_df, df_dict['CATEGORY_HEADER']) - merged_df = merged_df.drop(['QID_y', 'QID_x'], axis=1) - merged_df = merged_df.rename(columns={'Id': 'QID'}) - merged_df = merged_df.replace('N/A','').fillna('') - - try: - merged_df = \ - merged_df[~merged_df.Title.str.contains('Links Crawled|External Links Discovered' - )] - except Exception as e: - self.logger.error('Error merging df: {}'.format(str(e))) - return merged_df - - def download_file(self, file_id): - report = self.qw.download_report(file_id) - filename = str(file_id) + '.csv' - file_out = open(filename, 'w') - for line in report.splitlines(): - file_out.write(line + '\n') - file_out.close() - self.logger.info('File written to {}'.format(filename)) - return filename - - def remove_file(self, filename): - os.remove(filename) - - def process_data(self, file_id, scan=True, cleanup=True): - """Downloads a file from qualys and normalizes it""" - - download_file = self.download_file(file_id) - self.logger.info('Downloading file ID: {}'.format(file_id)) - report_data = self.grab_sections(download_file) - merged_data = self.data_normalizer(report_data) - if scan: - scan_name = self.get_scanreport_name(download_file) - merged_data['ScanName'] = scan_name - - # TODO cleanup old data (delete) - - return merged_data - - def whisper_reports(self, report_id, updated_date, cleanup=False): - """ - report_id: App ID - updated_date: Last time scan was ran for app_id - """ - vuln_ready = None - try: - - if 'Z' in updated_date: - updated_date = self.utils.iso_to_epoch(updated_date) - report_name = 'qualys_web_' + str(report_id) \ - + '_{last_updated}'.format(last_updated=updated_date) \ - + '.csv' - if os.path.isfile(report_name): - self.logger.info('File already exists! Skipping...') - pass - else: - self.logger.info('Generating report for {}'.format(report_id)) - status = self.qw.create_report(report_id) - root = objectify.fromstring(status) - if root.responseCode == 'SUCCESS': - self.logger.info('Successfully generated report for webapp: {}'.format(report_id)) - generated_report_id = root.data.Report.id - self.logger.info('New Report ID: {}'.format(generated_report_id)) - vuln_ready = self.process_data(generated_report_id) - - vuln_ready.to_csv(report_name, index=False, header=True) # add when timestamp occured - self.logger.info('Report written to {}'.format(report_name)) - if cleanup: - self.logger.info('Removing report {}'.format(generated_report_id)) - cleaning_up = \ - self.qw.delete_report(generated_report_id) - self.remove_file(str(generated_report_id) + '.csv') - self.logger.info('Deleted report: {}'.format(generated_report_id)) - else: - self.logger.error('Could not process report ID: {}'.format(status)) - except Exception as e: - self.logger.error('Could not process {}: {}'.format(report_id, e)) - return vuln_ready - - class qualysScanReport: # URL Vulnerability Information WEB_SCAN_VULN_BLOCK = list(qualysReportFields.VULN_BLOCK) @@ -734,6 +455,8 @@ def data_normalizer(self, dataframes): merged_df = merged_df.drop(['QID_y', 'QID_x'], axis=1) merged_df = merged_df.rename(columns={'Id': 'QID'}) + + #TODO CODE BREAKS HERE, SCAN_META IS AN EMPTY DATAFRAME merged_df = merged_df.assign(**df_dict['SCAN_META'].to_dict(orient='records')[0]) merged_df = pd.merge(merged_df, df_dict['CATEGORY_HEADER'], how='left', left_on=['Category', 'Severity Level'], @@ -743,8 +466,7 @@ def data_normalizer(self, dataframes): try: merged_df = \ - merged_df[~merged_df.Title.str.contains('Links Crawled|External Links Discovered' - )] + merged_df[~merged_df.Title.str.contains('Links Crawled|External Links Discovered')] except Exception as e: self.logger.error('Error normalizing: {}'.format(str(e))) return merged_df @@ -759,9 +481,6 @@ def download_file(self, path='', file_id=None): self.logger.info('File written to {}'.format(filename)) return filename - def remove_file(self, filename): - os.remove(filename) - def process_data(self, path='', file_id=None, cleanup=True): """Downloads a file from qualys and normalizes it""" @@ -770,62 +489,5 @@ def process_data(self, path='', file_id=None, cleanup=True): report_data = self.grab_sections(download_file) merged_data = self.data_normalizer(report_data) merged_data.sort_index(axis=1, inplace=True) - # TODO cleanup old data (delete) return merged_data - - def whisper_reports(self, report_id, updated_date, cleanup=False): - """ - report_id: App ID - updated_date: Last time scan was ran for app_id - """ - vuln_ready = None - try: - - if 'Z' in updated_date: - updated_date = self.utils.iso_to_epoch(updated_date) - report_name = 'qualys_web_' + str(report_id) \ - + '_{last_updated}'.format(last_updated=updated_date) \ - + '.csv' - if os.path.isfile(report_name): - self.logger.info('File already exist! Skipping...') - else: - self.logger.info('Generating report for {}'.format(report_id)) - status = self.qw.create_report(report_id) - root = objectify.fromstring(status) - if root.responseCode == 'SUCCESS': - self.logger.info('Successfully generated report for webapp: {}'.format(report_id)) - generated_report_id = root.data.Report.id - self.logger.info('New Report ID: {}'.format(generated_report_id)) - vuln_ready = self.process_data(generated_report_id) - - vuln_ready.to_csv(report_name, index=False, header=True) # add when timestamp occured - self.logger.info('Report written to {}'.format(report_name)) - if cleanup: - self.logger.info('Removing report {} from disk'.format(generated_report_id)) - cleaning_up = \ - self.qw.delete_report(generated_report_id) - self.remove_file(str(generated_report_id) + '.csv') - self.logger.info('Deleted report from Qualys Database: {}'.format(generated_report_id)) - else: - self.logger.error('Could not process report ID: {}'.format(status)) - except Exception as e: - self.logger.error('Could not process {}: {}'.format(report_id, e)) - return vuln_ready - - -maxInt = int(4000000) -maxSize = sys.maxsize - -if maxSize > maxInt and type(maxSize) == int: - maxInt = maxSize - -decrement = True - -while decrement: - decrement = False - try: - csv.field_size_limit(maxInt) - except OverflowError: - maxInt = int(maxInt/10) - decrement = True From 71352aee57b379cab718f021d7538541a1422778 Mon Sep 17 00:00:00 2001 From: pemontto <939704+pemontto@users.noreply.github.com> Date: Fri, 5 Apr 2019 19:57:39 +1100 Subject: [PATCH 40/96] Add external API mocking and travis tests (#164) * Fix closing logging handlers * Fix *some* unicode issues for nessus and qualys * Prevent multiple requests to nessus scans endpoint * More unicode fixes * Remove unnecessary call * Fix whitespace * Add mock module and argument * Add test config and data * Fix whitespace again * Disable qualys_web until data is available * Use logging module * Delete report_tracker.db * Cleanup mock calls * Add httpretty to requirements * Refactor into a class * Updates travis tests * Fix exit codes * Remove print statements * Remove test * Add test directory as submodule --- .gitmodules | 3 + .travis.yml | 3 +- bin/vuln_whisperer | 17 +++-- configs/test.ini | 110 ++++++++++++++++++++++++++++ requirements.txt | 1 + resources/elk6/init_kibana.sh | 2 +- test | 1 + vulnwhisp/base/config.py | 8 +- vulnwhisp/frameworks/qualys_vuln.py | 4 +- vulnwhisp/test/__init__.py | 0 vulnwhisp/test/mock.py | 75 +++++++++++++++++++ vulnwhisp/vulnwhisp.py | 16 ++-- 12 files changed, 221 insertions(+), 19 deletions(-) create mode 100644 .gitmodules create mode 100755 configs/test.ini create mode 160000 test create mode 100644 vulnwhisp/test/__init__.py create mode 100644 vulnwhisp/test/mock.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4d6eb1b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test"] + path = test + url = https://github.com/HASecuritySolutions/VulnWhisperer-tests diff --git a/.travis.yml b/.travis.yml index d82721d..f11e590 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,8 @@ before_script: # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --exclude=deps/qualysapi --max-complexity=10 --max-line-length=127 --statistics script: - - true # pytest --capture=sys # add other tests here + - python setup.py install + - vuln_whisperer -c configs/test.ini --mock --mock_dir test notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down diff --git a/bin/vuln_whisperer b/bin/vuln_whisperer index 187abbf..23d2000 100644 --- a/bin/vuln_whisperer +++ b/bin/vuln_whisperer @@ -5,6 +5,7 @@ __author__ = 'Austin Taylor' from vulnwhisp.vulnwhisp import vulnWhisperer from vulnwhisp.base.config import vwConfig +from vulnwhisp.test.mock import mockAPI import os import argparse import sys @@ -34,6 +35,9 @@ def main(): parser.add_argument('-p', '--password', dest='password', required=False, default=None, type=lambda x: x.strip(), help='The NESSUS password') parser.add_argument('-F', '--fancy', action='store_true', help='Enable colourful logging output') parser.add_argument('-d', '--debug', action='store_true', help='Enable debugging messages') + parser.add_argument('--mock', action='store_true', help='Enable mocked API responses') + parser.add_argument('--mock_dir', dest='mock_dir', required=False, default='test', + help='Path of test directory') args = parser.parse_args() # First setup logging @@ -54,9 +58,12 @@ def main(): import coloredlogs coloredlogs.install(level='DEBUG' if args.debug else 'INFO') + if args.mock: + mock_api = mockAPI(args.mock_dir, args.verbose) + mock_api.mock_endpoints() + try: if args.config and not args.section: - # this remains a print since we are in the main binary print('WARNING: {warning}'.format(warning='No section was specified, vulnwhisperer will scrape enabled modules from config file. \ \nPlease specify a section using -s. \ @@ -74,10 +81,10 @@ def main(): source=args.source, scanname=args.scanname) - vw.whisper_vulnerabilities() + exit_code = vw.whisper_vulnerabilities() # TODO: fix this to NOT be exit 1 unless in error close_logging_handlers(logger) - sys.exit(1) + sys.exit(exit_code) else: logger.info('Running vulnwhisperer for section {}'.format(args.section)) @@ -89,10 +96,10 @@ def main(): source=args.source, scanname=args.scanname) - vw.whisper_vulnerabilities() + exit_code = vw.whisper_vulnerabilities() # TODO: fix this to NOT be exit 1 unless in error close_logging_handlers(logger) - sys.exit(1) + sys.exit(exit_code) except Exception as e: if args.verbose: diff --git a/configs/test.ini b/configs/test.ini new file mode 100755 index 0000000..468ba4a --- /dev/null +++ b/configs/test.ini @@ -0,0 +1,110 @@ +[nessus] +enabled=true +hostname=nessus +port=443 +username=nessus_username +password=nessus_password +write_path=/tmp/VulnWhisperer/data/nessus/ +db_path=/tmp/VulnWhisperer/data/database +trash=false +verbose=true + +[tenable] +enabled=true +hostname=tenable +port=443 +username=tenable.io_username +password=tenable.io_password +write_path=/tmp/VulnWhisperer/data/tenable/ +db_path=/tmp/VulnWhisperer/data/database +trash=false +verbose=true + +[qualys_web] +#Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API +enabled = false +hostname = qualys_web +username = exampleuser +password = examplepass +write_path=/tmp/VulnWhisperer/data/qualys/ +db_path=/tmp/VulnWhisperer/data/database +verbose=true + +# Set the maximum number of retries each connection should attempt. +#Note, this applies only to failed connections and timeouts, never to requests where the server returns a response. +max_retries = 10 +# Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. +template_id = 126024 + +[qualys_vuln] +#Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API +enabled = true +hostname = qualys_vuln +username = exampleuser +password = examplepass +write_path=/tmp/VulnWhisperer/data/qualys/ +db_path=/tmp/VulnWhisperer/data/database +verbose=true + +# Set the maximum number of retries each connection should attempt. +#Note, this applies only to failed connections and timeouts, never to requests where the server returns a response. +max_retries = 10 +# Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. +template_id = 126024 + +[detectify] +#Reference https://developer.detectify.com/ +enabled = false +hostname = detectify +#username variable used as apiKey +username = exampleuser +#password variable used as secretKey +password = examplepass +write_path =/tmp/VulnWhisperer/data/detectify/ +db_path = /tmp/VulnWhisperer/data/database +verbose = true + +[openvas] +enabled = false +hostname = openvas +port = 4000 +username = exampleuser +password = examplepass +write_path=/tmp/VulnWhisperer/data/openvas/ +db_path=/tmp/VulnWhisperer/data/database +verbose=true + +#[proxy] +; This section is optional. Leave it out if you're not using a proxy. +; You can use environmental variables as well: http://www.python-requests.org/en/latest/user/advanced/#proxies + +; proxy_protocol set to https, if not specified. +#proxy_url = proxy.mycorp.com + +; proxy_port will override any port specified in proxy_url +#proxy_port = 8080 + +; proxy authentication +#proxy_username = proxyuser +#proxy_password = proxypass + +[jira] +enabled = false +hostname = jira-host +username = username +password = password +write_path = /tmp/VulnWhisperer/data/jira/ +db_path = /tmp/VulnWhisperer/data/database +verbose = true +dns_resolv = False + +#Sample jira report scan, will automatically be created for existent scans +#[jira.qualys_vuln.test_scan] +#source = qualys_vuln +#scan_name = Test Scan +#jira_project = PROJECT +; if multiple components, separate by "," = None +#components = +; minimum criticality to report (low, medium, high or critical) = None +#min_critical_to_report = high + diff --git a/requirements.txt b/requirements.txt index a49e39d..0697037 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,4 @@ jira bottle coloredlogs qualysapi>=5.1.0 +httpretty \ No newline at end of file diff --git a/resources/elk6/init_kibana.sh b/resources/elk6/init_kibana.sh index e9c6075..ca23d74 100755 --- a/resources/elk6/init_kibana.sh +++ b/resources/elk6/init_kibana.sh @@ -14,7 +14,7 @@ until [ "`curl -I "$kibana_url"/status | head -n1 |cut -d$' ' -f2`" == "200" ]; echo "Waiting for Kibana" sleep 5 done - + echo "Loading VulnWhisperer Saved Objects" echo $add_saved_objects$saved_objects_file eval $(echo $add_saved_objects$saved_objects_file) diff --git a/test b/test new file mode 160000 index 0000000..606b8bc --- /dev/null +++ b/test @@ -0,0 +1 @@ +Subproject commit 606b8bcbe32b057376ea04b9b2e6a0b0e4454006 diff --git a/vulnwhisp/base/config.py b/vulnwhisp/base/config.py index 04cbb2a..7786883 100644 --- a/vulnwhisp/base/config.py +++ b/vulnwhisp/base/config.py @@ -26,16 +26,16 @@ def getbool(self, section, option): return self.config.getboolean(section, option) def get_sections_with_attribute(self, attribute): - sections = [] + sections = [] # TODO: does this not also need the "yes" case? - check = ["true", "True", "1"] - for section in self.config.sections(): + check = ["true", "True", "1"] + for section in self.config.sections(): try: if self.get(section, attribute) in check: sections.append(section) except: self.logger.warn("Section {} has no option '{}'".format(section, attribute)) - return sections + return sections def exists_jira_profiles(self, profiles): # get list of profiles source_scanner.scan_name diff --git a/vulnwhisp/frameworks/qualys_vuln.py b/vulnwhisp/frameworks/qualys_vuln.py index 43495b1..08df551 100644 --- a/vulnwhisp/frameworks/qualys_vuln.py +++ b/vulnwhisp/frameworks/qualys_vuln.py @@ -64,8 +64,8 @@ def get_scan_details(self, scan_id=None): # First two columns are metadata we already have # Last column corresponds to "target_distribution_across_scanner_appliances" element - # which doesn't follow the schema and breaks the pandas data manipulation - return pd.read_json(scan_json).iloc[2:-1] + # which doesn't follow the schema and breaks the pandas data manipulation + return pd.read_json(scan_json).iloc[2:-1] class qualysUtils: def __init__(self): diff --git a/vulnwhisp/test/__init__.py b/vulnwhisp/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vulnwhisp/test/mock.py b/vulnwhisp/test/mock.py new file mode 100644 index 0000000..6d05e65 --- /dev/null +++ b/vulnwhisp/test/mock.py @@ -0,0 +1,75 @@ +import os +import logging +import httpretty + +class mockAPI(object): + def __init__(self, mock_dir=None, debug=False): + self.mock_dir = mock_dir + if not self.mock_dir: + # Try to guess the mock_dir if python setup.py develop was used + self.mock_dir = '/'.join(__file__.split('/')[:-3]) + '/test' + + self.logger = logging.getLogger('mockAPI') + + if debug: + self.logger.setLevel(logging.DEBUG) + + self.logger.info('mockAPI initialised, API requests will be mocked'.format(self.mock_dir)) + self.logger.debug('Test path resolved as {}'.format(self.mock_dir)) + + def get_directories(self, path): + dir, subdirs, files = next(os.walk(path)) + return subdirs + + def get_files(self, path): + dir, subdirs, files = next(os.walk(path)) + return files + + def qualys_vuln_callback(self, request, uri, response_headers): + self.logger.debug('Simulating response for {} ({})'.format(uri, request.body)) + if 'list' in request.parsed_body['action']: + return [ 200, + response_headers, + open('{}/{}'.format(self.qualys_vuln_path, 'scans')).read()] + elif 'fetch' in request.parsed_body['action']: + try: + response_body = open('{}/{}'.format( + self.qualys_vuln_path, + request.parsed_body['scan_ref'][0].replace('/', '_')) + ).read() + except: + # Can't find the file, just send an empty response + response_body = '' + return [200, response_headers, response_body] + + def create_nessus_resource(self, framework): + for filename in self.get_files('{}/{}'.format(self.mock_dir, framework)): + method, resource = filename.split('_',1) + resource = resource.replace('_', '/') + self.logger.debug('Adding mocked {} endpoint {} {}'.format(framework, method, resource)) + httpretty.register_uri( + getattr(httpretty, method), 'https://{}:443/{}'.format(framework, resource), + body=open('{}/{}/{}'.format(self.mock_dir, framework, filename)).read() + ) + + def create_qualys_vuln_resource(self, framework): + # Create health check endpoint + self.logger.debug('Adding mocked {} endpoint {} {}'.format(framework, 'GET', 'msp/about.php')) + httpretty.register_uri( + httpretty.GET, + 'https://{}:443/{}'.format(framework, 'msp/about.php'), + body='') + + self.logger.debug('Adding mocked {} endpoint {} {}'.format(framework, 'POST', 'api/2.0/fo/scan')) + httpretty.register_uri( + httpretty.POST, 'https://{}:443/{}'.format(framework, 'api/2.0/fo/scan/'), + body=self.qualys_vuln_callback) + + def mock_endpoints(self): + for framework in self.get_directories(self.mock_dir): + if framework in ['nessus', 'tenable']: + self.create_nessus_resource(framework) + elif framework == 'qualys_vuln': + self.qualys_vuln_path = self.mock_dir + '/' + framework + self.create_qualys_vuln_resource(framework) + httpretty.enable() \ No newline at end of file diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index 8a5af1a..87d3a58 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -478,8 +478,9 @@ def whisper_nessus(self): self.conn.close() self.logger.info('Scan aggregation complete! Connection to database closed.') else: - self.logger.error('Failed to use scanner at {host}:{port}'.format(host=self.hostname, port=self.nessus_port)) + return 1 + return 0 class vulnWhispererQualys(vulnWhispererBase): @@ -1244,6 +1245,7 @@ def __init__(self, self.verbose = verbose self.source = source self.scanname = scanname + self.exit_code = 0 def whisper_vulnerabilities(self): @@ -1254,15 +1256,15 @@ def whisper_vulnerabilities(self): password=self.password, verbose=self.verbose, profile=self.profile) - vw.whisper_nessus() + self.exit_code += vw.whisper_nessus() elif self.profile == 'qualys_web': vw = vulnWhispererQualys(config=self.config) - vw.process_web_assets() + self.exit_code += vw.process_web_assets() elif self.profile == 'openvas': vw_openvas = vulnWhispererOpenVAS(config=self.config) - vw_openvas.process_openvas_scans() + self.exit_code += vw_openvas.process_openvas_scans() elif self.profile == 'tenable': vw = vulnWhispererNessus(config=self.config, @@ -1270,11 +1272,11 @@ def whisper_vulnerabilities(self): password=self.password, verbose=self.verbose, profile=self.profile) - vw.whisper_nessus() + self.exit_code += vw.whisper_nessus() elif self.profile == 'qualys_vuln': vw = vulnWhispererQualysVuln(config=self.config) - vw.process_vuln_scans() + self.exit_code += vw.process_vuln_scans() elif self.profile == 'jira': #first we check config fields are created, otherwise we create them @@ -1288,3 +1290,5 @@ def whisper_vulnerabilities(self): return 0 else: vw.jira_sync(self.source, self.scanname) + + return self.exit_code From 1ef67d48bea5607aacc5aa7650bea50b0e52711f Mon Sep 17 00:00:00 2001 From: pemontto <939704+pemontto@users.noreply.github.com> Date: Fri, 5 Apr 2019 20:36:13 +1100 Subject: [PATCH 41/96] Feature error codes (#165) * Use error codes for failed scans * Fix indentations * Fix more indentation * Continue after failed download * Add tests for failed scans * Add more tests * move definition * Update nessus.py This function was used by function `print_scans` which at the same time was an unused one that had been deleted in the PR itself. --- .travis.yml | 15 +++++++ bin/vuln_whisperer | 49 ++++++++++---------- vulnwhisp/base/config.py | 26 +++++------ vulnwhisp/frameworks/nessus.py | 69 ++--------------------------- vulnwhisp/frameworks/qualys_vuln.py | 26 +++++------ vulnwhisp/test/mock.py | 15 ++++--- vulnwhisp/vulnwhisp.py | 50 ++++++++++++--------- 7 files changed, 104 insertions(+), 146 deletions(-) diff --git a/.travis.yml b/.travis.yml index f11e590..c806fb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,22 @@ before_script: - flake8 . --count --exit-zero --exclude=deps/qualysapi --max-complexity=10 --max-line-length=127 --statistics script: - python setup.py install + # Test successful scan download and parsing - vuln_whisperer -c configs/test.ini --mock --mock_dir test + - rm -rf /tmp/VulnWhisperer + # Test one failed scan + - rm -f test/nessus/GET_scans_exports_164_download + - vuln_whisperer -c configs/test.ini --mock --mock_dir test; [[ $? -eq 1 ]] + - rm -rf /tmp/VulnWhisperer + # Test two failed scans + - rm -f test/qualys_vuln/scan_1553941061.87241 + - vuln_whisperer -c configs/test.ini --mock --mock_dir test; [[ $? -eq 2 ]] + - rm -rf /tmp/VulnWhisperer + # Test only nessus + - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir test; [[ $? -eq 1 ]] + - rm -rf /tmp/VulnWhisperer + # Test only qualy_vuln + - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir test; [[ $? -eq 1 ]] notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down diff --git a/bin/vuln_whisperer b/bin/vuln_whisperer index 23d2000..010e9db 100644 --- a/bin/vuln_whisperer +++ b/bin/vuln_whisperer @@ -11,12 +11,14 @@ import argparse import sys import logging + def isFileValid(parser, arg): if not os.path.exists(arg): parser.error("The file %s does not exist!" % arg) else: return arg + def main(): parser = argparse.ArgumentParser(description=""" VulnWhisperer is designed to create actionable data from\ @@ -31,8 +33,10 @@ def main(): help='JIRA required only! Scan name from scan to report') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=True, help='Prints status out to screen (defaults to True)') - parser.add_argument('-u', '--username', dest='username', required=False, default=None, type=lambda x: x.strip(), help='The NESSUS username') - parser.add_argument('-p', '--password', dest='password', required=False, default=None, type=lambda x: x.strip(), help='The NESSUS password') + parser.add_argument('-u', '--username', dest='username', required=False, default=None, + help='The NESSUS username', type=lambda x: x.strip()) + parser.add_argument('-p', '--password', dest='password', required=False, default=None, + help='The NESSUS password', type=lambda x: x.strip()) parser.add_argument('-F', '--fancy', action='store_true', help='Enable colourful logging output') parser.add_argument('-d', '--debug', action='store_true', help='Enable debugging messages') parser.add_argument('--mock', action='store_true', help='Enable mocked API responses') @@ -62,30 +66,28 @@ def main(): mock_api = mockAPI(args.mock_dir, args.verbose) mock_api.mock_endpoints() + exit_code = 0 + try: if args.config and not args.section: # this remains a print since we are in the main binary print('WARNING: {warning}'.format(warning='No section was specified, vulnwhisperer will scrape enabled modules from config file. \ - \nPlease specify a section using -s. \ + \nPlease specify a section using -s. \ \nExample vuln_whisperer -c config.ini -s nessus')) logger.info('No section was specified, vulnwhisperer will scrape enabled modules from the config file.') - config = vwConfig(config_in=args.config) - enabled_sections = config.get_sections_with_attribute('enabled') - - for section in enabled_sections: - vw = vulnWhisperer(config=args.config, - profile=section, - verbose=args.verbose, - username=args.username, - password=args.password, - source=args.source, - scanname=args.scanname) - - exit_code = vw.whisper_vulnerabilities() - # TODO: fix this to NOT be exit 1 unless in error - close_logging_handlers(logger) - sys.exit(exit_code) - + + config = vwConfig(config_in=args.config) + enabled_sections = config.get_sections_with_attribute('enabled') + + for section in enabled_sections: + vw = vulnWhisperer(config=args.config, + profile=section, + verbose=args.verbose, + username=args.username, + password=args.password, + source=args.source, + scanname=args.scanname) + exit_code += vw.whisper_vulnerabilities() else: logger.info('Running vulnwhisperer for section {}'.format(args.section)) vw = vulnWhisperer(config=args.config, @@ -95,11 +97,10 @@ def main(): password=args.password, source=args.source, scanname=args.scanname) + exit_code += vw.whisper_vulnerabilities() - exit_code = vw.whisper_vulnerabilities() - # TODO: fix this to NOT be exit 1 unless in error - close_logging_handlers(logger) - sys.exit(exit_code) + close_logging_handlers(logger) + sys.exit(exit_code) except Exception as e: if args.verbose: diff --git a/vulnwhisp/base/config.py b/vulnwhisp/base/config.py index 7786883..e8490d6 100644 --- a/vulnwhisp/base/config.py +++ b/vulnwhisp/base/config.py @@ -1,9 +1,8 @@ -import os import sys import logging # Support for python3 -if (sys.version_info > (3, 0)): +if sys.version_info > (3, 0): import configparser as cp else: import ConfigParser as cp @@ -45,7 +44,6 @@ def exists_jira_profiles(self, profiles): return False return True - def update_jira_profiles(self, profiles): # create JIRA profiles in the ini config file self.logger.debug('Updating Jira profiles: {}'.format(str(profiles))) @@ -59,27 +57,27 @@ def update_jira_profiles(self, profiles): except: self.logger.warn("Creating config section for '{}'".format(section_name)) self.config.add_section(section_name) - self.config.set(section_name,'source',profile.split('.')[0]) + self.config.set(section_name, 'source', profile.split('.')[0]) # in case any scan name contains '.' character - self.config.set(section_name,'scan_name','.'.join(profile.split('.')[1:])) - self.config.set(section_name,'jira_project', '') - self.config.set(section_name,'; if multiple components, separate by ","') - self.config.set(section_name,'components', '') - self.config.set(section_name,'; minimum criticality to report (low, medium, high or critical)') - self.config.set(section_name,'min_critical_to_report', 'high') - self.config.set(section_name,'; automatically report, boolean value ') - self.config.set(section_name,'autoreport', 'false') + self.config.set(section_name, 'scan_name', '.'.join(profile.split('.')[1:])) + self.config.set(section_name, 'jira_project', '') + self.config.set(section_name, '; if multiple components, separate by ","') + self.config.set(section_name, 'components', '') + self.config.set(section_name, '; minimum criticality to report (low, medium, high or critical)') + self.config.set(section_name, 'min_critical_to_report', 'high') + self.config.set(section_name, '; automatically report, boolean value ') + self.config.set(section_name, 'autoreport', 'false') # TODO: try/catch this # writing changes back to file with open(self.config_in, 'w') as configfile: self.config.write(configfile) self.logger.debug('Written configuration to {}'.format(self.config_in)) - + # FIXME: this is the same as return None, that is the default return for return-less functions return def normalize_section(self, profile): - profile = "jira.{}".format(profile.lower().replace(" ","_")) + profile = "jira.{}".format(profile.lower().replace(" ", "_")) self.logger.debug('Normalized profile as: {}'.format(profile)) return profile diff --git a/vulnwhisp/frameworks/nessus.py b/vulnwhisp/frameworks/nessus.py index 5423caf..7944963 100755 --- a/vulnwhisp/frameworks/nessus.py +++ b/vulnwhisp/frameworks/nessus.py @@ -1,19 +1,14 @@ -import requests -from requests.packages.urllib3.exceptions import InsecureRequestWarning -requests.packages.urllib3.disable_warnings(InsecureRequestWarning) - -import pytz from datetime import datetime -import json import sys import time +import json import logging - +import pytz +import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) - class NessusAPI(object): SESSION = '/session' FOLDERS = '/folders' @@ -58,7 +53,7 @@ def __init__(self, hostname=None, port=None, username=None, password=None, verbo def login(self): resp = self.get_token() - if resp.status_code is 200: + if resp.status_code == 200: self.headers['X-Cookie'] = 'token={token}'.format(token=resp.json()['token']) else: raise Exception('[FAIL] Could not login to Nessus') @@ -101,14 +96,6 @@ def get_token(self): token = self.request(self.SESSION, data=auth, json=False) return token - def logout(self): - self.logger.debug('Logging out') - self.request(self.SESSION, method='DELETE') - - def get_folders(self): - folders = self.request(self.FOLDERS, method='GET', json=True) - return folders - def get_scans(self): scans = self.request(self.SCANS, method='GET', json=True) return scans @@ -119,47 +106,10 @@ def get_scan_ids(self): self.logger.debug('Found {} scan_ids'.format(len(scan_ids))) return scan_ids - def count_scan(self, scans, folder_id): - count = 0 - for scan in scans: - if scan['folder_id'] == folder_id: count = count + 1 - return count - - def print_scans(self, data): - for folder in data['folders']: - self.logger.info("\\{0} - ({1})\\".format(folder['name'], self.count_scan(data['scans'], folder['id']))) - for scan in data['scans']: - if scan['folder_id'] == folder['id']: - self.logger.info("\t\"{0}\" - sid:{1} - uuid: {2}".format(scan['name'].encode('utf-8'), scan['id'], scan['uuid'])) - - def get_scan_details(self, scan_id): - data = self.request(self.SCAN_ID.format(scan_id=scan_id), method='GET', json=True) - return data - def get_scan_history(self, scan_id): data = self.request(self.SCAN_ID.format(scan_id=scan_id), method='GET', json=True) return data['history'] - def get_scan_hosts(self, scan_id): - data = self.request(self.SCAN_ID.format(scan_id=scan_id), method='GET', json=True) - return data['hosts'] - - def get_host_vulnerabilities(self, scan_id, host_id): - query = self.HOST_VULN.format(scan_id=scan_id, host_id=host_id) - data = self.request(query, method='GET', json=True) - return data - - def get_plugin_info(self, scan_id, host_id, plugin_id): - query = self.PLUGINS.format(scan_id=scan_id, host_id=host_id, plugin_id=plugin_id) - data = self.request(query, method='GET', json=True) - return data - - def export_scan(self, scan_id, history_id): - data = {'format': 'csv'} - query = self.EXPORT_REPORT.format(scan_id=scan_id, history_id=history_id) - req = self.request(query, data=data, method='POST') - return req - def download_scan(self, scan_id=None, history=None, export_format="", chapters="", dbpasswd="", profile=""): running = True counter = 0 @@ -195,17 +145,6 @@ def download_scan(self, scan_id=None, history=None, export_format="", chapters=" content = self.request(self.EXPORT_TOKEN_DOWNLOAD.format(token_id=token_id), method='GET', download=True) return content - @staticmethod - def merge_dicts(self, *dict_args): - """ - Given any number of dicts, shallow copy and merge into a new dict, - precedence goes to key value pairs in latter dicts. - """ - result = {} - for dictionary in dict_args: - result.update(dictionary) - return result - def get_utc_from_local(self, date_time, local_tz=None, epoch=True): date_time = datetime.fromtimestamp(date_time) if local_tz is None: diff --git a/vulnwhisp/frameworks/qualys_vuln.py b/vulnwhisp/frameworks/qualys_vuln.py index 08df551..c60ff26 100644 --- a/vulnwhisp/frameworks/qualys_vuln.py +++ b/vulnwhisp/frameworks/qualys_vuln.py @@ -3,12 +3,9 @@ __author__ = 'Nathan Young' import xml.etree.ElementTree as ET -import pandas as pd -import qualysapi -import requests -import sys import logging -import os +import qualysapi +import pandas as pd import dateutil.parser as dp @@ -61,12 +58,13 @@ def get_scan_details(self, scan_id=None): 'scan_ref': scan_id } scan_json = self.qgc.request(self.SCANS, parameters) - + # First two columns are metadata we already have # Last column corresponds to "target_distribution_across_scanner_appliances" element # which doesn't follow the schema and breaks the pandas data manipulation return pd.read_json(scan_json).iloc[2:-1] + class qualysUtils: def __init__(self): self.logger = logging.getLogger('qualysUtils') @@ -78,15 +76,15 @@ def iso_to_epoch(self, dt): class qualysVulnScan: - + def __init__( - self, - config=None, - file_in=None, - file_stream=False, - delimiter=',', - quotechar='"', - ): + self, + config=None, + file_in=None, + file_stream=False, + delimiter=',', + quotechar='"', + ): self.logger = logging.getLogger('qualysVulnScan') self.file_in = file_in self.file_stream = file_stream diff --git a/vulnwhisp/test/mock.py b/vulnwhisp/test/mock.py index 6d05e65..fcc7e1c 100644 --- a/vulnwhisp/test/mock.py +++ b/vulnwhisp/test/mock.py @@ -2,19 +2,20 @@ import logging import httpretty + class mockAPI(object): def __init__(self, mock_dir=None, debug=False): self.mock_dir = mock_dir if not self.mock_dir: # Try to guess the mock_dir if python setup.py develop was used self.mock_dir = '/'.join(__file__.split('/')[:-3]) + '/test' - + self.logger = logging.getLogger('mockAPI') if debug: self.logger.setLevel(logging.DEBUG) - self.logger.info('mockAPI initialised, API requests will be mocked'.format(self.mock_dir)) + self.logger.info('mockAPI initialised, API requests will be mocked') self.logger.debug('Test path resolved as {}'.format(self.mock_dir)) def get_directories(self, path): @@ -28,23 +29,23 @@ def get_files(self, path): def qualys_vuln_callback(self, request, uri, response_headers): self.logger.debug('Simulating response for {} ({})'.format(uri, request.body)) if 'list' in request.parsed_body['action']: - return [ 200, + return [200, response_headers, open('{}/{}'.format(self.qualys_vuln_path, 'scans')).read()] elif 'fetch' in request.parsed_body['action']: try: response_body = open('{}/{}'.format( - self.qualys_vuln_path, - request.parsed_body['scan_ref'][0].replace('/', '_')) + self.qualys_vuln_path, + request.parsed_body['scan_ref'][0].replace('/', '_')) ).read() except: # Can't find the file, just send an empty response response_body = '' - return [200, response_headers, response_body] + return [200, response_headers, response_body] def create_nessus_resource(self, framework): for filename in self.get_files('{}/{}'.format(self.mock_dir, framework)): - method, resource = filename.split('_',1) + method, resource = filename.split('_', 1) resource = resource.replace('_', '/') self.logger.debug('Adding mocked {} endpoint {} {}'.format(framework, method, resource)) httpretty.register_uri( diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index 87d3a58..e01d2da 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -43,11 +43,11 @@ def __init__( if self.CONFIG_SECTION is None: raise Exception('Implementing class must define CONFIG_SECTION') + self.exit_code = 0 self.db_name = db_name self.purge = purge self.develop = develop - if config is not None: self.config = vwConfig(config_in=config) try: @@ -361,7 +361,7 @@ def whisper_nessus(self): if not scan_list: self.logger.warn('No new scans to process. Exiting...') - return 0 + return self.exit_code # Create scan subfolders @@ -432,9 +432,15 @@ def whisper_nessus(self): self.record_insert(record_meta) self.logger.info('File {filename} already exist! Updating database'.format(filename=relative_path_name)) else: - file_req = \ - self.nessus.download_scan(scan_id=scan_id, history=history_id, - export_format='csv', profile=self.CONFIG_SECTION) + try: + file_req = \ + self.nessus.download_scan(scan_id=scan_id, history=history_id, + export_format='csv', profile=self.CONFIG_SECTION) + except Exception as e: + self.logger.error('Could not download {} scan {}: {}'.format(self.CONFIG_SECTION, scan_id, str(e))) + self.exit_code += 1 + continue + clean_csv = \ pd.read_csv(io.StringIO(file_req.decode('utf-8'))) if len(clean_csv) > 2: @@ -479,8 +485,8 @@ def whisper_nessus(self): self.logger.info('Scan aggregation complete! Connection to database closed.') else: self.logger.error('Failed to use scanner at {host}:{port}'.format(host=self.hostname, port=self.nessus_port)) - return 1 - return 0 + self.exit_code += 1 + return self.exit_code class vulnWhispererQualys(vulnWhispererBase): @@ -550,7 +556,6 @@ def __init__( if debug: self.logger.setLevel(logging.DEBUG) - self.qualys_scan = qualysScanReport(config=config) self.latest_scans = self.qualys_scan.qw.get_all_scans() self.directory_check() @@ -672,7 +677,7 @@ def process_web_assets(self): else: self.logger.info('No new scans to process. Exiting...') self.conn.close() - return 0 + return self.exit_code class vulnWhispererOpenVAS(vulnWhispererBase): @@ -718,7 +723,6 @@ def __init__( if debug: self.logger.setLevel(logging.DEBUG) - self.directory_check() self.port = int(self.config.get(self.CONFIG_SECTION, 'port')) self.develop = True @@ -809,7 +813,7 @@ def process_openvas_scans(self): else: self.logger.info('No new scans to process. Exiting...') self.conn.close() - return 0 + return self.exit_code class vulnWhispererQualysVuln(vulnWhispererBase): @@ -850,7 +854,6 @@ def whisper_reports(self, scan_reference=None, output_format='json', cleanup=True): - try: launched_date if 'Z' in launched_date: launched_date = self.qualys_scan.utils.iso_to_epoch(launched_date) @@ -879,11 +882,16 @@ def whisper_reports(self, self.logger.info('File {filename} already exist! Updating database'.format(filename=relative_path_name)) else: - self.logger.info('Processing report ID: {}'.format(report_id)) - vuln_ready = self.qualys_scan.process_data(scan_id=report_id) - vuln_ready['scan_name'] = scan_name - vuln_ready['scan_reference'] = report_id - vuln_ready.rename(columns=self.COLUMN_MAPPING, inplace=True) + try: + self.logger.info('Processing report ID: {}'.format(report_id)) + vuln_ready = self.qualys_scan.process_data(scan_id=report_id) + vuln_ready['scan_name'] = scan_name + vuln_ready['scan_reference'] = report_id + vuln_ready.rename(columns=self.COLUMN_MAPPING, inplace=True) + except Exception as e: + self.logger.error('Could not process {}: {}'.format(report_id, str(e))) + self.exit_code += 1 + return self.exit_code record_meta = ( scan_name, @@ -905,9 +913,7 @@ def whisper_reports(self, f.write('\n') self.logger.info('Report written to {}'.format(report_name)) - - except Exception as e: - self.logger.error('Could not process {}: {}'.format(report_id, str(e))) + return self.exit_code def identify_scans_to_process(self): @@ -929,14 +935,14 @@ def process_vuln_scans(self): counter += 1 r = app[1] self.logger.info('Processing {}/{}'.format(counter, len(self.scans_to_process))) - self.whisper_reports(report_id=r['id'], + self.exit_code += self.whisper_reports(report_id=r['id'], launched_date=r['date'], scan_name=r['name'], scan_reference=r['type']) else: self.logger.info('No new scans to process. Exiting...') self.conn.close() - return 0 + return self.exit_code class vulnWhispererJIRA(vulnWhispererBase): From 0a877ce2670c9a2f6c84298de9514475dacbb649 Mon Sep 17 00:00:00 2001 From: Quim Date: Fri, 5 Apr 2019 23:37:04 +0200 Subject: [PATCH 42/96] fix nessus download 'imported' scans --- vulnwhisp/vulnwhisp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index 87d3a58..ded70b3 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -407,7 +407,7 @@ def whisper_nessus(self): folder_name = '' else: folder_name = next(f['name'] for f in folders if f['id'] == folder_id) - if status == 'completed': + if status in ['completed', 'imported']: file_name = '%s_%s_%s_%s.%s' % (scan_name, scan_id, history_id, norm_time, 'csv') repls = (('\\', '_'), ('/', '_'), ('/', '_'), (' ', '_')) From 5edde8760a0a4829cc1bc24062a34e911b95b704 Mon Sep 17 00:00:00 2001 From: pemontto Date: Sat, 6 Apr 2019 11:02:42 +1100 Subject: [PATCH 43/96] Fix missing sys import --- vulnwhisp/frameworks/qualys_vuln.py | 1 + 1 file changed, 1 insertion(+) diff --git a/vulnwhisp/frameworks/qualys_vuln.py b/vulnwhisp/frameworks/qualys_vuln.py index 34367ff..0ba409d 100644 --- a/vulnwhisp/frameworks/qualys_vuln.py +++ b/vulnwhisp/frameworks/qualys_vuln.py @@ -3,6 +3,7 @@ __author__ = 'Nathan Young' import xml.etree.ElementTree as ET +import sys import logging import qualysapi import pandas as pd From 33f2a5a3d151f1e234dcec04c241603d84a3f261 Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 12:24:22 +1000 Subject: [PATCH 44/96] Use a session and don't overwrite imports --- vulnwhisp/frameworks/nessus.py | 47 +++++++++++++++------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/vulnwhisp/frameworks/nessus.py b/vulnwhisp/frameworks/nessus.py index 7944963..7f5450f 100755 --- a/vulnwhisp/frameworks/nessus.py +++ b/vulnwhisp/frameworks/nessus.py @@ -34,7 +34,10 @@ def __init__(self, hostname=None, port=None, username=None, password=None, verbo self.base = 'https://{hostname}:{port}'.format(hostname=hostname, port=port) self.verbose = verbose - self.headers = { + self.session = requests.Session() + self.session.verify = False + self.session.stream = True + self.session.headers = { 'Origin': self.base, 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'en-US,en;q=0.8', @@ -52,27 +55,24 @@ def __init__(self, hostname=None, port=None, username=None, password=None, verbo self.scan_ids = self.get_scan_ids() def login(self): - resp = self.get_token() + auth = '{"username":"%s", "password":"%s"}' % (self.user, self.password) + resp = self.request(self.SESSION, data=auth, json_output=False) if resp.status_code == 200: - self.headers['X-Cookie'] = 'token={token}'.format(token=resp.json()['token']) + self.session.headers['X-Cookie'] = 'token={token}'.format(token=resp.json()['token']) else: raise Exception('[FAIL] Could not login to Nessus') - def request(self, url, data=None, headers=None, method='POST', download=False, json=False): - if headers is None: - headers = self.headers + def request(self, url, data=None, headers=None, method='POST', download=False, json_output=False): timeout = 0 success = False - + + method = method.lower() url = self.base + url self.logger.debug('Requesting to url {}'.format(url)) - methods = {'GET': requests.get, - 'POST': requests.post, - 'DELETE': requests.delete} while (timeout <= 10) and (not success): - data = methods[method](url, data=data, headers=self.headers, verify=False) - if data.status_code == 401: + response = getattr(self.session, method)(url, data=data) + if response.status_code == 401: if url == self.base + self.SESSION: break try: @@ -84,20 +84,15 @@ def request(self, url, data=None, headers=None, method='POST', download=False, j else: success = True - if json: - data = data.json() + if json_output: + return response.json() if download: self.logger.debug('Returning data.content') - return data.content - return data - - def get_token(self): - auth = '{"username":"%s", "password":"%s"}' % (self.user, self.password) - token = self.request(self.SESSION, data=auth, json=False) - return token + return response.content + return response def get_scans(self): - scans = self.request(self.SCANS, method='GET', json=True) + scans = self.request(self.SCANS, method='GET', json_output=True) return scans def get_scan_ids(self): @@ -107,10 +102,10 @@ def get_scan_ids(self): return scan_ids def get_scan_history(self, scan_id): - data = self.request(self.SCAN_ID.format(scan_id=scan_id), method='GET', json=True) + data = self.request(self.SCAN_ID.format(scan_id=scan_id), method='GET', json_output=True) return data['history'] - def download_scan(self, scan_id=None, history=None, export_format="", chapters="", dbpasswd="", profile=""): + def download_scan(self, scan_id=None, history=None, export_format="", profile=""): running = True counter = 0 @@ -120,7 +115,7 @@ def download_scan(self, scan_id=None, history=None, export_format="", chapters=" else: query = self.EXPORT_HISTORY.format(scan_id=scan_id, history_id=history) scan_id = str(scan_id) - req = self.request(query, data=json.dumps(data), method='POST', json=True) + req = self.request(query, data=json.dumps(data), method='POST', json_output=True) try: file_id = req['file'] token_id = req['token'] if 'token' in req else req['temp_token'] @@ -131,7 +126,7 @@ def download_scan(self, scan_id=None, history=None, export_format="", chapters=" time.sleep(2) counter += 2 report_status = self.request(self.EXPORT_STATUS.format(scan_id=scan_id, file_id=file_id), method='GET', - json=True) + json_output=True) running = report_status['status'] != 'ready' sys.stdout.write(".") sys.stdout.flush() From ec5d6cd38890718ac28d7fa5f1511cc84ac8aa1b Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 12:25:50 +1000 Subject: [PATCH 45/96] Iterate through nessus download data --- vulnwhisp/frameworks/nessus.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/vulnwhisp/frameworks/nessus.py b/vulnwhisp/frameworks/nessus.py index 7f5450f..7775a04 100755 --- a/vulnwhisp/frameworks/nessus.py +++ b/vulnwhisp/frameworks/nessus.py @@ -88,7 +88,14 @@ def request(self, url, data=None, headers=None, method='POST', download=False, j return response.json() if download: self.logger.debug('Returning data.content') - return response.content + response_data = '' + count = 0 + for chunk in response.iter_content(chunk_size=8192): + count += 1 + if chunk: + response_data += chunk + self.logger.debug('Processed {} chunks'.format(count)) + return response_data return response def get_scans(self): From 973c69dffb957f868c25ee8ff5363856ca550549 Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 17:43:15 +1000 Subject: [PATCH 46/96] Updates tests --- .gitmodules | 4 ++-- bin/vuln_whisperer | 11 +++++++---- test | 1 - vulnwhisp/test/mock.py | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) delete mode 160000 test diff --git a/.gitmodules b/.gitmodules index 4d6eb1b..546e654 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "test"] - path = test +[submodule "tests/data"] + path = tests/data url = https://github.com/HASecuritySolutions/VulnWhisperer-tests diff --git a/bin/vuln_whisperer b/bin/vuln_whisperer index 010e9db..09ed142 100644 --- a/bin/vuln_whisperer +++ b/bin/vuln_whisperer @@ -37,10 +37,13 @@ def main(): help='The NESSUS username', type=lambda x: x.strip()) parser.add_argument('-p', '--password', dest='password', required=False, default=None, help='The NESSUS password', type=lambda x: x.strip()) - parser.add_argument('-F', '--fancy', action='store_true', help='Enable colourful logging output') - parser.add_argument('-d', '--debug', action='store_true', help='Enable debugging messages') - parser.add_argument('--mock', action='store_true', help='Enable mocked API responses') - parser.add_argument('--mock_dir', dest='mock_dir', required=False, default='test', + parser.add_argument('-F', '--fancy', action='store_true', + help='Enable colourful logging output') + parser.add_argument('-d', '--debug', action='store_true', + help='Enable debugging messages') + parser.add_argument('--mock', action='store_true', + help='Enable mocked API responses') + parser.add_argument('--mock_dir', dest='mock_dir', required=False, default=None, help='Path of test directory') args = parser.parse_args() diff --git a/test b/test deleted file mode 160000 index 606b8bc..0000000 --- a/test +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 606b8bcbe32b057376ea04b9b2e6a0b0e4454006 diff --git a/vulnwhisp/test/mock.py b/vulnwhisp/test/mock.py index fcc7e1c..5d48729 100644 --- a/vulnwhisp/test/mock.py +++ b/vulnwhisp/test/mock.py @@ -6,12 +6,12 @@ class mockAPI(object): def __init__(self, mock_dir=None, debug=False): self.mock_dir = mock_dir + if not self.mock_dir: # Try to guess the mock_dir if python setup.py develop was used - self.mock_dir = '/'.join(__file__.split('/')[:-3]) + '/test' + self.mock_dir = '/'.join(__file__.split('/')[:-3]) + '/tests/data' self.logger = logging.getLogger('mockAPI') - if debug: self.logger.setLevel(logging.DEBUG) From 873066a4190a5af52986e57a0f7395837a9b33b1 Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 17:43:50 +1000 Subject: [PATCH 47/96] reorder imports --- vulnwhisp/frameworks/nessus.py | 8 +++++--- vulnwhisp/frameworks/qualys_vuln.py | 9 +++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/vulnwhisp/frameworks/nessus.py b/vulnwhisp/frameworks/nessus.py index 7775a04..3dfdcd5 100755 --- a/vulnwhisp/frameworks/nessus.py +++ b/vulnwhisp/frameworks/nessus.py @@ -1,11 +1,13 @@ -from datetime import datetime -import sys -import time import json import logging +import sys +import time +from datetime import datetime + import pytz import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning + requests.packages.urllib3.disable_warnings(InsecureRequestWarning) diff --git a/vulnwhisp/frameworks/qualys_vuln.py b/vulnwhisp/frameworks/qualys_vuln.py index 0ba409d..2413400 100644 --- a/vulnwhisp/frameworks/qualys_vuln.py +++ b/vulnwhisp/frameworks/qualys_vuln.py @@ -2,12 +2,13 @@ # -*- coding: utf-8 -*- __author__ = 'Nathan Young' -import xml.etree.ElementTree as ET -import sys import logging -import qualysapi -import pandas as pd +import sys +import xml.etree.ElementTree as ET + import dateutil.parser as dp +import pandas as pd +import qualysapi class qualysWhisperAPI(object): From a12e9f70a1a87de5cdd7ddfdfc0cedf4be6a5416 Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 18:38:03 +1000 Subject: [PATCH 48/96] Remove redundant param --- vulnwhisp/frameworks/nessus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vulnwhisp/frameworks/nessus.py b/vulnwhisp/frameworks/nessus.py index 3dfdcd5..783f2f5 100755 --- a/vulnwhisp/frameworks/nessus.py +++ b/vulnwhisp/frameworks/nessus.py @@ -143,8 +143,8 @@ def download_scan(self, scan_id=None, history=None, export_format="", profile="" if counter % 60 == 0: self.logger.info("Completed: {}".format(counter)) self.logger.info("Done: {}".format(counter)) - if profile=='tenable': - content = self.request(self.EXPORT_FILE_DOWNLOAD.format(scan_id=scan_id, file_id=file_id), method='GET', download=True) + if profile == 'tenable': + content = self.request(self.EXPORT_FILE_DOWNLOAD.format(file_id=file_id), method='GET', download=True) else: content = self.request(self.EXPORT_TOKEN_DOWNLOAD.format(token_id=token_id), method='GET', download=True) return content From 1ef7289b8d558988b6b32d1e58b947ada21f386e Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 18:44:30 +1000 Subject: [PATCH 49/96] reduntant replace, formatting --- vulnwhisp/frameworks/qualys_vuln.py | 19 +++++++++++-------- vulnwhisp/vulnwhisp.py | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/vulnwhisp/frameworks/qualys_vuln.py b/vulnwhisp/frameworks/qualys_vuln.py index 2413400..69cddfa 100644 --- a/vulnwhisp/frameworks/qualys_vuln.py +++ b/vulnwhisp/frameworks/qualys_vuln.py @@ -79,13 +79,13 @@ def iso_to_epoch(self, dt): class qualysVulnScan: def __init__( - self, - config=None, - file_in=None, - file_stream=False, - delimiter=',', - quotechar='"', - ): + self, + config=None, + file_in=None, + file_stream=False, + delimiter=',', + quotechar='"', + ): self.logger = logging.getLogger('qualysVulnScan') self.file_in = file_in self.file_stream = file_stream @@ -110,7 +110,10 @@ def process_data(self, scan_id=None): self.logger.info('Downloading scan ID: {}'.format(scan_id)) scan_report = self.qw.get_scan_details(scan_id=scan_id) if not scan_report.empty: - keep_columns = ['category', 'cve_id', 'cvss3_base', 'cvss3_temporal', 'cvss_base', 'cvss_temporal', 'dns', 'exploitability', 'fqdn', 'impact', 'ip', 'ip_status', 'netbios', 'os', 'pci_vuln', 'port', 'protocol', 'qid', 'results', 'severity', 'solution', 'ssl', 'threat', 'title', 'type', 'vendor_reference'] + keep_columns = ['category', 'cve_id', 'cvss3_base', 'cvss3_temporal', 'cvss_base', + 'cvss_temporal', 'dns', 'exploitability', 'fqdn', 'impact', 'ip', 'ip_status', + 'netbios', 'os', 'pci_vuln', 'port', 'protocol', 'qid', 'results', 'severity', + 'solution', 'ssl', 'threat', 'title', 'type', 'vendor_reference'] scan_report = scan_report.filter(keep_columns) scan_report['severity'] = scan_report['severity'].astype(int).astype(str) scan_report['qid'] = scan_report['qid'].astype(int).astype(str) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index 21351da..782af0e 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -410,7 +410,7 @@ def whisper_nessus(self): if status in ['completed', 'imported']: file_name = '%s_%s_%s_%s.%s' % (scan_name, scan_id, history_id, norm_time, 'csv') - repls = (('\\', '_'), ('/', '_'), ('/', '_'), (' ', '_')) + repls = (('\\', '_'), ('/', '_'), (' ', '_')) file_name = reduce(lambda a, kv: a.replace(*kv), repls, file_name) relative_path_name = self.path_check(folder_name + '/' + file_name) From 8086e7cf9f93a66a25bf446f4e9112e8d89d7a3a Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 18:46:30 +1000 Subject: [PATCH 50/96] Fix tests directory --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c806fb0..00abf64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,21 +20,21 @@ before_script: script: - python setup.py install # Test successful scan download and parsing - - vuln_whisperer -c configs/test.ini --mock --mock_dir test + - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data - rm -rf /tmp/VulnWhisperer # Test one failed scan - rm -f test/nessus/GET_scans_exports_164_download - - vuln_whisperer -c configs/test.ini --mock --mock_dir test; [[ $? -eq 1 ]] + - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 1 ]] - rm -rf /tmp/VulnWhisperer # Test two failed scans - rm -f test/qualys_vuln/scan_1553941061.87241 - - vuln_whisperer -c configs/test.ini --mock --mock_dir test; [[ $? -eq 2 ]] + - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 2 ]] - rm -rf /tmp/VulnWhisperer # Test only nessus - - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir test; [[ $? -eq 1 ]] + - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir tests/data; [[ $? -eq 1 ]] - rm -rf /tmp/VulnWhisperer # Test only qualy_vuln - - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir test; [[ $? -eq 1 ]] + - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir tests/data; [[ $? -eq 1 ]] notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down From 7b1ebb51fa50c146428969e8c9ac1897531eed56 Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 19:02:02 +1000 Subject: [PATCH 51/96] Updates tests --- .gitignore | 1 + .travis.yml | 10 +++++----- tests/data | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) create mode 160000 tests/data diff --git a/.gitignore b/.gitignore index b4a878c..9fc0cb6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ logs/ elk6/vulnwhisperer.ini resources/elk6/vulnwhisperer.ini configs/frameworks_example.ini +tests/data # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.travis.yml b/.travis.yml index 00abf64..2603323 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,21 +20,21 @@ before_script: script: - python setup.py install # Test successful scan download and parsing - - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data + - vuln_whisperer -c configs/test.ini --mock - rm -rf /tmp/VulnWhisperer # Test one failed scan - rm -f test/nessus/GET_scans_exports_164_download - - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - vuln_whisperer -c configs/test.ini --mock; [[ $? -eq 1 ]] - rm -rf /tmp/VulnWhisperer # Test two failed scans - rm -f test/qualys_vuln/scan_1553941061.87241 - - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 2 ]] + - vuln_whisperer -c configs/test.ini --mock; [[ $? -eq 2 ]] - rm -rf /tmp/VulnWhisperer # Test only nessus - - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - vuln_whisperer -c configs/test.ini -s nessus --mock; [[ $? -eq 1 ]] - rm -rf /tmp/VulnWhisperer # Test only qualy_vuln - - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock; [[ $? -eq 1 ]] notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down diff --git a/tests/data b/tests/data new file mode 160000 index 0000000..606b8bc --- /dev/null +++ b/tests/data @@ -0,0 +1 @@ +Subproject commit 606b8bcbe32b057376ea04b9b2e6a0b0e4454006 From 3d2c939cfbad51182e58c44b3c1dedc8df77234b Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 19:13:45 +1000 Subject: [PATCH 52/96] Update .travis.yml --- .travis.yml | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2603323..54f3d9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,21 +20,27 @@ before_script: script: - python setup.py install # Test successful scan download and parsing - - vuln_whisperer -c configs/test.ini --mock - - rm -rf /tmp/VulnWhisperer + - | + rm -rf /tmp/VulnWhisperer + vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data # Test one failed scan - - rm -f test/nessus/GET_scans_exports_164_download - - vuln_whisperer -c configs/test.ini --mock; [[ $? -eq 1 ]] - - rm -rf /tmp/VulnWhisperer + - | + rm -rf /tmp/VulnWhisperer + rm -f test/nessus/GET_scans_exports_164_download + vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 1 ]] # Test two failed scans - - rm -f test/qualys_vuln/scan_1553941061.87241 - - vuln_whisperer -c configs/test.ini --mock; [[ $? -eq 2 ]] - - rm -rf /tmp/VulnWhisperer + - | + rm -rf /tmp/VulnWhisperer + rm -f test/qualys_vuln/scan_1553941061.87241 + vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 2 ]] # Test only nessus - - vuln_whisperer -c configs/test.ini -s nessus --mock; [[ $? -eq 1 ]] - - rm -rf /tmp/VulnWhisperer + - | + rm -rf /tmp/VulnWhisperer + vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir tests/data; [[ $? -eq 1 ]] # Test only qualy_vuln - - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock; [[ $? -eq 1 ]] + - | + rm -rf /tmp/VulnWhisperer + vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir tests/data; [[ $? -eq 1 ]] notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down From e1f2c00b9e3a826c408a5a1c890233b36b8045ca Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 19:17:47 +1000 Subject: [PATCH 53/96] fix tests --- .travis.yml | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 54f3d9c..687ecae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,27 +20,22 @@ before_script: script: - python setup.py install # Test successful scan download and parsing - - | - rm -rf /tmp/VulnWhisperer - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data + - rm -rf /tmp/VulnWhisperer + - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data # Test one failed scan - - | - rm -rf /tmp/VulnWhisperer - rm -f test/nessus/GET_scans_exports_164_download - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - rm -rf /tmp/VulnWhisperer + - rm -f test/nessus/GET_scans_exports_164_download + - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 1 ]] # Test two failed scans - - | - rm -rf /tmp/VulnWhisperer - rm -f test/qualys_vuln/scan_1553941061.87241 - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 2 ]] + - rm -rf /tmp/VulnWhisperer + - rm -f test/qualys_vuln/scan_1553941061.87241 + - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 2 ]] # Test only nessus - - | - rm -rf /tmp/VulnWhisperer - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - rm -rf /tmp/VulnWhisperer + - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir tests/data; [[ $? -eq 1 ]] # Test only qualy_vuln - - | - rm -rf /tmp/VulnWhisperer - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - rm -rf /tmp/VulnWhisperer + - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir tests/data; [[ $? -eq 1 ]] notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down From c8d906c05fa752a5c2df1f596de91c2a859bc89b Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 19:30:48 +1000 Subject: [PATCH 54/96] Fix tenable downloads --- vulnwhisp/frameworks/nessus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulnwhisp/frameworks/nessus.py b/vulnwhisp/frameworks/nessus.py index 783f2f5..23c67d6 100755 --- a/vulnwhisp/frameworks/nessus.py +++ b/vulnwhisp/frameworks/nessus.py @@ -144,7 +144,7 @@ def download_scan(self, scan_id=None, history=None, export_format="", profile="" self.logger.info("Completed: {}".format(counter)) self.logger.info("Done: {}".format(counter)) if profile == 'tenable': - content = self.request(self.EXPORT_FILE_DOWNLOAD.format(file_id=file_id), method='GET', download=True) + content = self.request(self.EXPORT_FILE_DOWNLOAD.format(scan_id=scan_id, file_id=file_id), method='GET', download=True) else: content = self.request(self.EXPORT_TOKEN_DOWNLOAD.format(token_id=token_id), method='GET', download=True) return content From 302037893dee9e0d0188d12e8f4d8e6e69031243 Mon Sep 17 00:00:00 2001 From: pemontto Date: Mon, 8 Apr 2019 19:41:48 +1000 Subject: [PATCH 55/96] Add test path to env vars --- .travis.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 687ecae..c412177 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,8 @@ language: python cache: pip python: - 2.7 - +env: + - TEST_PATH=tests/data # - 3.6 #matrix: # allow_failures: @@ -21,21 +22,21 @@ script: - python setup.py install # Test successful scan download and parsing - rm -rf /tmp/VulnWhisperer - - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data + - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH} # Test one failed scan - rm -rf /tmp/VulnWhisperer - - rm -f test/nessus/GET_scans_exports_164_download - - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - rm -f ${TEST_PATH}/nessus/GET_scans_exports_164_download + - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] # Test two failed scans - rm -rf /tmp/VulnWhisperer - - rm -f test/qualys_vuln/scan_1553941061.87241 - - vuln_whisperer -c configs/test.ini --mock --mock_dir tests/data; [[ $? -eq 2 ]] + - rm -f ${TEST_PATH}/qualys_vuln/scan_1553941061.87241 + - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 2 ]] # Test only nessus - rm -rf /tmp/VulnWhisperer - - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] # Test only qualy_vuln - rm -rf /tmp/VulnWhisperer - - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir tests/data; [[ $? -eq 1 ]] + - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down From 36a8528abce119ed08ebc0f8b05b825123e2b003 Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 8 Apr 2019 12:27:26 +0200 Subject: [PATCH 56/96] Jira Workflow documentation --- docs/source/jira_workflow.png | Bin 0 -> 460050 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/source/jira_workflow.png diff --git a/docs/source/jira_workflow.png b/docs/source/jira_workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..7a9b2dbbb5346704028bc3b50a057f7542728645 GIT binary patch literal 460050 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

d+4dCoY9RMA)hj%Kd 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

&B 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 Date: Mon, 8 Apr 2019 16:37:42 +0200 Subject: [PATCH 57/96] changed version to 1.8 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index baeb231..db493cd 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name='VulnWhisperer', - version='1.7.1', + version='1.8', packages=find_packages(), url='https://github.com/austin-taylor/vulnwhisperer', license="""MIT License From cde2fe2dd82ab89b66d20d13b9a639326fd724d5 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 10 Apr 2019 11:19:44 +0200 Subject: [PATCH 58/96] final commit for qualys web --- vulnwhisp/frameworks/qualys_web.py | 45 ++++++------------------------ 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/vulnwhisp/frameworks/qualys_web.py b/vulnwhisp/frameworks/qualys_web.py index 9e9d762..540d70f 100644 --- a/vulnwhisp/frameworks/qualys_web.py +++ b/vulnwhisp/frameworks/qualys_web.py @@ -126,7 +126,7 @@ def get_all_scans(self, limit=1000, offset=1, status='FINISHED'): return pd.concat(dataframes, axis=0).reset_index().drop('index', axis=1) #### - #### NEXT THING + #### CREATE VULNERABILITY REPORT AND DOWNLOAD IT #### def get_report_status(self, report_id): @@ -140,13 +140,13 @@ def generate_scan_report_XML(self, scan_id): report_xml = E.ServiceRequest( E.data( E.Report( - E.name('![CDATA[API Scan Report generated by VulnWhisperer]]>'), - E.description('![CDATA[CSV Scanning report for VulnWhisperer]]'), + E.name(''), + E.description(''), E.format('CSV'), - E.type('WAS_SCAN_REPORT'), - #E.template( - # E.id(self.template_id) - #), + #E.type('WAS_SCAN_REPORT'), + E.template( + E.id(self.template_id) + ), E.config( E.scanReport( E.target( @@ -163,36 +163,8 @@ def generate_scan_report_XML(self, scan_id): ) return report_xml - def generate_webapp_report_XML(self, app_id): - """Generates a CSV report for an asset based on template defined in .ini file""" - report_xml = E.ServiceRequest( - E.data( - E.Report( - E.name('![CDATA[API Web Application Report generated by VulnWhisperer]]>'), - E.description(''), - E.format('CSV'), - E.template( - E.id(self.template_id) - ), - E.config( - E.webAppReport( - E.target( - E.webapps( - E.WebApp( - E.id(app_id) - ) - ), - ), - ), - ) - ) - ) - ) - return report_xml - def create_report(self, report_id, kind='scan'): - mapper = {'scan': self.generate_scan_report_XML, - 'webapp': self.generate_webapp_report_XML} + mapper = {'scan': self.generate_scan_report_XML} try: data = mapper[kind](report_id) except Exception as e: @@ -456,7 +428,6 @@ def data_normalizer(self, dataframes): merged_df = merged_df.drop(['QID_y', 'QID_x'], axis=1) merged_df = merged_df.rename(columns={'Id': 'QID'}) - #TODO CODE BREAKS HERE, SCAN_META IS AN EMPTY DATAFRAME merged_df = merged_df.assign(**df_dict['SCAN_META'].to_dict(orient='records')[0]) merged_df = pd.merge(merged_df, df_dict['CATEGORY_HEADER'], how='left', left_on=['Category', 'Severity Level'], From 45e23985d3371dec667bf10675f717e45ef81350 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 10 Apr 2019 11:25:28 +0200 Subject: [PATCH 59/96] added comment --- vulnwhisp/frameworks/qualys_web.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vulnwhisp/frameworks/qualys_web.py b/vulnwhisp/frameworks/qualys_web.py index 540d70f..4e50c5f 100644 --- a/vulnwhisp/frameworks/qualys_web.py +++ b/vulnwhisp/frameworks/qualys_web.py @@ -143,7 +143,8 @@ def generate_scan_report_XML(self, scan_id): E.name(''), E.description(''), E.format('CSV'), - #E.type('WAS_SCAN_REPORT'), + #type is not needed, as the template already has it + E.type('WAS_SCAN_REPORT'), E.template( E.id(self.template_id) ), From db669c531a7aa638806243154f1c718f0cb4b02d Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 10 Apr 2019 11:47:58 +0200 Subject: [PATCH 60/96] changing submodule reference --- .gitmodules | 2 +- tests/data | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 160000 tests/data diff --git a/.gitmodules b/.gitmodules index 546e654..f3817e8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "tests/data"] path = tests/data - url = https://github.com/HASecuritySolutions/VulnWhisperer-tests + url = https://github.com/HASecuritySolutions/VulnWhisperer-tests.git diff --git a/tests/data b/tests/data deleted file mode 160000 index 606b8bc..0000000 --- a/tests/data +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 606b8bcbe32b057376ea04b9b2e6a0b0e4454006 From 8b8938e7b31700308785921e472b56a4d90bd123 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 10 Apr 2019 12:04:36 +0200 Subject: [PATCH 61/96] updating submodule --- tests/data | 1 + 1 file changed, 1 insertion(+) create mode 160000 tests/data diff --git a/tests/data b/tests/data new file mode 160000 index 0000000..55dc683 --- /dev/null +++ b/tests/data @@ -0,0 +1 @@ +Subproject commit 55dc6832f8e39f17c97295aadb7de4d6a1277d73 From 7960bd3c59c462a67b308778f02acda8edd58727 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 10 Apr 2019 15:29:29 +0200 Subject: [PATCH 62/96] updating documentation --- README.md | 179 +++++++++++++----------------------------------------- 1 file changed, 41 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 0315a55..0973e56 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@

-VulnWhisperer is a vulnerability data and report aggregator. VulnWhisperer will pull all the reports - and create a file with a unique filename which is then fed into logstash. Logstash extracts data from the filename and tags all of the information inside the report (see logstash_vulnwhisp.conf file). Data is then shipped to elasticsearch to be indexed. +VulnWhisperer is a vulnerability management tool and report aggregator. VulnWhisperer will pull all the reports from the different Vulnerability scanners and create a file with a unique filename for each one, using that data later to sync with Jira and feed Logstash. Jira does a closed cycle full Sync with the data provided by the Scanenrs, while Logstash indexes and tags all of the information inside the report (see logstash files at /resources/elk6/pipeline/). Data is then shipped to ElasticSearch to be indexed, and ends up in a visual and searchable format in Kibana with already defined dashboards. [![Build Status](https://travis-ci.org/HASecuritySolutions/VulnWhisperer.svg?branch=master)](https://travis-ci.org/HASecuritySolutions/VulnWhisperer) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://choosealicense.com/licenses/mit/) @@ -39,9 +38,10 @@ Getting Started =============== 1) Follow the [install requirements](#installreq) -2) Fill out the section you want to process in
example.ini file -3) Modify the IP settings in the logstash files to accomodate your environment and import them to your logstash conf directory (default is /etc/logstash/conf.d/) -4) Import the kibana visualizations +2) Fill out the section you want to process in frameworks_example.ini file +3) [JIRA] If using Jira, fill Jira config in the config file mentioned above. +3) [ELK] Modify the IP settings in the Logstash files to accommodate your environment and import them to your logstash conf directory (default is /etc/logstash/conf.d/) +4) [ELK] Import the Kibana visualizations 5) [Run Vulnwhisperer](#run) Need assistance or just want to chat? Join our [slack channel](https://join.slack.com/t/vulnwhisperer/shared_invite/enQtNDQ5MzE4OTIyODU0LWQxZTcxYTY0MWUwYzA4MTlmMWZlYWY2Y2ZmM2EzNDFmNWVlOTM4MzNjYzI0YzdkMDA0YmQyYWRhZGI2NGUxNGI) @@ -49,20 +49,27 @@ Need assistance or just want to chat? Join our [slack channel](https://join.slac Requirements ------------- #### -* ElasticStack 5.x * Python 2.7 * Vulnerability Scanner -* Optional: Message broker such as Kafka or RabbitMQ +* Reporting System: Jira / ElasticStack 6.6 Install Requirements-VulnWhisperer(may require sudo) -------------------- -**First install requirement dependencies** +**Install OS packages requirement dependencies** (Debian-based distros, CentOS don't need it) ```shell sudo apt-get install zlib1g-dev libxml2-dev libxslt1-dev ``` -**Then install requirements** +**(Optional) Use a python virtualenv to not mess with host python libraries** +```shell +virtualenv venv (will create the python 2.7 virtualenv) +source venv/bin/activate (start the virtualenv, now pip will run there and should install libraries without sudo) + +deactivate (for quitting the virtualenv once you are done) +``` + +**Install python libraries requirements** ```python pip install -r /path/to/VulnWhisperer/requirements.txt @@ -72,89 +79,6 @@ python setup.py install Now you're ready to pull down scans. (see run section) - -Install Requirements-ELK Node **\*SAMPLE\*** --------------------- -The following instructions should be utilized as a **Sample Guide** in the absence of an existing ELK Cluster/Node. This will cover a Debian example install guide of a stand-alone node of Elasticsearch & Kibana. - -While Logstash is included in this install guide, it it recommended that a seperate host pulling the VulnWhisperer data is utilized with Logstash to ship the data to the Elasticsearch node. - -*Please note there is a docker-compose.yml available as well.* - -**Debian:** *(https://www.elastic.co/guide/en/elasticsearch/reference/5.6/deb.html)* -```shell -sudo apt-get install -y default-jre -wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add - -sudo apt-get install apt-transport-https -echo "deb https://artifacts.elastic.co/packages/5.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list -sudo apt-get update && sudo apt-get install elasticsearch kibana logstash -sudo /bin/systemctl daemon-reload -sudo /bin/systemctl enable elasticsearch.service -sudo /bin/systemctl enable kibana.service -sudo /bin/systemctl enable logstash.service -``` - -**Elasticsearch & Kibana Sample Config Notes** - -Utilizing your favorite text editor: -* Grab your host IP and change the IP of your /etc/elasticsearch/elasticsearch.yml file. (This defaults to 'localhost') -* Validate Elasticsearch is set to run on port 9200 (Default) -* Grab your host IP and change the IP of your /etc/kibana/kibana.yml file. (This defaults to 'localhost') *Validate that Kibana is pointing to the correct Elasticsearch IP (This was set in the previous step)* -* Validate Kibana is set to run on port 5601 (Default) - -*Start elasticsearch and validate they are running/communicating with one another:* -```shell -sudo service elasticsearch start -sudo service kibana start -``` -OR -```shell -sudo systemctl start elasticsearch.service -sudo systemctl start kibana.service -``` - -**Logstash Sample Config Notes** - -* Copy/Move the Logstash .conf files from */VulnWhisperer/logstash/* to */etc/logstash/conf.d/* -* Validate the Logstash.conf files *input* contains the correct location of VulnWhisper Scans in the *input.file.path* directory identified below: -``` -input { - file { - path => "/opt/vulnwhisperer/nessus/**/*" - start_position => "beginning" - tags => "nessus" - type => "nessus" - } -} -``` -* Validate the Logstash.conf files *output* contains the correct Elasticsearch IP set during the previous step above: (This will default to localhost) -``` -output { - if "nessus" in [tags] or [type] == "nessus" { - #stdout { codec => rubydebug } - elasticsearch { - hosts => [ "localhost:9200" ] - index => "logstash-vulnwhisperer-%{+YYYY.MM}" - } - } -``` -* Validate logstash has the correct file permissions to read the location of the VulnWhisperer Scans - -Once configured run Logstash: (Running Logstash as a service will pick up all the files in */etc/logstash/conf.d/* If you would like to run only one logstash file please reference the command below): - -Logstash as a service: -```shell -sudo service logstash start -``` -*OR* -```shell -sudo systemctl start logstash.service -``` -Single Logstash file: -```shell -sudo /usr/share/logstash/bin/logstash --path.settings /etc/logstash/ -f /etc/logstash/conf.d/1000_nessus_process_file.conf -``` - Configuration ----- @@ -178,64 +102,43 @@ or vuln_whisperer -c configs/frameworks_example.ini -s qualys ``` -If no section is specified (e.g. -s nessus), vulnwhisperer will check on the config file for the modules that have the property enabled=true and run them sequentially. +If no section is specified (e.g. -s nessus), vulnwhisperer will check on the config file for the modules that have the property `enabled=true` and run them sequentially.

-Next you'll need to import the visualizations into Kibana and setup your logstash config. A more thorough README is underway with setup instructions. +Next you'll need to import the visualizations into Kibana and setup your logstash config. You can either follow the sample setup instructions [here](https://github.com/HASecuritySolutions/VulnWhisperer/wiki/Sample-Guide-ELK-Deployment) or go for the `docker-compose` solution we offer. Docker-compose ----- -The docker-compose file has been tested and running on a Ubuntu 18.04 environment, with docker-ce v.18.06. The structure's purpose is to store locally the data from the scanners, letting vulnwhisperer update the records and Logstash feed them to ElasticSearch, so it requires a local storage folder. - -- It will run out of the box if you create on the root directory of VulnWhisperer a folder named "data", which needs permissions for other users to read/write/execute in order to sync: -```shell - mkdir data && chmod -R 666 data #data/database/report_tracker.db will need 777 to use with local vulnwhisperer -``` -otherwise the users running inside the docker containers will not be able to work with it properly. If you don't apply chmod recursively, it will still work to sync the data, but only root use in localhost will have access to the created data (if you run local vulnwhisperer with the same data will break). -- docker/logstash.yml file will need other read/write permissions in order for logstash container to use the configuration file; youll need to run: -```shell -chmod 666 docker/logstash.yml +ELK is a whole world by itself, and for newcomers to the platform, it requires basic Linux skills and usually a bit of troubleshooting until it is deployed and working as expected. As we are not able to provide support for each users ELK problems, we put together a docker-compose which includes: -- The vulnwhisperer container inside of docker-compose is using network_mode=host instead of the bridge mode by default; this is due to issues encountered when the container is trying to pull data from your scanners from a different VLAN than the one you currently are. The host network mode uses the DNS and interface from the host itself, fixing those issues, but it breaks the network isolation from the container (this is due to docker creating bridge interfaces to route the traffic, blocking both container's and host's network). If you change this to bridge, you might need to add your DNS to the config in order to resolve internal hostnames. -- ElasticSearch requires having the value vm.max_map_count with a minimum of 262144; otherwise, it will probably break at launch. Please check https://elk-docker.readthedocs.io/#prerequisites to solve that. -- If you want to change the "data" folder for storing the results, remember to change it from both the docker-compose.yml file and the logstash files that are in the root "docker/" folder. -- Hostnames do NOT allow _ (underscores) on it, if you change the hostname configuration from the docker-compose file and add underscores, config files from logstash will fail. -- If you are having issues with the connection between hosts, to troubleshoot them you can spawn a shell in said host doing the following: -```shell -docker ps #check the container is running -docker exec -i -t vulnwhisp-vulnwhisperer /bin/bash #where vulnwhisp-vulnwhisperer is the container name you want to troubleshoot -``` -You can also make sure that all ELK components are working by doing "curl -i host:9200 (elastic)/ host:5601 (kibana) /host:9600 (logstash). WARNING! It is possible that logstash is not exposing to the external network the port but it does to its internal docker network "esnet". -- If Kibana is not showing the results, check that you are searching on the whole ES range, as by default it shows logs for the last 15 minutes (you can choose up to last 5 years) -- X-Pack has been disabled by default due to the noise, plus being a trial version. You can enable it modifying the docker-compose.yml and docker/logstash.conf files. Logstash.conf contains the default credentials for the X-Pack enabled ES. -- On Logstash container, "/usr/share/logstash/pipeline/" is the default path for pipelines and "/usr/share/logstash/config/" for logstash.yml file, instead of "/etc/logstash/conf.d/" and "/etc/logstash/". -- In order to make vulnwhisperer run periodically, and only the vulnwhisperer code, add to crontab the following: - - -```shell -0 8 * * * /usr/bin/docker-compose up vulnwhisp-vulnwhisperer -``` - -To launch docker-compose, do: -```shell -docker-compose -f docker-compose.yml up -``` +- VulnWhisperer +- Logstash 6.6 +- ElasticSearch 6.6 +- Kibana 6.6 +The docker-compose just requires specifying the paths where the VulnWhisperer data will be saved, and where the config files reside. If ran directly after `git clone`, with just adding the Scanner config to the VulnWhisperer config file (/resources/elk6/vulnwhisperer.ini), it will work out of the box. -Running Nightly ---------------- -If you're running linux, be sure to setup a cronjob to remove old files that get stored in the database. Be sure to change .csv if you're using json. +It also takes care to load the Kibana Dashboards and Visualizations automatically through the API, which needs to be done manually otherwise at Kibana's startup. -Setup crontab -e with the following config (modify to your environment) - this will run vulnwhisperer each night at 0130: +For more info about the docker-compose, check on the [docker-compose wiki](https://github.com/HASecuritySolutions/VulnWhisperer/wiki/docker-compose-Instructions) or the [FAQ](https://github.com/HASecuritySolutions/VulnWhisperer/wiki). -`00 1 * * * /usr/bin/find /opt/vulnwhisp/ -type f -name '*.csv' -ctime +3 -exec rm {} \;` +Getting Started +=============== -`30 1 * * * /usr/local/bin/vuln_whisperer -c /opt/vulnwhisp/configs/example.ini` +Our current Roadmap is as follows: +- [ ] Create a Vulnerability Standard +- [ ] Map every scanner results to the standard +- [ ] Create Scanner module guidelines for easy integration of new scanners (consistency will allow #14) +- [ ] Refactor the code to reuse functions and enable full compatibility among modules +- [ ] Change Nessus CSV to JSON (Consistency and Fix #82) +- [ ] Adapt single Logstash to standard and Kibana Dashboards +- [ ] Implement Detectify Scanner +- [ ] Implement Splunk Reporting/Dashboards -Another option is to tell logstash to delete files after they have been processed. +On top of this, we try to focus on fixing bugs as soon as possible, which might delay the development. We also very welcome PR's, and once we have the new standard implemented, it will be very easy to add compatibility with new scanners. -_For windows, you may need to type the full path of the binary in vulnWhisperer located in the bin directory._ +The Vulnerability Standard will initially be a new simple one level JSON with all the information that matches from the different scanners having standardized variable names, while maintaining the rest of the variables as they are. In the future, once everything is implemented, we will evaluate moving to an existing standard like ECS or AWS Vulnerability Schema; we prioritize functionality over perfection. Video Walkthrough -- Featured on ElasticWebinar ---------------------------------------------- @@ -250,9 +153,9 @@ Authors Contributors ------------ - - [@pemontto](https://github.com/pemontto) - [Quim Montal (@qmontal)](https://github.com/qmontal) - - [Andrea Lusuardi (@uovobw)](https://github.com/uovobw) + - [@pemontto](https://github.com/pemontto) + - [@cybergoof](https://github.com/cybergoof) AS SEEN ON TV ------------- From b25c769a0137baf1438f77045adc9ec3f08dcb21 Mon Sep 17 00:00:00 2001 From: Quim Date: Wed, 10 Apr 2019 15:46:57 +0200 Subject: [PATCH 63/96] readme details --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0973e56..58620df 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

-VulnWhisperer is a vulnerability management tool and report aggregator. VulnWhisperer will pull all the reports from the different Vulnerability scanners and create a file with a unique filename for each one, using that data later to sync with Jira and feed Logstash. Jira does a closed cycle full Sync with the data provided by the Scanenrs, while Logstash indexes and tags all of the information inside the report (see logstash files at /resources/elk6/pipeline/). Data is then shipped to ElasticSearch to be indexed, and ends up in a visual and searchable format in Kibana with already defined dashboards. +VulnWhisperer is a vulnerability management tool and report aggregator. VulnWhisperer will pull all the reports from the different Vulnerability scanners and create a file with a unique filename for each one, using that data later to sync with Jira and feed Logstash. Jira does a closed cycle full Sync with the data provided by the Scanners, while Logstash indexes and tags all of the information inside the report (see logstash files at /resources/elk6/pipeline/). Data is then shipped to ElasticSearch to be indexed, and ends up in a visual and searchable format in Kibana with already defined dashboards. [![Build Status](https://travis-ci.org/HASecuritySolutions/VulnWhisperer.svg?branch=master)](https://travis-ci.org/HASecuritySolutions/VulnWhisperer) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://choosealicense.com/licenses/mit/) @@ -96,8 +96,8 @@ There are a few configuration steps to setting up VulnWhisperer: ----- To run, fill out the configuration file with your vulnerability scanner settings. Then you can execute from the command line. ```python - -vuln_whisperer -c configs/frameworks_example.ini -s nessus +(optional flag: -F -> provides "Fancy" log colouring, good for comprehension when manually executing VulnWhisperer) +vuln_whisperer -c configs/frameworks_example.ini -s nessus or vuln_whisperer -c configs/frameworks_example.ini -s qualys From 4ed6827ee6225ebae71cd5cf4702a9bdda2983cb Mon Sep 17 00:00:00 2001 From: pemontto Date: Thu, 11 Apr 2019 08:27:28 +1000 Subject: [PATCH 64/96] Clean config and separate qualys data --- configs/frameworks_example.ini | 24 ++----------------- configs/test.ini | 24 ++----------------- .../elk6/pipeline/2000_qualys_web_scans.conf | 3 +-- 3 files changed, 5 insertions(+), 46 deletions(-) diff --git a/configs/frameworks_example.ini b/configs/frameworks_example.ini index 77d283c..20410cb 100755 --- a/configs/frameworks_example.ini +++ b/configs/frameworks_example.ini @@ -26,7 +26,7 @@ enabled = true hostname = qualysapi.qg2.apps.qualys.com username = exampleuser password = examplepass -write_path=/opt/VulnWhisperer/data/qualys/ +write_path=/opt/VulnWhisperer/data/qualys_web/ db_path=/opt/VulnWhisperer/data/database verbose=true @@ -42,16 +42,10 @@ enabled = true hostname = qualysapi.qg2.apps.qualys.com username = exampleuser password = examplepass -write_path=/opt/VulnWhisperer/data/qualys/ +write_path=/opt/VulnWhisperer/data/qualys_vuln/ db_path=/opt/VulnWhisperer/data/database verbose=true -# Set the maximum number of retries each connection should attempt. -#Note, this applies only to failed connections and timeouts, never to requests where the server returns a response. -max_retries = 10 -# Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. -template_id = 126024 - [detectify] #Reference https://developer.detectify.com/ enabled = false @@ -74,20 +68,6 @@ write_path=/opt/VulnWhisperer/data/openvas/ db_path=/opt/VulnWhisperer/data/database verbose=true -#[proxy] -; This section is optional. Leave it out if you're not using a proxy. -; You can use environmental variables as well: http://www.python-requests.org/en/latest/user/advanced/#proxies - -; proxy_protocol set to https, if not specified. -#proxy_url = proxy.mycorp.com - -; proxy_port will override any port specified in proxy_url -#proxy_port = 8080 - -; proxy authentication -#proxy_username = proxyuser -#proxy_password = proxypass - [jira] enabled = false hostname = jira-host diff --git a/configs/test.ini b/configs/test.ini index 468ba4a..b8ce72f 100755 --- a/configs/test.ini +++ b/configs/test.ini @@ -26,7 +26,7 @@ enabled = false hostname = qualys_web username = exampleuser password = examplepass -write_path=/tmp/VulnWhisperer/data/qualys/ +write_path=/tmp/VulnWhisperer/data/qualys_web/ db_path=/tmp/VulnWhisperer/data/database verbose=true @@ -42,16 +42,10 @@ enabled = true hostname = qualys_vuln username = exampleuser password = examplepass -write_path=/tmp/VulnWhisperer/data/qualys/ +write_path=/tmp/VulnWhisperer/data/qualys_vuln/ db_path=/tmp/VulnWhisperer/data/database verbose=true -# Set the maximum number of retries each connection should attempt. -#Note, this applies only to failed connections and timeouts, never to requests where the server returns a response. -max_retries = 10 -# Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. -template_id = 126024 - [detectify] #Reference https://developer.detectify.com/ enabled = false @@ -74,20 +68,6 @@ write_path=/tmp/VulnWhisperer/data/openvas/ db_path=/tmp/VulnWhisperer/data/database verbose=true -#[proxy] -; This section is optional. Leave it out if you're not using a proxy. -; You can use environmental variables as well: http://www.python-requests.org/en/latest/user/advanced/#proxies - -; proxy_protocol set to https, if not specified. -#proxy_url = proxy.mycorp.com - -; proxy_port will override any port specified in proxy_url -#proxy_port = 8080 - -; proxy authentication -#proxy_username = proxyuser -#proxy_password = proxypass - [jira] enabled = false hostname = jira-host diff --git a/resources/elk6/pipeline/2000_qualys_web_scans.conf b/resources/elk6/pipeline/2000_qualys_web_scans.conf index 66b0993..fbf83ee 100644 --- a/resources/elk6/pipeline/2000_qualys_web_scans.conf +++ b/resources/elk6/pipeline/2000_qualys_web_scans.conf @@ -6,7 +6,7 @@ input { file { - path => "/opt/vulnwhisperer/data/qualys/*.json" + path => [ "/opt/vulnwhisperer/data/qualys/*.json" , "/opt/vulnwhisperer/data/qualys_web/*.json", "/opt/vulnwhisperer/data/qualys_vuln/*.json"] type => json codec => json start_position => "beginning" @@ -14,7 +14,6 @@ input { mode => "read" start_position => "beginning" file_completed_action => "delete" - } } From d2a7513ed1191b85c0e2e73b0a93548bd8ca8492 Mon Sep 17 00:00:00 2001 From: pemontto Date: Thu, 11 Apr 2019 10:36:41 +1000 Subject: [PATCH 65/96] Fix nessus logstash field cvss3_vector --- .../logstash/1000_nessus_process_file.conf | 2 +- resources/elk6/pipeline/1000_nessus_process_file.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf b/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf index 60e1920..d8d4f92 100644 --- a/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf +++ b/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf @@ -27,7 +27,7 @@ filter { csv { # columns => ["plugin_id", "cve", "cvss", "risk", "asset", "protocol", "port", "plugin_name", "synopsis", "description", "solution", "see_also", "plugin_output"] - columns => ["plugin_id", "cve", "cvss", "risk", "asset", "protocol", "port", "plugin_name", "synopsis", "description", "solution", "see_also", "plugin_output", "asset_uuid", "vulnerability_state", "ip", "fqdn", "netbios", "operating_system", "mac_address", "plugin_family", "cvss_base", "cvss_temporal", "cvss_temporal_vector", "cvss_vector", "cvss3_base", "cvss3_temporal", "cvss3_temporal_vector", "cvss_vector", "system_type", "host_start", "host_end"] + columns => ["plugin_id", "cve", "cvss", "risk", "asset", "protocol", "port", "plugin_name", "synopsis", "description", "solution", "see_also", "plugin_output", "asset_uuid", "vulnerability_state", "ip", "fqdn", "netbios", "operating_system", "mac_address", "plugin_family", "cvss_base", "cvss_temporal", "cvss_temporal_vector", "cvss_vector", "cvss3_base", "cvss3_temporal", "cvss3_temporal_vector", "cvss3_vector", "system_type", "host_start", "host_end"] separator => "," source => "message" } diff --git a/resources/elk6/pipeline/1000_nessus_process_file.conf b/resources/elk6/pipeline/1000_nessus_process_file.conf index dcb74a2..0c64047 100644 --- a/resources/elk6/pipeline/1000_nessus_process_file.conf +++ b/resources/elk6/pipeline/1000_nessus_process_file.conf @@ -29,7 +29,7 @@ filter { csv { # columns => ["plugin_id", "cve", "cvss", "risk", "asset", "protocol", "port", "plugin_name", "synopsis", "description", "solution", "see_also", "plugin_output"] - columns => ["plugin_id", "cve", "cvss", "risk", "asset", "protocol", "port", "plugin_name", "synopsis", "description", "solution", "see_also", "plugin_output", "asset_uuid", "vulnerability_state", "ip", "fqdn", "netbios", "operating_system", "mac_address", "plugin_family", "cvss_base", "cvss_temporal", "cvss_temporal_vector", "cvss_vector", "cvss3_base", "cvss3_temporal", "cvss3_temporal_vector", "cvss_vector", "system_type", "host_start", "host_end"] + columns => ["plugin_id", "cve", "cvss", "risk", "asset", "protocol", "port", "plugin_name", "synopsis", "description", "solution", "see_also", "plugin_output", "asset_uuid", "vulnerability_state", "ip", "fqdn", "netbios", "operating_system", "mac_address", "plugin_family", "cvss_base", "cvss_temporal", "cvss_temporal_vector", "cvss_vector", "cvss3_base", "cvss3_temporal", "cvss3_temporal_vector", "cvss3_vector", "system_type", "host_start", "host_end"] separator => "," source => "message" } From 8dc3b2f8aca3e89f540565278c99cba6b839d9d0 Mon Sep 17 00:00:00 2001 From: pemontto Date: Thu, 11 Apr 2019 10:41:13 +1000 Subject: [PATCH 66/96] Add qualys paths to elk5 logstash config --- .../elk5-old_compatibility/logstash/2000_qualys_web_scans.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf b/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf index b330260..504de84 100644 --- a/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf +++ b/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf @@ -6,7 +6,7 @@ input { file { - path => "/opt/vulnwhisperer/qualys/*.json" + path => [ "/opt/vulnwhisperer/data/qualys/*.json" , "/opt/vulnwhisperer/data/qualys_web/*.json", "/opt/vulnwhisperer/data/qualys_vuln/*.json" ] type => json codec => json start_position => "beginning" From 90908bd0c6bde38942be41316e05548ef230ba74 Mon Sep 17 00:00:00 2001 From: pemontto Date: Fri, 12 Apr 2019 11:39:49 +1000 Subject: [PATCH 67/96] Remove deps from docker image --- Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 64d75a8..606e6a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,14 +13,11 @@ COPY requirements.txt requirements.txt COPY setup.py setup.py COPY vulnwhisp/ vulnwhisp/ COPY bin/ bin/ -COPY deps/ deps/ COPY configs/frameworks_example.ini frameworks_example.ini RUN python setup.py clean --all RUN pip install -r requirements.txt -WORKDIR /opt/VulnWhisperer/deps/qualysapi -RUN python setup.py install WORKDIR /opt/VulnWhisperer RUN python setup.py install From 8433055f174530fbb61612177b1df16c57b3a8ad Mon Sep 17 00:00:00 2001 From: pemontto Date: Fri, 12 Apr 2019 11:40:01 +1000 Subject: [PATCH 68/96] Fix more unicode issues --- vulnwhisp/vulnwhisp.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index 782af0e..5903630 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -412,7 +412,7 @@ def whisper_nessus(self): history_id, norm_time, 'csv') repls = (('\\', '_'), ('/', '_'), (' ', '_')) file_name = reduce(lambda a, kv: a.replace(*kv), repls, file_name) - relative_path_name = self.path_check(folder_name + '/' + file_name) + relative_path_name = self.path_check(folder_name + '/' + file_name).encode('utf8') if os.path.isfile(relative_path_name): if self.develop: @@ -581,7 +581,7 @@ def whisper_reports(self, + '_{last_updated}'.format(last_updated=launched_date) \ + '.{extension}'.format(extension=output_format) - relative_path_name = self.path_check(report_name) + relative_path_name = self.path_check(report_name).encode('utf8') if os.path.isfile(relative_path_name): #TODO Possibly make this optional to sync directories @@ -743,7 +743,7 @@ def whisper_reports(self, output_format='json', launched_date=None, report_id=No report_name = 'openvas_scan_{scan_name}_{last_updated}.{extension}'.format(scan_name=scan_name, last_updated=launched_date, extension=output_format) - relative_path_name = self.path_check(report_name) + relative_path_name = self.path_check(report_name).encode('utf8') scan_reference = report_id if os.path.isfile(relative_path_name): @@ -861,7 +861,7 @@ def whisper_reports(self, + '_{last_updated}'.format(last_updated=launched_date) \ + '.json' - relative_path_name = self.path_check(report_name) + relative_path_name = self.path_check(report_name).encode('utf8') if os.path.isfile(relative_path_name): #TODO Possibly make this optional to sync directories From 668efe2b7afe418f3b587331cc72fa4a4ab49cdc Mon Sep 17 00:00:00 2001 From: pemontto Date: Fri, 12 Apr 2019 11:44:04 +1000 Subject: [PATCH 69/96] Add extra test case --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index c412177..2452b5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,8 @@ script: # Test successful scan download and parsing - rm -rf /tmp/VulnWhisperer - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH} + # Run a second time with no scans to import + - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH} # Test one failed scan - rm -rf /tmp/VulnWhisperer - rm -f ${TEST_PATH}/nessus/GET_scans_exports_164_download From b35da1c79efb018b344fad592e8252c3f118694b Mon Sep 17 00:00:00 2001 From: pemontto Date: Fri, 12 Apr 2019 17:51:15 +1000 Subject: [PATCH 70/96] reduce docker layers and support test data --- Dockerfile | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 606e6a1..a2806ee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,10 @@ FROM centos:latest MAINTAINER Justin Henderson justin@hasecuritysolutions.com -RUN yum update -y -RUN yum install -y python python-devel git gcc -RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py -RUN python get-pip.py +RUN yum update -y && \ + yum install -y python python-devel git gcc && \ + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \ + python get-pip.py WORKDIR /opt/VulnWhisperer @@ -15,12 +15,13 @@ COPY vulnwhisp/ vulnwhisp/ COPY bin/ bin/ COPY configs/frameworks_example.ini frameworks_example.ini -RUN python setup.py clean --all -RUN pip install -r requirements.txt +RUN python setup.py clean --all && \ + pip install -r requirements.txt WORKDIR /opt/VulnWhisperer -RUN python setup.py install +RUN python setup.py install && \ + ln -s /opt/VulnWhisperer /tmp/VulnWhisperer CMD vuln_whisperer -c /opt/VulnWhisperer/frameworks_example.ini From 0227636c4cd3c9d84d61e65ee8e61b4c515e6051 Mon Sep 17 00:00:00 2001 From: pemontto Date: Fri, 12 Apr 2019 17:54:17 +1000 Subject: [PATCH 71/96] unify case among config --- docker-compose-test.yml | 94 +++++++++++++++++++ docker-compose.v6.yml | 10 +- .../logstash/1000_nessus_process_file.conf | 4 +- .../logstash/2000_qualys_web_scans.conf | 2 +- .../logstash/3000_openvas.conf | 2 +- .../logstash/4000_jira.conf | 2 +- .../pipeline/1000_nessus_process_file.conf | 9 +- .../elk6/pipeline/2000_qualys_web_scans.conf | 7 +- resources/elk6/pipeline/3000_openvas.conf | 7 +- resources/elk6/pipeline/4000_jira.conf | 2 +- resources/elk6/vulnwhisperer.ini | 26 ++--- 11 files changed, 137 insertions(+), 28 deletions(-) create mode 100644 docker-compose-test.yml diff --git a/docker-compose-test.yml b/docker-compose-test.yml new file mode 100644 index 0000000..89e3ee4 --- /dev/null +++ b/docker-compose-test.yml @@ -0,0 +1,94 @@ +version: '2' +services: + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:6.6.0 + container_name: elasticsearch + environment: + - cluster.name=vulnwhisperer + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms1g -Xmx1g" + - xpack.security.enabled=false + - cluster.routing.allocation.disk.threshold_enabled=false + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + mem_limit: 8g + volumes: + - ./docker_data/esdata1:/usr/share/elasticsearch/data + ports: + - 9200:9200 + #restart: always + networks: + esnet: + aliases: + - elasticsearch.local + + kibana: + image: docker.elastic.co/kibana/kibana:6.6.0 + container_name: kibana + environment: + SERVER_NAME: kibana + ELASTICSEARCH_URL: http://elasticsearch:9200 + ports: + - 5601:5601 + depends_on: + - elasticsearch + # volumes: + # - ./kibana-data: + networks: + esnet: + aliases: + - kibana.local + + kibana-config: + image: alpine + container_name: kibana-config + volumes: + - ./resources/elk6/init_kibana.sh:/opt/init_kibana.sh + - ./resources/elk6/kibana_APIonly.json:/opt/kibana_APIonly.json + - ./docker_data/kibana_optimize:/usr/share/kibana/optimize + command: sh -c "apk add --no-cache curl bash && chmod +x /opt/init_kibana.sh && chmod +r /opt/kibana_APIonly.json && cd /opt/ && /bin/bash /opt/init_kibana.sh" # /opt/kibana_APIonly.json" + networks: + esnet: + aliases: + - kibana-config.local + + logstash: + image: docker.elastic.co/logstash/logstash:6.6.0 + container_name: logstash + volumes: + - ./resources/elk6/pipeline/:/usr/share/logstash/pipeline + - ./docker_data/data/:/opt/VulnWhisperer/data + - ./resources/elk6/logstash.yml:/usr/share/logstash/config/logstash.yml + environment: + - xpack.monitoring.enabled=false + depends_on: + - elasticsearch + networks: + esnet: + aliases: + - logstash.local + vulnwhisperer: + image: vulnwhisperer-1.8 + container_name: vulnwhisperer + entrypoint: [ + "vuln_whisperer", + "-c", + "/opt/VulnWhisperer/vulnwhisperer.ini", + "--mock", + "--mock_dir", + "/tests/data" + ] + volumes: + # - /opt/VulnWhisperer/data/:/opt/VulnWhisperer/data + - ./docker_data/data/:/opt/VulnWhisperer/data + - ./configs/test.ini:/opt/VulnWhisperer/vulnwhisperer.ini + - ./tests/data/:/tests/data + network_mode: host + +networks: + esnet: diff --git a/docker-compose.v6.yml b/docker-compose.v6.yml index b5a833e..f53aa0c 100644 --- a/docker-compose.v6.yml +++ b/docker-compose.v6.yml @@ -56,7 +56,7 @@ services: container_name: logstash volumes: - ./resources/elk6/pipeline/:/usr/share/logstash/pipeline - - ./data/:/opt/vulnwhisperer/data + - ./data/:/opt/VulnWhisperer/data #- ./resources/elk6/logstash.yml:/usr/share/logstash/config/logstash.yml environment: - xpack.monitoring.enabled=false @@ -72,12 +72,12 @@ services: entrypoint: [ "vuln_whisperer", "-c", - "/opt/vulnwhisperer/vulnwhisperer.ini" + "/opt/VulnWhisperer/vulnwhisperer.ini" ] volumes: - - /opt/vulnwhisperer/data/:/opt/vulnwhisperer/data - - ./data/:/opt/vulnwhisperer/data - - ./resources/elk6/vulnwhisperer.ini:/opt/vulnwhisperer/vulnwhisperer.ini + - /opt/VulnWhisperer/data/:/opt/VulnWhisperer/data + - ./data/:/opt/VulnWhisperer/data + - ./resources/elk6/vulnwhisperer.ini:/opt/VulnWhisperer/vulnwhisperer.ini network_mode: host volumes: esdata1: diff --git a/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf b/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf index d8d4f92..306bd3d 100644 --- a/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf +++ b/resources/elk5-old_compatibility/logstash/1000_nessus_process_file.conf @@ -7,13 +7,13 @@ input { file { - path => "/opt/vulnwhisperer/nessus/**/*" + path => "/opt/VulnWhisperer/nessus/**/*" start_position => "beginning" tags => "nessus" type => "nessus" } file { - path => "/opt/vulnwhisperer/tenable/*.csv" + path => "/opt/VulnWhisperer/tenable/*.csv" start_position => "beginning" tags => "tenable" type => "tenable" diff --git a/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf b/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf index 504de84..fe98ef8 100644 --- a/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf +++ b/resources/elk5-old_compatibility/logstash/2000_qualys_web_scans.conf @@ -6,7 +6,7 @@ input { file { - path => [ "/opt/vulnwhisperer/data/qualys/*.json" , "/opt/vulnwhisperer/data/qualys_web/*.json", "/opt/vulnwhisperer/data/qualys_vuln/*.json" ] + path => [ "/opt/VulnWhisperer/data/qualys/*.json" , "/opt/VulnWhisperer/data/qualys_web/*.json", "/opt/VulnWhisperer/data/qualys_vuln/*.json" ] type => json codec => json start_position => "beginning" diff --git a/resources/elk5-old_compatibility/logstash/3000_openvas.conf b/resources/elk5-old_compatibility/logstash/3000_openvas.conf index f560731..32e889c 100644 --- a/resources/elk5-old_compatibility/logstash/3000_openvas.conf +++ b/resources/elk5-old_compatibility/logstash/3000_openvas.conf @@ -6,7 +6,7 @@ input { file { - path => "/opt/vulnwhisperer/openvas/*.json" + path => "/opt/VulnWhisperer/openvas/*.json" type => json codec => json start_position => "beginning" diff --git a/resources/elk5-old_compatibility/logstash/4000_jira.conf b/resources/elk5-old_compatibility/logstash/4000_jira.conf index e4106c7..03a0b04 100644 --- a/resources/elk5-old_compatibility/logstash/4000_jira.conf +++ b/resources/elk5-old_compatibility/logstash/4000_jira.conf @@ -2,7 +2,7 @@ input { file { - path => "/opt/vulnwhisperer/jira/*.json" + path => "/opt/VulnWhisperer/jira/*.json" type => json codec => json start_position => "beginning" diff --git a/resources/elk6/pipeline/1000_nessus_process_file.conf b/resources/elk6/pipeline/1000_nessus_process_file.conf index 0c64047..0ea00c1 100644 --- a/resources/elk6/pipeline/1000_nessus_process_file.conf +++ b/resources/elk6/pipeline/1000_nessus_process_file.conf @@ -7,14 +7,14 @@ input { file { - path => "/opt/vulnwhisperer/data/nessus/**/*" + path => "/opt/VulnWhisperer/data/nessus/**/*" mode => "read" start_position => "beginning" file_completed_action => "delete" tags => "nessus" } file { - path => "/opt/vulnwhisperer/data/tenable/*.csv" + path => "/opt/VulnWhisperer/data/tenable/*.csv" mode => "read" start_position => "beginning" file_completed_action => "delete" @@ -53,11 +53,13 @@ filter { } #If using filebeats as your source, you will need to replace the "path" field to "source" + # Remove when scan name is included in event (current method is error prone) grok { match => { "path" => "(?[a-zA-Z0-9_.\-]+)_%{INT:scan_id}_%{INT:history_id}_%{INT:last_updated}.csv$" } tag_on_failure => [] } + # TODO remove when @timestamp is included in event date { match => [ "last_updated", "UNIX" ] target => "@timestamp" @@ -169,6 +171,9 @@ filter { output { if "nessus" in [tags] or "tenable" in [tags]{ + stdout { + codec => dots + } elasticsearch { hosts => [ "elasticsearch:9200" ] index => "logstash-vulnwhisperer-%{+YYYY.MM}" diff --git a/resources/elk6/pipeline/2000_qualys_web_scans.conf b/resources/elk6/pipeline/2000_qualys_web_scans.conf index fbf83ee..7c207c7 100644 --- a/resources/elk6/pipeline/2000_qualys_web_scans.conf +++ b/resources/elk6/pipeline/2000_qualys_web_scans.conf @@ -6,7 +6,7 @@ input { file { - path => [ "/opt/vulnwhisperer/data/qualys/*.json" , "/opt/vulnwhisperer/data/qualys_web/*.json", "/opt/vulnwhisperer/data/qualys_vuln/*.json"] + path => [ "/opt/VulnWhisperer/data/qualys/*.json" , "/opt/VulnWhisperer/data/qualys_web/*.json", "/opt/VulnWhisperer/data/qualys_vuln/*.json"] type => json codec => json start_position => "beginning" @@ -98,6 +98,8 @@ filter { target => "last_time_tested" } } + + # TODO remove when @timestamp is included in event date { match => [ "last_updated", "UNIX" ] target => "@timestamp" @@ -147,6 +149,9 @@ filter { } output { if "qualys" in [tags] { + stdout { + codec => dots + } elasticsearch { hosts => [ "elasticsearch:9200" ] index => "logstash-vulnwhisperer-%{+YYYY.MM}" diff --git a/resources/elk6/pipeline/3000_openvas.conf b/resources/elk6/pipeline/3000_openvas.conf index 5fcc56c..4a96ca3 100644 --- a/resources/elk6/pipeline/3000_openvas.conf +++ b/resources/elk6/pipeline/3000_openvas.conf @@ -6,7 +6,7 @@ input { file { - path => "/opt/vulnwhisperer/data/openvas/*.json" + path => "/opt/VulnWhisperer/data/openvas/*.json" type => json codec => json start_position => "beginning" @@ -92,6 +92,8 @@ filter { target => "last_time_tested" } } + + # TODO remove when @timestamp is included in event date { match => [ "last_updated", "UNIX" ] target => "@timestamp" @@ -141,6 +143,9 @@ filter { } output { if "openvas" in [tags] { + stdout { + codec => dots + } elasticsearch { hosts => [ "elasticsearch:9200" ] index => "logstash-vulnwhisperer-%{+YYYY.MM}" diff --git a/resources/elk6/pipeline/4000_jira.conf b/resources/elk6/pipeline/4000_jira.conf index 83030cc..47d978c 100644 --- a/resources/elk6/pipeline/4000_jira.conf +++ b/resources/elk6/pipeline/4000_jira.conf @@ -2,7 +2,7 @@ input { file { - path => "/opt/vulnwhisperer/data/jira/*.json" + path => "/opt/VulnWhisperer/data/jira/*.json" type => json codec => json start_position => "beginning" diff --git a/resources/elk6/vulnwhisperer.ini b/resources/elk6/vulnwhisperer.ini index 2b92761..2e8c687 100644 --- a/resources/elk6/vulnwhisperer.ini +++ b/resources/elk6/vulnwhisperer.ini @@ -4,8 +4,8 @@ hostname=localhost port=8834 username=nessus_username password=nessus_password -write_path=/opt/vulnwhisperer/data/nessus/ -db_path=/opt/vulnwhisperer/database +write_path=/opt/VulnWhisperer/data/nessus/ +db_path=/opt/VulnWhisperer/database trash=false verbose=true @@ -15,7 +15,7 @@ hostname=cloud.tenable.com port=443 username=tenable.io_username password=tenable.io_password -write_path=/opt/vulnwhisperer/data/tenable/ +write_path=/opt/VulnWhisperer/data/tenable/ db_path=/opt/VulnWhisperer/data/database trash=false verbose=true @@ -26,8 +26,8 @@ enabled = true hostname = qualysapi.qg2.apps.qualys.com username = exampleuser password = examplepass -write_path=/opt/vulnwhisperer/data/qualys/ -db_path=/opt/vulnwhisperer/data/database +write_path=/opt/VulnWhisperer/data/qualys/ +db_path=/opt/VulnWhisperer/data/database verbose=true # Set the maximum number of retries each connection should attempt. @@ -42,8 +42,8 @@ enabled = true hostname = qualysapi.qg2.apps.qualys.com username = exampleuser password = examplepass -write_path=/opt/vulnwhisperer/data/qualys/ -db_path=/opt/vulnwhisperer/data/database +write_path=/opt/VulnWhisperer/data/qualys/ +db_path=/opt/VulnWhisperer/data/database verbose=true # Set the maximum number of retries each connection should attempt. @@ -60,8 +60,8 @@ hostname = api.detectify.com username = exampleuser #password variable used as secretKey password = examplepass -write_path =/opt/vulnwhisperer/data/detectify/ -db_path = /opt/vulnwhisperer/data/database +write_path =/opt/VulnWhisperer/data/detectify/ +db_path = /opt/VulnWhisperer/data/database verbose = true [openvas] @@ -70,8 +70,8 @@ hostname = localhost port = 4000 username = exampleuser password = examplepass -write_path=/opt/vulnwhisperer/data/openvas/ -db_path=/opt/vulnwhisperer/data/database +write_path=/opt/VulnWhisperer/data/openvas/ +db_path=/opt/VulnWhisperer/data/database verbose=true #[proxy] @@ -92,8 +92,8 @@ verbose=true hostname = jira-host username = username password = password -write_path = /opt/vulnwhisperer/data/jira/ -db_path = /opt/vulnwhisperer/data/database +write_path = /opt/VulnWhisperer/data/jira/ +db_path = /opt/VulnWhisperer/data/database verbose = true dns_resolv = False From bf5070f361a72859d63ca4f12c60a0fbb3fe8696 Mon Sep 17 00:00:00 2001 From: pemontto Date: Fri, 12 Apr 2019 17:55:59 +1000 Subject: [PATCH 72/96] fix vulnwhisperer image --- docker-compose-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 89e3ee4..d1f2e99 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -73,7 +73,7 @@ services: aliases: - logstash.local vulnwhisperer: - image: vulnwhisperer-1.8 + image: hasecuritysolutions/vulnwhisperer:latest container_name: vulnwhisperer entrypoint: [ "vuln_whisperer", From 3ecb26886a1e4ceda21fadf2b84a4ddfe55bcf77 Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 15 Apr 2019 12:43:47 +0200 Subject: [PATCH 73/96] added proxy config to instructions --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 58620df..6bfa906 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,12 @@ cd /path/to/VulnWhisperer python setup.py install ``` +**(Optional) If using a proxy, add proxy URL as environment variable to PATH** +```shell +export HTTP_PROXY=http://example.com:8080 +export HTTPS_PROXY=http://example.com:8080 +``` + Now you're ready to pull down scans. (see run section) Configuration From 1d92f71f9cdf9405d131490f09caec82452901f6 Mon Sep 17 00:00:00 2001 From: Quim Date: Mon, 15 Apr 2019 17:06:09 +0200 Subject: [PATCH 74/96] fix issue mentioned in #163, although not applied to ELK6 --- .../elasticsearch/logstash-vulnwhisperer-template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/elk5-old_compatibility/elasticsearch/logstash-vulnwhisperer-template.json b/resources/elk5-old_compatibility/elasticsearch/logstash-vulnwhisperer-template.json index 3292534..b9d1a15 100755 --- a/resources/elk5-old_compatibility/elasticsearch/logstash-vulnwhisperer-template.json +++ b/resources/elk5-old_compatibility/elasticsearch/logstash-vulnwhisperer-template.json @@ -53,7 +53,7 @@ ], "properties": { "plugin_id": { - "type": "integer" + "type": "float" }, "last_updated": { "type": "date" From 4d153ec7f26277a3e249d59b6639fecac96c7841 Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 09:57:20 +1000 Subject: [PATCH 75/96] Add index template to ES for docker --- docker-compose.v6.yml | 2 +- resources/elk6/init_kibana.sh | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/docker-compose.v6.yml b/docker-compose.v6.yml index f53aa0c..85e38cc 100644 --- a/docker-compose.v6.yml +++ b/docker-compose.v6.yml @@ -8,7 +8,6 @@ services: - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms1g -Xmx1g" - xpack.security.enabled=false - ulimits: memlock: soft: -1 @@ -46,6 +45,7 @@ services: volumes: - ./resources/elk6/init_kibana.sh:/opt/init_kibana.sh - ./resources/elk6/kibana_APIonly.json:/opt/kibana_APIonly.json + - ./resources/elk6/logstash-vulnwhisperer-template.json:/opt/index-template.json command: sh -c "apk add --no-cache curl bash && chmod +x /opt/init_kibana.sh && chmod +r /opt/kibana_APIonly.json && cd /opt/ && /bin/bash /opt/init_kibana.sh" # /opt/kibana_APIonly.json" networks: esnet: diff --git a/resources/elk6/init_kibana.sh b/resources/elk6/init_kibana.sh index ca23d74..be666aa 100755 --- a/resources/elk6/init_kibana.sh +++ b/resources/elk6/init_kibana.sh @@ -2,14 +2,28 @@ #kibana_url="localhost:5601" kibana_url="kibana.local:5601" -add_saved_objects="curl -u elastic:changeme -k -XPOST 'http://"$kibana_url"/api/saved_objects/_bulk_create' -H 'Content-Type: application/json' -H \"kbn-xsrf: true\" -d @" +elasticsearch_url="elasticsearch.local:9200" +add_saved_objects="curl -s -u elastic:changeme -k -XPOST 'http://"$kibana_url"/api/saved_objects/_bulk_create' -H 'Content-Type: application/json' -H \"kbn-xsrf: true\" -d @" #Create all saved objects - including index pattern saved_objects_file="kibana_APIonly.json" #if [ `curl -I localhost:5601/status | head -n1 |cut -d$' ' -f2` -eq '200' ]; then echo "Loading VulnWhisperer Saved Objects"; eval $(echo $add_saved_objects$saved_objects_file); else echo "waiting for kibana"; fi - -until [ "`curl -I "$kibana_url"/status | head -n1 |cut -d$' ' -f2`" == "200" ]; do + +until curl -s "$elasticsearch_url/_cluster/health?pretty" | grep '"status"' | grep -qE "green|yellow"; do + curl -s "$elasticsearch_url/_cluster/health?pretty" + echo "Waiting for Elasticsearch" + sleep 5 +done + +echo "Loading VulnWhisperer index template" +if curl -s --fail -XPUT "http://$elasticsearch_url/_template/vulnwhisperer" -H 'Content-Type: application/json' -d '@/opt/index-template.json'; then + echo -e "\nVulnWhisperer index template loaded successfully!" +else + echo -e "\nFAILED to load VulnWhisperer index template" +fi + +until [ "`curl -s -I "$kibana_url"/status | head -n1 |cut -d$' ' -f2`" == "200" ]; do curl -I "$kibana_url"/status echo "Waiting for Kibana" sleep 5 @@ -29,5 +43,4 @@ eval $(echo $add_saved_objects$saved_objects_file) #Create jira index pattern, separated for not fill of crap variables the Discover tab by default #index_name = "logstash-jira-*" -#os.system(add_index+index_name+"' '-d{\"attributes\":{\"title\":\""+index_name+"\",\"timeFieldName\":\"@timestamp\"}}'") - +#os.system(add_index+index_name+"' '-d{\"attributes\":{\"title\":\""+index_name+"\",\"timeFieldName\":\"@timestamp\"}}'") \ No newline at end of file From d9ab33d6c9fec2f3af0e582cd598cf1c8dc8418d Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 11:18:27 +1000 Subject: [PATCH 76/96] Set logstash and vw to use the same volume --- docker-compose-test.yml | 19 ++++++++++--------- docker-compose.v6.yml | 1 - 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index d1f2e99..9beb9fc 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -18,7 +18,8 @@ services: hard: 65536 mem_limit: 8g volumes: - - ./docker_data/esdata1:/usr/share/elasticsearch/data + - ./data/esdata1:/usr/share/elasticsearch/data + - ./data/es_snapshots:/snapshots ports: - 9200:9200 #restart: always @@ -37,8 +38,6 @@ services: - 5601:5601 depends_on: - elasticsearch - # volumes: - # - ./kibana-data: networks: esnet: aliases: @@ -50,7 +49,7 @@ services: volumes: - ./resources/elk6/init_kibana.sh:/opt/init_kibana.sh - ./resources/elk6/kibana_APIonly.json:/opt/kibana_APIonly.json - - ./docker_data/kibana_optimize:/usr/share/kibana/optimize + - ./resources/elk6/logstash-vulnwhisperer-template.json:/opt/index-template.json command: sh -c "apk add --no-cache curl bash && chmod +x /opt/init_kibana.sh && chmod +r /opt/kibana_APIonly.json && cd /opt/ && /bin/bash /opt/init_kibana.sh" # /opt/kibana_APIonly.json" networks: esnet: @@ -62,8 +61,8 @@ services: container_name: logstash volumes: - ./resources/elk6/pipeline/:/usr/share/logstash/pipeline - - ./docker_data/data/:/opt/VulnWhisperer/data - - ./resources/elk6/logstash.yml:/usr/share/logstash/config/logstash.yml + - ./data/vulnwhisperer/:/opt/VulnWhisperer/data + # - ./resources/elk6/logstash.yml:/usr/share/logstash/config/logstash.yml environment: - xpack.monitoring.enabled=false depends_on: @@ -73,10 +72,12 @@ services: aliases: - logstash.local vulnwhisperer: - image: hasecuritysolutions/vulnwhisperer:latest + # image: hasecuritysolutions/vulnwhisperer:latest + image: vulnwhisperer-local container_name: vulnwhisperer entrypoint: [ "vuln_whisperer", + "-F", "-c", "/opt/VulnWhisperer/vulnwhisperer.ini", "--mock", @@ -84,8 +85,8 @@ services: "/tests/data" ] volumes: - # - /opt/VulnWhisperer/data/:/opt/VulnWhisperer/data - - ./docker_data/data/:/opt/VulnWhisperer/data + - ./data/vulnwhisperer/:/opt/VulnWhisperer/data + # - ./resources/elk6/vulnwhisperer.ini:/opt/VulnWhisperer/vulnwhisperer.ini - ./configs/test.ini:/opt/VulnWhisperer/vulnwhisperer.ini - ./tests/data/:/tests/data network_mode: host diff --git a/docker-compose.v6.yml b/docker-compose.v6.yml index 85e38cc..ab570b7 100644 --- a/docker-compose.v6.yml +++ b/docker-compose.v6.yml @@ -75,7 +75,6 @@ services: "/opt/VulnWhisperer/vulnwhisperer.ini" ] volumes: - - /opt/VulnWhisperer/data/:/opt/VulnWhisperer/data - ./data/:/opt/VulnWhisperer/data - ./resources/elk6/vulnwhisperer.ini:/opt/VulnWhisperer/vulnwhisperer.ini network_mode: host From 3433231bb407f5f46897fa46966eec3ce3a3583f Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 11:30:27 +1000 Subject: [PATCH 77/96] Add initial ELK6 index template --- .../elk6/logstash-vulnwhisperer-template.json | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 resources/elk6/logstash-vulnwhisperer-template.json diff --git a/resources/elk6/logstash-vulnwhisperer-template.json b/resources/elk6/logstash-vulnwhisperer-template.json new file mode 100755 index 0000000..45cc8ae --- /dev/null +++ b/resources/elk6/logstash-vulnwhisperer-template.json @@ -0,0 +1,73 @@ +{ + "order": 0, + "index_patterns": "logstash-vulnwhisperer-*", + "mappings": { + "doc": { + "properties": { + "plugin_id": { + "type": "integer" + }, + "last_updated": { + "type": "date" + }, + "geoip": { + "dynamic": true, + "type": "object", + "properties": { + "ip": { + "type": "ip" + }, + "latitude": { + "type": "float" + }, + "location": { + "type": "geo_point" + }, + "longitude": { + "type": "float" + } + } + }, + "risk_score": { + "type": "float" + }, + "source": { + "type": "keyword" + }, + "synopsis": { + "type": "keyword" + }, + "see_also": { + "type": "keyword" + }, + "@timestamp": { + "type": "date" + }, + "cve": { + "type": "keyword" + }, + "solution": { + "type": "keyword" + }, + "port": { + "type": "integer" + }, + "host": { + "type": "text" + }, + "@version": { + "type": "keyword" + }, + "risk": { + "type": "keyword" + }, + "assign_ip": { + "type": "ip" + }, + "cvss": { + "type": "float" + } + } + } + } +} \ No newline at end of file From d67122a0995a57e9d7e1c7bf4bb14a88312d87a7 Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 14:40:07 +1000 Subject: [PATCH 78/96] Retry template installation a few times --- resources/elk6/init_kibana.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/resources/elk6/init_kibana.sh b/resources/elk6/init_kibana.sh index be666aa..eca079d 100755 --- a/resources/elk6/init_kibana.sh +++ b/resources/elk6/init_kibana.sh @@ -16,11 +16,17 @@ until curl -s "$elasticsearch_url/_cluster/health?pretty" | grep '"status"' | gr sleep 5 done -echo "Loading VulnWhisperer index template" -if curl -s --fail -XPUT "http://$elasticsearch_url/_template/vulnwhisperer" -H 'Content-Type: application/json' -d '@/opt/index-template.json'; then - echo -e "\nVulnWhisperer index template loaded successfully!" +count=0 +until curl -s --fail -XPUT "http://$elasticsearch_url/_template/vulnwhisperer" -H 'Content-Type: application/json' -d '@/opt/index-template.json'; do + echo "Loading VulnWhisperer index template..." + ((count++)) && ((count==60)) && break + sleep 1 +done + +if [[ count -le 60 && $(curl -s -I http://$elasticsearch_url/_template/vulnwhisperer | head -n1 |cut -d$' ' -f2) == "200" ]]; then + echo -e "\n✅ VulnWhisperer index template loaded" else - echo -e "\nFAILED to load VulnWhisperer index template" + echo -e "\n❌ VulnWhisperer index template failed to load" fi until [ "`curl -s -I "$kibana_url"/status | head -n1 |cut -d$' ' -f2`" == "200" ]; do From f7d47ae7539df4f2d342c6d0cdd823b0d0db19ec Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 14:41:14 +1000 Subject: [PATCH 79/96] update index template --- .../elk6/logstash-vulnwhisperer-template.json | 200 ++++++++++++++++-- 1 file changed, 180 insertions(+), 20 deletions(-) diff --git a/resources/elk6/logstash-vulnwhisperer-template.json b/resources/elk6/logstash-vulnwhisperer-template.json index 45cc8ae..946597f 100755 --- a/resources/elk6/logstash-vulnwhisperer-template.json +++ b/resources/elk6/logstash-vulnwhisperer-template.json @@ -1,15 +1,92 @@ { - "order": 0, "index_patterns": "logstash-vulnwhisperer-*", "mappings": { "doc": { "properties": { - "plugin_id": { - "type": "integer" - }, - "last_updated": { + "@timestamp": { "type": "date" }, + "@version": { + "type": "keyword" + }, + "asset": { + "type": "text", + "norms": false, + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "asset_uuid": { + "type": "keyword" + }, + "assign_ip": { + "type": "ip" + }, + "category": { + "type": "keyword" + }, + "cve": { + "type": "keyword" + }, + "cvss_base": { + "type": "float" + }, + "cvss_temporal_vector": { + "type": "keyword" + }, + "cvss_temporal": { + "type": "float" + }, + "cvss_vector": { + "type": "keyword" + }, + "cvss": { + "type": "float" + }, + "cvss3_base": { + "type": "float" + }, + "cvss3_temporal_vector": { + "type": "keyword" + }, + "cvss3_temporal": { + "type": "float" + }, + "cvss3_vector": { + "type": "keyword" + }, + "cvss3": { + "type": "float" + }, + "description": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "norms": false, + "type": "text" + }, + "dns": { + "type": "keyword" + }, + "exploitability": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "norms": false, + "type": "text" + }, + "fqdn": { + "type": "keyword" + }, "geoip": { "dynamic": true, "type": "object", @@ -28,44 +105,127 @@ } } }, - "risk_score": { - "type": "float" - }, - "source": { + "history_id": { "type": "keyword" }, - "synopsis": { + "host": { "type": "keyword" }, - "see_also": { + "host_end": { + "type": "date" + }, + "host_start": { + "type": "date" + }, + "impact": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "norms": false, + "type": "text" + }, + "ip_status": { "type": "keyword" }, - "@timestamp": { + "ip": { + "type": "ip" + }, + "last_updated": { "type": "date" }, - "cve": { + "operating_system": { "type": "keyword" }, - "solution": { + "path": { + "type": "keyword" + }, + "pci_vuln": { + "type": "keyword" + }, + "plugin_family": { + "type": "keyword" + }, + "plugin_id": { + "type": "keyword" + }, + "plugin_name": { "type": "keyword" }, + "plugin_output": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "norms": false, + "type": "text" + }, "port": { "type": "integer" }, - "host": { + "protocol": { + "type": "keyword" + }, + "results": { "type": "text" }, - "@version": { + "risk_number": { + "type": "integer" + }, + "risk_score_name": { "type": "keyword" }, + "risk_score": { + "type": "float" + }, "risk": { "type": "keyword" }, - "assign_ip": { - "type": "ip" + "scan_id": { + "type": "keyword" }, - "cvss": { - "type": "float" + "scan_name": { + "type": "keyword" + }, + "scan_reference": { + "type": "keyword" + }, + "see_also": { + "type": "keyword" + }, + "solution": { + "type": "keyword" + }, + "source": { + "type": "keyword" + }, + "ssl": { + "type": "keyword" + }, + "synopsis": { + "type": "keyword" + }, + "system_type": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "threat": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "vendor_reference": { + "type": "keyword" + }, + "vulnerability_state": { + "type": "keyword" } } } From e17ff42adb14bd1cf6162f2df0b43732326a62f5 Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 14:41:25 +1000 Subject: [PATCH 80/96] update kibana objects to match template --- resources/elk6/kibana.json | 803 +++++++++++++++-------------- resources/elk6/kibana_APIonly.json | 802 ++++++++++++++-------------- 2 files changed, 806 insertions(+), 799 deletions(-) diff --git a/resources/elk6/kibana.json b/resources/elk6/kibana.json index cd3ea2a..363a972 100644 --- a/resources/elk6/kibana.json +++ b/resources/elk6/kibana.json @@ -1,428 +1,433 @@ [ - { - "_id": "AWCUqesWib22Ai8JwW3u", - "_type": "dashboard", - "_source": { - "title": "VulnWhisperer - Risk Mitigation", - "hits": 0, - "description": "", - "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"20\",\"w\":8,\"x\":40,\"y\":15},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"21\",\"w\":12,\"x\":0,\"y\":35},\"id\":\"852816e0-3eb1-11e7-90cb-918f9cb01e3d\",\"panelIndex\":\"21\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"27\",\"w\":12,\"x\":12,\"y\":35},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"27\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"28\",\"w\":8,\"x\":32,\"y\":15},\"id\":\"35b6d320-3f7f-11e7-bd24-6903e3283192\",\"panelIndex\":\"28\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"30\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"31\",\"w\":8,\"x\":24,\"y\":35},\"id\":\"de1a5f40-3f85-11e7-97f9-3777d794626d\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"37\",\"w\":16,\"x\":16,\"y\":25},\"id\":\"5093c620-44e9-11e7-8014-ede06a7e69f8\",\"panelIndex\":\"37\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"columns\":[\"host\",\"risk\",\"risk_score\",\"cve\",\"plugin_name\",\"solution\",\"plugin_output\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":30,\"i\":\"38\",\"w\":48,\"x\":0,\"y\":65},\"id\":\"54648700-3f74-11e7-852e-69207a3d0726\",\"panelIndex\":\"38\",\"type\":\"search\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"39\",\"w\":16,\"x\":16,\"y\":15},\"id\":\"fb6eb020-49ab-11e7-8f8c-57ad64ec48a6\",\"panelIndex\":\"39\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"legendOpen\":true}},\"gridData\":{\"h\":20,\"i\":\"46\",\"w\":16,\"x\":0,\"y\":15},\"id\":\"56f0f5f0-3ebe-11e7-a192-93f36fbd9d05\",\"panelIndex\":\"46\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"47\",\"w\":9,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"47\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"48\",\"w\":10,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"48\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#E5AC0E\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"50\",\"w\":10,\"x\":20,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"50\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"51\",\"w\":10,\"x\":10,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"51\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", - "version": 1, - "timeRestore": false, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" - } + { + "_id": "AWCUqesWib22Ai8JwW3u", + "_type": "dashboard", + "_source": { + "title": "VulnWhisperer - Risk Mitigation", + "hits": 0, + "description": "", + "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"20\",\"w\":8,\"x\":40,\"y\":15},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"21\",\"w\":12,\"x\":0,\"y\":35},\"id\":\"852816e0-3eb1-11e7-90cb-918f9cb01e3d\",\"panelIndex\":\"21\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"27\",\"w\":12,\"x\":12,\"y\":35},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"27\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"28\",\"w\":8,\"x\":32,\"y\":15},\"id\":\"35b6d320-3f7f-11e7-bd24-6903e3283192\",\"panelIndex\":\"28\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"30\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"31\",\"w\":8,\"x\":24,\"y\":35},\"id\":\"de1a5f40-3f85-11e7-97f9-3777d794626d\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"37\",\"w\":16,\"x\":16,\"y\":25},\"id\":\"5093c620-44e9-11e7-8014-ede06a7e69f8\",\"panelIndex\":\"37\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"columns\":[\"host\",\"risk\",\"risk_score\",\"cve\",\"plugin_name\",\"solution\",\"plugin_output\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":30,\"i\":\"38\",\"w\":48,\"x\":0,\"y\":65},\"id\":\"54648700-3f74-11e7-852e-69207a3d0726\",\"panelIndex\":\"38\",\"type\":\"search\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"39\",\"w\":16,\"x\":16,\"y\":15},\"id\":\"fb6eb020-49ab-11e7-8f8c-57ad64ec48a6\",\"panelIndex\":\"39\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"legendOpen\":true}},\"gridData\":{\"h\":20,\"i\":\"46\",\"w\":16,\"x\":0,\"y\":15},\"id\":\"56f0f5f0-3ebe-11e7-a192-93f36fbd9d05\",\"panelIndex\":\"46\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"47\",\"w\":9,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"47\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"48\",\"w\":10,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"48\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#E5AC0E\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"50\",\"w\":10,\"x\":20,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"50\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"51\",\"w\":10,\"x\":10,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"51\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", + "version": 1, + "timeRestore": true, + "timeTo": "now", + "timeFrom": "now-30d", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" } - }, - { - "_id": "72051530-448e-11e7-a818-f5f80dfc3590", - "_type": "dashboard", - "_source": { - "title": "VulnWhisperer - Reporting", - "hits": 0, - "description": "", - "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"legendOpen\":false}},\"gridData\":{\"h\":20,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":56},\"id\":\"2f979030-44b9-11e7-a818-f5f80dfc3590\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"12\",\"w\":24,\"x\":0,\"y\":36},\"id\":\"8d9592d0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"12\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":16},\"id\":\"67d432e0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"14\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"15\",\"w\":12,\"x\":36,\"y\":36},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"15\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"20\",\"w\":12,\"x\":24,\"y\":36},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"22\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"22\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"29\",\"w\":24,\"x\":0,\"y\":16},\"id\":\"479deab0-8a39-11e7-a58a-9bfcb3761a3d\",\"panelIndex\":\"29\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"30\",\"w\":10,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#EAB839\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"31\",\"w\":9,\"x\":21,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"10 - 20\":\"#890F02\"},\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"32\",\"w\":11,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"32\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"33\",\"w\":10,\"x\":11,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"33\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", - "version": 1, - "timeRestore": true, - "timeTo": "now", - "timeFrom": "now-7d", - "refreshInterval": { - "pause": true, - "value": 0 - }, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" - } - } - }, - { - "_id": "4a6d9090-f66e-11e8-8f42-af2e41422cf8", - "_type": "index-pattern", - "_source": { - "title": "logstash-vulnwhisperer-*", - "timeFieldName": "@timestamp", - "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"IpPort\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"asset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"asset.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"assign_ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cve\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"history_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"history_id.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"last_updated\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"message_error\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"opcode\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"path.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"plugin_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_output\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"plugin_output.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"port\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"protocol.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"record_number\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_number\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"risk_number.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"risk_score_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"scan_id.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"scan_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"see_also\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"solution\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"synopsis\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_fingerprint\",\"type\":\"string\",\"count\":1,\"scripted\":true,\"script\":\"doc['asset.keyword']+'_'+doc['plugin_id']\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", - "fieldFormatMap": "{\"plugin_id\":{\"id\":\"number\",\"params\":{\"pattern\":\"00.[000]\"}}}" + } + }, + { + "_id": "72051530-448e-11e7-a818-f5f80dfc3590", + "_type": "dashboard", + "_source": { + "title": "VulnWhisperer - Reporting", + "hits": 0, + "description": "", + "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"legendOpen\":false}},\"gridData\":{\"h\":20,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":56},\"id\":\"2f979030-44b9-11e7-a818-f5f80dfc3590\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"12\",\"w\":24,\"x\":0,\"y\":36},\"id\":\"8d9592d0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"12\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":16},\"id\":\"67d432e0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"14\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"15\",\"w\":12,\"x\":36,\"y\":36},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"15\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"20\",\"w\":12,\"x\":24,\"y\":36},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"22\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"22\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"29\",\"w\":24,\"x\":0,\"y\":16},\"id\":\"479deab0-8a39-11e7-a58a-9bfcb3761a3d\",\"panelIndex\":\"29\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"30\",\"w\":10,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#EAB839\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"31\",\"w\":9,\"x\":21,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"10 - 20\":\"#890F02\"},\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"32\",\"w\":11,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"32\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"33\",\"w\":10,\"x\":11,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"33\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", + "version": 1, + "timeRestore": true, + "timeTo": "now", + "timeFrom": "now-30d", + "refreshInterval": { + "pause": true, + "value": 0 + }, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" } - }, - { - "_id": "159d2500-f773-11e8-8f42-af2e41422cf8", - "_type": "search", - "_source": { - "title": "VulnWhisperer - High Risk", - "description": "", - "hits": 0, - "columns": [ - "host", - "risk", - "risk_score", - "cve", - "plugin_name", - "solution", - "plugin_output" - ], - "sort": [ - "@timestamp", - "desc" - ], - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}}},\"filter\":[{\"meta\":{\"negate\":false,\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"type\":\"phrase\",\"key\":\"risk\",\"value\":\"High\",\"params\":{\"query\":\"High\",\"type\":\"phrase\"},\"disabled\":false,\"alias\":null},\"query\":{\"match\":{\"risk\":{\"query\":\"High\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647},\"highlightAll\":true,\"version\":true}" - } + } + }, + { + "_id": "159d2500-f773-11e8-8f42-af2e41422cf8", + "_type": "search", + "_source": { + "title": "VulnWhisperer - High Risk", + "description": "", + "hits": 0, + "columns": [ + "host", + "risk", + "risk_score", + "cve", + "plugin_name", + "solution", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}}},\"filter\":[{\"meta\":{\"negate\":false,\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"type\":\"phrase\",\"key\":\"risk\",\"value\":\"High\",\"params\":{\"query\":\"High\",\"type\":\"phrase\"},\"disabled\":false,\"alias\":null},\"query\":{\"match\":{\"risk\":{\"query\":\"High\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647},\"highlightAll\":true,\"version\":true}" } - }, - { - "_id": "54648700-3f74-11e7-852e-69207a3d0726", - "_type": "search", - "_source": { - "title": "VulnWhisperer - Saved Search", - "description": "", - "hits": 0, - "columns": [ - "host", - "risk", - "risk_score", - "cve", - "plugin_name", - "solution", - "plugin_output" - ], - "sort": [ - "@timestamp", - "desc" - ], - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647}}" - } + } + }, + { + "_id": "54648700-3f74-11e7-852e-69207a3d0726", + "_type": "search", + "_source": { + "title": "VulnWhisperer - Saved Search", + "description": "", + "hits": 0, + "columns": [ + "host", + "risk", + "risk_score", + "cve", + "plugin_name", + "solution", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647}}" } - }, - { - "_id": "41a7e430-fdb5-11e8-8f42-af2e41422cf8", - "_type": "search", - "_source": { - "title": "VulnWhisperer - Compliance", - "description": "", - "hits": 0, - "columns": [ - "plugin_id", - "cve", - "cvss", - "risk", - "asset", - "protocol", - "port", - "plugin_name", - "synopsis", - "description", - "solution", - "see_also", - "plugin_output" - ], - "sort": [ - "@timestamp", - "desc" - ], - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" - } + } + }, + { + "_id": "41a7e430-fdb5-11e8-8f42-af2e41422cf8", + "_type": "search", + "_source": { + "title": "VulnWhisperer - Compliance", + "description": "", + "hits": 0, + "columns": [ + "plugin_id", + "cve", + "cvss", + "risk", + "asset", + "protocol", + "port", + "plugin_name", + "synopsis", + "description", + "solution", + "see_also", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" } - }, - { - "_id": "465c5820-8977-11e7-857e-e1d56b17746d", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Critical Assets", - "visState": "{\"title\":\"VulnWhisperer - Critical Assets\",\"type\":\"heatmap\",\"params\":{\"addTooltip\":true,\"addLegend\":true,\"enableHover\":true,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Green to Red\",\"setColorRange\":true,\"colorsRange\":[{\"from\":0,\"to\":3},{\"from\":3,\"to\":7},{\"from\":7,\"to\":9},{\"from\":9,\"to\":11}],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"color\":\"white\"}}],\"type\":\"heatmap\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"risk_score\",\"customLabel\":\"Residual Risk Score\"}},{\"id\":\"2\",\"enabled\":false,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"risk_score\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"Date\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"asset.keyword\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Critical Asset\"}}],\"listeners\":{}}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 3\":\"rgb(0,104,55)\",\"3 - 7\":\"rgb(135,203,103)\",\"7 - 9\":\"rgb(255,255,190)\",\"9 - 11\":\"rgb(249,142,82)\"},\"colors\":{\"8 - 10\":\"#BF1B00\",\"9 - 11\":\"#BF1B00\",\"7 - 9\":\"#EF843C\",\"3 - 7\":\"#EAB839\",\"0 - 3\":\"#7EB26D\"},\"legendOpen\":false}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"index\":\"logstash-vulnwhisperer-*\",\"negate\":false,\"disabled\":false,\"alias\":\"Critical Asset\",\"type\":\"phrase\",\"key\":\"tags\",\"value\":\"critical_asset\"},\"query\":{\"match\":{\"tags\":{\"query\":\"critical_asset\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" - } + } + }, + { + "_id": "465c5820-8977-11e7-857e-e1d56b17746d", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Critical Assets", + "visState": "{\"title\":\"VulnWhisperer - Critical Assets\",\"type\":\"heatmap\",\"params\":{\"addTooltip\":true,\"addLegend\":true,\"enableHover\":true,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Green to Red\",\"setColorRange\":true,\"colorsRange\":[{\"from\":0,\"to\":3},{\"from\":3,\"to\":7},{\"from\":7,\"to\":9},{\"from\":9,\"to\":11}],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"color\":\"white\"}}],\"type\":\"heatmap\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"risk_score\",\"customLabel\":\"Residual Risk Score\"}},{\"id\":\"2\",\"enabled\":false,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"risk_score\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"Date\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"asset.keyword\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Critical Asset\"}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 3\":\"rgb(0,104,55)\",\"3 - 7\":\"rgb(135,203,103)\",\"7 - 9\":\"rgb(255,255,190)\",\"9 - 11\":\"rgb(249,142,82)\"},\"colors\":{\"8 - 10\":\"#BF1B00\",\"9 - 11\":\"#BF1B00\",\"7 - 9\":\"#EF843C\",\"3 - 7\":\"#EAB839\",\"0 - 3\":\"#7EB26D\"},\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"index\":\"logstash-vulnwhisperer-*\",\"negate\":false,\"disabled\":false,\"alias\":\"Critical Asset\",\"type\":\"phrase\",\"key\":\"tags\",\"value\":\"critical_asset\"},\"query\":{\"match\":{\"tags\":{\"query\":\"critical_asset\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" } - }, - { - "_id": "56f0f5f0-3ebe-11e7-a192-93f36fbd9d05", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer-RiskOverTime", - "visState": "{\"title\":\"VulnWhisperer-RiskOverTime\",\"type\":\"line\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per 12 hours\"},\"type\":\"category\"}],\"defaultYExtents\":false,\"drawLinesBetweenPoints\":true,\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"},\"valueAxis\":\"ValueAxis-1\"},\"interpolate\":\"linear\",\"legendPosition\":\"right\",\"orderBucketsBySum\":false,\"radiusRatio\":9,\"scale\":\"linear\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"interpolate\":\"linear\",\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"setYExtents\":false,\"showCircles\":true,\"times\":[],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"type\":\"line\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:info\"}}},\"label\":\"Info\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:low\"}}},\"label\":\"Low\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:medium\"}}},\"label\":\"Medium\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:high\"}}},\"label\":\"High\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:critical\"}}},\"label\":\"Critical\"}]}}],\"listeners\":{}}", - "uiStateJSON": "{\"vis\":{\"colors\":{\"Critical\":\"#962D82\",\"High\":\"#BF1B00\",\"Low\":\"#629E51\",\"Medium\":\"#EAB839\",\"Info\":\"#65C5DB\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "56f0f5f0-3ebe-11e7-a192-93f36fbd9d05", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer-RiskOverTime", + "visState": "{\"title\":\"VulnWhisperer-RiskOverTime\",\"type\":\"line\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per 12 hours\"},\"type\":\"category\"}],\"defaultYExtents\":false,\"drawLinesBetweenPoints\":true,\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"},\"valueAxis\":\"ValueAxis-1\"},\"interpolate\":\"linear\",\"legendPosition\":\"right\",\"orderBucketsBySum\":false,\"radiusRatio\":9,\"scale\":\"linear\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"interpolate\":\"linear\",\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"setYExtents\":false,\"showCircles\":true,\"times\":[],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"type\":\"line\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:info\"}}},\"label\":\"Info\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:low\"}}},\"label\":\"Low\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:medium\"}}},\"label\":\"Medium\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:high\"}}},\"label\":\"High\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:critical\"}}},\"label\":\"Critical\"}]}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"colors\":{\"Critical\":\"#962D82\",\"High\":\"#BF1B00\",\"Low\":\"#629E51\",\"Medium\":\"#EAB839\",\"Info\":\"#65C5DB\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "5093c620-44e9-11e7-8014-ede06a7e69f8", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Mitigation Readme", - "visState": "{\"title\":\"VulnWhisperer - Mitigation Readme\",\"type\":\"markdown\",\"params\":{\"markdown\":\"** Legend **\\n\\n* [Common Vulnerability Scoring System (CVSS)](https://nvd.nist.gov/vuln-metrics/cvss) is the NIST vulnerability scoring system\\n* Risk Number is residual risk score calculated from CVSS, which is adjusted to be specific to Heartland which accounts for services not in use such as Java and Flash\\n* Vulnerabilities by Tag are systems tagged with HIPAA and PCI identification.\\n\\n\\n** Workflow **\\n* Select 10.0 under Risk Number to identify Critical Vulnerabilities. \\n* For more information about a CVE, scroll down and click the CVE link.\\n* To filter by tags, use one of the following filters:\\n** tags:has_hipaa_data, tags:pci_asset, tags:hipaa_asset, tags:critical_asset**\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "5093c620-44e9-11e7-8014-ede06a7e69f8", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Mitigation Readme", + "visState": "{\"title\":\"VulnWhisperer - Mitigation Readme\",\"type\":\"markdown\",\"params\":{\"markdown\":\"** Legend **\\n\\n* [Common Vulnerability Scoring System (CVSS)](https://nvd.nist.gov/vuln-metrics/cvss) is the NIST vulnerability scoring system\\n* Risk Number is residual risk score calculated from CVSS, which is adjusted to be specific to Heartland which accounts for services not in use such as Java and Flash\\n* Vulnerabilities by Tag are systems tagged with HIPAA and PCI identification.\\n\\n\\n** Workflow **\\n* Select 10.0 under Risk Number to identify Critical Vulnerabilities. \\n* For more information about a CVE, scroll down and click the CVE link.\\n* To filter by tags, use one of the following filters:\\n** tags:has_hipaa_data, tags:pci_asset, tags:hipaa_asset, tags:critical_asset**\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "471a3580-3f6b-11e7-88e7-df1abe6547fb", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Vulnerabilities by Tag", - "visState": "{\"title\":\"VulnWhisperer - Vulnerabilities by Tag\",\"type\":\"table\",\"params\":{\"perPage\":3,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"bucket\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:has_hipaa_data\",\"analyze_wildcard\":true}}},\"label\":\"Systems with HIPAA data\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:pci_asset\",\"analyze_wildcard\":true}}},\"label\":\"PCI Systems\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:hipaa_asset\",\"analyze_wildcard\":true}}},\"label\":\"HIPAA Systems\"}]}}],\"listeners\":{}}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "471a3580-3f6b-11e7-88e7-df1abe6547fb", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Vulnerabilities by Tag", + "visState": "{\"title\":\"VulnWhisperer - Vulnerabilities by Tag\",\"type\":\"table\",\"params\":{\"perPage\":3,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"bucket\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:has_hipaa_data\",\"analyze_wildcard\":true}}},\"label\":\"Systems with HIPAA data\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:pci_asset\",\"analyze_wildcard\":true}}},\"label\":\"PCI Systems\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:hipaa_asset\",\"analyze_wildcard\":true}}},\"label\":\"HIPAA Systems\"}]}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "1de9e550-3df1-11e7-a44e-c79ca8efb780", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer-Description", - "visState": "{\"title\":\"VulnWhisperer-Description\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"description.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Description\"}}],\"listeners\":{}}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "1de9e550-3df1-11e7-a44e-c79ca8efb780", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer-Description", + "visState": "{\"title\":\"VulnWhisperer-Description\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"description.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Description\"}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "fb6eb020-49ab-11e7-8f8c-57ad64ec48a6", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Critical Risk Score for Tagged Assets", - "visState": "{\"title\":\"VulnWhisperer - Critical Risk Score for Tagged Assets\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:hipaa_asset').label(\\\"HIPAA Assets\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:pci_asset').label(\\\"PCI Systems\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:has_hipaa_data').label(\\\"Has HIPAA Data\\\")\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "fb6eb020-49ab-11e7-8f8c-57ad64ec48a6", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Critical Risk Score for Tagged Assets", + "visState": "{\"title\":\"VulnWhisperer - Critical Risk Score for Tagged Assets\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:hipaa_asset').label(\\\"HIPAA Assets\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:pci_asset').label(\\\"PCI Systems\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:has_hipaa_data').label(\\\"Has HIPAA Data\\\")\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "479deab0-8a39-11e7-a58a-9bfcb3761a3d", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - TL - TaggedAssetsPluginNames", - "visState": "{\"title\":\"VulnWhisperer - TL - TaggedAssetsPluginNames\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*', q='tags:critical_asset OR tags:hipaa_asset OR tags:pci_asset', split=\\\"plugin_name.keyword:10\\\").bars(width=4).label(regex=\\\".*:(.+)>.*\\\",label=\\\"$1\\\")\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "13c7d4e0-3df3-11e7-a44e-c79ca8efb780", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer-Solution", + "visState": "{\n \"title\": \"VulnWhisperer-Solution\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showMeticsAtAllLevels\": false,\n \"showPartialRows\": false,\n \"showTotal\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"totalFunc\": \"sum\"\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"count\",\n \"schema\": \"metric\",\n \"params\": {}\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"solution\",\n \"size\": 50,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"customLabel\": \"Solution\"\n }\n }\n ],\n \"listeners\": {}\n}", + "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"analyze_wildcard\": true,\n \"query\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } - }, - { - "_id": "13c7d4e0-3df3-11e7-a44e-c79ca8efb780", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer-Solution", - "visState": "{\n \"title\": \"VulnWhisperer-Solution\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showMeticsAtAllLevels\": false,\n \"showPartialRows\": false,\n \"showTotal\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"totalFunc\": \"sum\"\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"count\",\n \"schema\": \"metric\",\n \"params\": {}\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"solution\",\n \"size\": 50,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"customLabel\": \"Solution\"\n }\n }\n ],\n \"listeners\": {}\n}", - "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"analyze_wildcard\": true,\n \"query\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" - } + } + }, + { + "_id": "f9b68640-fda5-11e8-8f42-af2e41422cf8", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - AggTest", + "visState": "{\"title\":\"VulnWhisperer - AggTest\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"top_hits\",\"schema\":\"metric\",\"params\":{\"field\":\"@timestamp\",\"aggregate\":\"concat\",\"size\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":1000,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"plugin_id\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "e6b5b920-f77a-11e8-8f42-af2e41422cf8", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Risk: Low", - "visState": "{\"title\":\"VulnWhisperer - Risk: Low\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Greens\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":100}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Low\"},\"label\":\"Low Risk\"}]}}]}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "852816e0-3eb1-11e7-90cb-918f9cb01e3d", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer-CVSS", + "visState": "{\"title\":\"VulnWhisperer-CVSS\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Unique Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"cvss\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"CVSS Score\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"asset.keyword\",\"customLabel\":\"# of Assets\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "61b43c00-f77b-11e8-8f42-af2e41422cf8", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Risk: Medium", - "visState": "{\"title\":\"VulnWhisperer - Risk: Medium\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Blues\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10}],\"invertColors\":true,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Medium\"},\"label\":\"Medium Risk\"}]}}]}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "35b6d320-3f7f-11e7-bd24-6903e3283192", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Residual Risk", + "visState": "{\"title\":\"VulnWhisperer - Residual Risk\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Count\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"risk_score\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Risk Number\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "f9b68640-fda5-11e8-8f42-af2e41422cf8", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - AggTest", - "visState": "{\"title\":\"VulnWhisperer - AggTest\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"top_hits\",\"schema\":\"metric\",\"params\":{\"field\":\"@timestamp\",\"aggregate\":\"concat\",\"size\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":1000,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"plugin_id\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "995e2280-3df3-11e7-a44e-c79ca8efb780", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer-Asset", + "visState": "{\"title\":\"VulnWhisperer-Asset\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Asset\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "2f979030-44b9-11e7-a818-f5f80dfc3590", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - ScanBarChart", - "visState": "{\"title\":\"VulnWhisperer - ScanBarChart\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[],\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Unique count of scan_fingerprint\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Unique count of scan_fingerprint\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"plugin_name.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Scan Name\"}}]}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "67d432e0-44ec-11e7-a05f-d9719b331a27", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - TL-Critical Risk", + "visState": "{\"title\":\"VulnWhisperer - TL-Critical Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "8c9c9430-f77b-11e8-8f42-af2e41422cf8", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Risk: Critical", - "visState": "{\"title\":\"VulnWhisperer - Risk: Critical\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10},{\"from\":10,\"to\":20}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Critical\"},\"label\":\"Critical Risk\"}]}}]}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" - } + } + }, + { + "_id": "8d9592d0-44ec-11e7-a05f-d9719b331a27", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - TL-High Risk", + "visState": "{\"title\":\"VulnWhisperer - TL-High Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "c533c120-fe8c-11e8-8f42-af2e41422cf8", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Risk: High", - "visState": "{\"title\":\"VulnWhisperer - Risk: High\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":1,\"to\":5},{\"from\":5,\"to\":19999}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Risk: High\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:High\"},\"label\":\"risk: High\"}]}}]}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"1 - 5\":\"rgb(255,245,240)\",\"5 - 19999\":\"rgb(103,0,13)\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "2f979030-44b9-11e7-a818-f5f80dfc3590", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - ScanBarChart", + "visState": "{\n \"title\": \"VulnWhisperer - ScanBarChart\",\n \"type\": \"histogram\",\n \"params\": {\n \"addLegend\": true,\n \"addTimeMarker\": false,\n \"addTooltip\": true,\n \"defaultYExtents\": false,\n \"legendPosition\": \"right\",\n \"mode\": \"stacked\",\n \"scale\": \"linear\",\n \"setYExtents\": false,\n \"times\": [],\n \"type\": \"histogram\",\n \"grid\": {\n \"categoryLines\": false,\n \"style\": {\n \"color\": \"#eee\"\n }\n },\n \"categoryAxes\": [\n {\n \"id\": \"CategoryAxis-1\",\n \"type\": \"category\",\n \"position\": \"bottom\",\n \"show\": true,\n \"style\": {},\n \"scale\": {\n \"type\": \"linear\"\n },\n \"labels\": {\n \"show\": true,\n \"truncate\": 100\n },\n \"title\": {}\n }\n ],\n \"valueAxes\": [\n {\n \"id\": \"ValueAxis-1\",\n \"name\": \"LeftAxis-1\",\n \"type\": \"value\",\n \"position\": \"left\",\n \"show\": true,\n \"style\": {},\n \"scale\": {\n \"type\": \"linear\",\n \"mode\": \"normal\",\n \"setYExtents\": false,\n \"defaultYExtents\": false\n },\n \"labels\": {\n \"show\": true,\n \"rotate\": 0,\n \"filter\": false,\n \"truncate\": 100\n },\n \"title\": {\n \"text\": \"Unique count of scan_fingerprint\"\n }\n }\n ],\n \"seriesParams\": [\n {\n \"show\": \"true\",\n \"type\": \"histogram\",\n \"mode\": \"stacked\",\n \"data\": {\n \"label\": \"Unique count of scan_fingerprint\",\n \"id\": \"1\"\n },\n \"valueAxis\": \"ValueAxis-1\"\n }\n ]\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"cardinality\",\n \"schema\": \"metric\",\n \"params\": {\n \"field\": \"scan_fingerprint\"\n }\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"segment\",\n \"params\": {\n \"field\": \"plugin_name\",\n \"size\": 10,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"otherBucket\": false,\n \"otherBucketLabel\": \"Other\",\n \"missingBucket\": false,\n \"missingBucketLabel\": \"Missing\",\n \"customLabel\": \"Scan Name\"\n }\n }\n ]\n}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"analyze_wildcard\": true,\n \"query\": \"*\",\n \"default_field\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } - }, - { - "_id": "852816e0-3eb1-11e7-90cb-918f9cb01e3d", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer-CVSS", - "visState": "{\"title\":\"VulnWhisperer-CVSS\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Unique Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"cvss\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"CVSS Score\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"asset.keyword\",\"customLabel\":\"# of Assets\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "8c9c9430-f77b-11e8-8f42-af2e41422cf8", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Risk: Critical", + "visState": "{\"title\":\"VulnWhisperer - Risk: Critical\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10},{\"from\":10,\"to\":20}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk_score_name:critical\"},\"label\":\"Critical Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" } - }, - { - "_id": "35b6d320-3f7f-11e7-bd24-6903e3283192", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Residual Risk", - "visState": "{\"title\":\"VulnWhisperer - Residual Risk\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Count\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"risk_score\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Risk Number\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "e6b5b920-f77a-11e8-8f42-af2e41422cf8", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Risk: Low", + "visState": "{\"title\":\"VulnWhisperer - Risk: Low\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Greens\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":100}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk_score_name:low\"},\"label\":\"Low Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "de1a5f40-3f85-11e7-97f9-3777d794626d", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - ScanName", - "visState": "{\"title\":\"VulnWhisperer - ScanName\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"scan_name.keyword\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Scan Name\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "c533c120-fe8c-11e8-8f42-af2e41422cf8", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Risk: High", + "visState": "{\"title\":\"VulnWhisperer - Risk: High\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":1,\"to\":5},{\"from\":5,\"to\":19999}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Risk: High\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk_score_name:high\"},\"label\":\"risk: High\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"1 - 5\":\"rgb(255,245,240)\",\"5 - 19999\":\"rgb(103,0,13)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "995e2280-3df3-11e7-a44e-c79ca8efb780", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer-Asset", - "visState": "{\"title\":\"VulnWhisperer-Asset\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Asset\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "61b43c00-f77b-11e8-8f42-af2e41422cf8", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Risk: Medium", + "visState": "{\"title\":\"VulnWhisperer - Risk: Medium\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Blues\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10}],\"invertColors\":true,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk_score_name:medium\"},\"label\":\"Medium Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "_id": "297df800-3f7e-11e7-bd24-6903e3283192", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - Plugin Name", - "visState": "{\"title\":\"VulnWhisperer - Plugin Name\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Count\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"plugin_name.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Plugin Name\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "297df800-3f7e-11e7-bd24-6903e3283192", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - Plugin Name", + "visState": "{\n \"title\": \"VulnWhisperer - Plugin Name\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showPartialRows\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"showTotal\": false,\n \"totalFunc\": \"sum\",\n \"showMetricsAtAllLevels\": false\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"cardinality\",\n \"schema\": \"metric\",\n \"params\": {\n \"field\": \"scan_fingerprint\",\n \"customLabel\": \"Count\"\n }\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"plugin_name\",\n \"size\": 10,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"otherBucket\": false,\n \"otherBucketLabel\": \"Other\",\n \"missingBucket\": false,\n \"missingBucketLabel\": \"Missing\",\n \"customLabel\": \"Plugin Name\"\n }\n }\n ]\n}", + "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"query\": \"*\",\n \"analyze_wildcard\": true,\n \"default_field\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } - }, - { - "_id": "67d432e0-44ec-11e7-a05f-d9719b331a27", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - TL-Critical Risk", - "visState": "{\"title\":\"VulnWhisperer - TL-Critical Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "_id": "479deab0-8a39-11e7-a58a-9bfcb3761a3d", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - TL - TaggedAssetsPluginNames", + "visState": "{\n \"title\": \"VulnWhisperer - TL - TaggedAssetsPluginNames\",\n \"type\": \"timelion\",\n \"params\": {\n \"expression\": \".es(index='logstash-vulnwhisperer-*', q='tags:critical_asset OR tags:hipaa_asset OR tags:pci_asset', split=\\\"plugin_name:10\\\").bars(width=4).label(regex=\\\".*:(.+)>.*\\\",label=\\\"$1\\\")\",\n \"interval\": \"auto\"\n },\n \"aggs\": [],\n \"listeners\": {}\n}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"query\": \"*\",\n \"analyze_wildcard\": true\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } + } + }, + { + "_id": "4a6d9090-f66e-11e8-8f42-af2e41422cf8", + "_type": "index-pattern", + "_source": { + "title": "logstash-vulnwhisperer-*", + "timeFieldName": "@timestamp", + "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"asset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"asset.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"asset_uuid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"assign_ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"category\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cve\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3_base\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3_temporal\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3_temporal_vector\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3_vector\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss_base\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss_temporal\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss_temporal_vector\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss_vector\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"exploitability\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"exploitability.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"fqdn\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"history_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host_end\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host_start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"impact\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"impact.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip_status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"last_updated\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"operating_system\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pci_vuln\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_output\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"plugin_output.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"port\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"results\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"risk\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_number\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_reference\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"see_also\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"solution\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ssl\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"synopsis\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vendor_reference\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability_state\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_fingerprint\",\"type\":\"string\",\"count\":1,\"scripted\":true,\"script\":\"doc['asset.keyword']+'_'+doc['plugin_id']\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", + "fieldFormatMap": "{\"plugin_id\":{\"id\":\"number\",\"params\":{\"pattern\":\"00.[000]\"}}}" }, - { - "_id": "8d9592d0-44ec-11e7-a05f-d9719b331a27", - "_type": "visualization", - "_source": { - "title": "VulnWhisperer - TL-High Risk", - "visState": "{\"title\":\"VulnWhisperer - TL-High Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + "_migrationVersion": { + "index-pattern": "6.5.0" + } + }, + { + "_id": "de1a5f40-3f85-11e7-97f9-3777d794626d", + "_type": "visualization", + "_source": { + "title": "VulnWhisperer - ScanName", + "visState": "{\n \"title\": \"VulnWhisperer - ScanName\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showPartialRows\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"showTotal\": false,\n \"totalFunc\": \"sum\",\n \"showMetricsAtAllLevels\": false\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"count\",\n \"schema\": \"metric\",\n \"params\": {}\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"scan_name\",\n \"size\": 20,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"otherBucket\": false,\n \"otherBucketLabel\": \"Other\",\n \"missingBucket\": false,\n \"missingBucketLabel\": \"Missing\",\n \"customLabel\": \"Scan Name\"\n }\n }\n ]\n}", + "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"query\": \"*\",\n \"analyze_wildcard\": true,\n \"default_field\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } } - ] \ No newline at end of file + } +] \ No newline at end of file diff --git a/resources/elk6/kibana_APIonly.json b/resources/elk6/kibana_APIonly.json index 7a76a29..b42dd58 100755 --- a/resources/elk6/kibana_APIonly.json +++ b/resources/elk6/kibana_APIonly.json @@ -1,428 +1,430 @@ [ - { - "id": "AWCUqesWib22Ai8JwW3u", - "type": "dashboard", - "attributes": { - "title": "VulnWhisperer - Risk Mitigation", - "hits": 0, - "description": "", - "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"20\",\"w\":8,\"x\":40,\"y\":15},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"21\",\"w\":12,\"x\":0,\"y\":35},\"id\":\"852816e0-3eb1-11e7-90cb-918f9cb01e3d\",\"panelIndex\":\"21\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"27\",\"w\":12,\"x\":12,\"y\":35},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"27\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"28\",\"w\":8,\"x\":32,\"y\":15},\"id\":\"35b6d320-3f7f-11e7-bd24-6903e3283192\",\"panelIndex\":\"28\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"30\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"31\",\"w\":8,\"x\":24,\"y\":35},\"id\":\"de1a5f40-3f85-11e7-97f9-3777d794626d\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"37\",\"w\":16,\"x\":16,\"y\":25},\"id\":\"5093c620-44e9-11e7-8014-ede06a7e69f8\",\"panelIndex\":\"37\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"columns\":[\"host\",\"risk\",\"risk_score\",\"cve\",\"plugin_name\",\"solution\",\"plugin_output\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":30,\"i\":\"38\",\"w\":48,\"x\":0,\"y\":65},\"id\":\"54648700-3f74-11e7-852e-69207a3d0726\",\"panelIndex\":\"38\",\"type\":\"search\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"39\",\"w\":16,\"x\":16,\"y\":15},\"id\":\"fb6eb020-49ab-11e7-8f8c-57ad64ec48a6\",\"panelIndex\":\"39\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"legendOpen\":true}},\"gridData\":{\"h\":20,\"i\":\"46\",\"w\":16,\"x\":0,\"y\":15},\"id\":\"56f0f5f0-3ebe-11e7-a192-93f36fbd9d05\",\"panelIndex\":\"46\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"47\",\"w\":9,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"47\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"48\",\"w\":10,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"48\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#E5AC0E\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"50\",\"w\":10,\"x\":20,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"50\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"51\",\"w\":10,\"x\":10,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"51\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", - "version": 1, - "timeRestore": false, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" - } + { + "id": "AWCUqesWib22Ai8JwW3u", + "type": "dashboard", + "attributes": { + "title": "VulnWhisperer - Risk Mitigation", + "hits": 0, + "description": "", + "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"20\",\"w\":8,\"x\":40,\"y\":15},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"21\",\"w\":12,\"x\":0,\"y\":35},\"id\":\"852816e0-3eb1-11e7-90cb-918f9cb01e3d\",\"panelIndex\":\"21\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":30,\"i\":\"27\",\"w\":12,\"x\":12,\"y\":35},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"27\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}},\"gridData\":{\"h\":30,\"i\":\"28\",\"w\":8,\"x\":32,\"y\":15},\"id\":\"35b6d320-3f7f-11e7-bd24-6903e3283192\",\"panelIndex\":\"28\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"30\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"31\",\"w\":8,\"x\":24,\"y\":35},\"id\":\"de1a5f40-3f85-11e7-97f9-3777d794626d\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"37\",\"w\":16,\"x\":16,\"y\":25},\"id\":\"5093c620-44e9-11e7-8014-ede06a7e69f8\",\"panelIndex\":\"37\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"columns\":[\"host\",\"risk\",\"risk_score\",\"cve\",\"plugin_name\",\"solution\",\"plugin_output\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":30,\"i\":\"38\",\"w\":48,\"x\":0,\"y\":65},\"id\":\"54648700-3f74-11e7-852e-69207a3d0726\",\"panelIndex\":\"38\",\"type\":\"search\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":10,\"i\":\"39\",\"w\":16,\"x\":16,\"y\":15},\"id\":\"fb6eb020-49ab-11e7-8f8c-57ad64ec48a6\",\"panelIndex\":\"39\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"legendOpen\":true}},\"gridData\":{\"h\":20,\"i\":\"46\",\"w\":16,\"x\":0,\"y\":15},\"id\":\"56f0f5f0-3ebe-11e7-a192-93f36fbd9d05\",\"panelIndex\":\"46\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"47\",\"w\":9,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"47\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"48\",\"w\":10,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"48\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#E5AC0E\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":15,\"i\":\"50\",\"w\":10,\"x\":20,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"50\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"51\",\"w\":10,\"x\":10,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"51\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", + "version": 1, + "timeRestore": true, + "timeTo": "now", + "timeFrom": "now-30d", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" } - }, - { - "id": "72051530-448e-11e7-a818-f5f80dfc3590", - "type": "dashboard", - "attributes": { - "title": "VulnWhisperer - Reporting", - "hits": 0, - "description": "", - "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"legendOpen\":false}},\"gridData\":{\"h\":20,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":56},\"id\":\"2f979030-44b9-11e7-a818-f5f80dfc3590\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"12\",\"w\":24,\"x\":0,\"y\":36},\"id\":\"8d9592d0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"12\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":16},\"id\":\"67d432e0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"14\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"15\",\"w\":12,\"x\":36,\"y\":36},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"15\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"20\",\"w\":12,\"x\":24,\"y\":36},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"22\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"22\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"29\",\"w\":24,\"x\":0,\"y\":16},\"id\":\"479deab0-8a39-11e7-a58a-9bfcb3761a3d\",\"panelIndex\":\"29\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"30\",\"w\":10,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#EAB839\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"31\",\"w\":9,\"x\":21,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"10 - 20\":\"#890F02\"},\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"32\",\"w\":11,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"32\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"33\",\"w\":10,\"x\":11,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"33\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", - "version": 1, - "timeRestore": true, - "timeTo": "now", - "timeFrom": "now-7d", - "refreshInterval": { - "pause": true, - "value": 0 - }, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" - } - } - }, - { - "id": "4a6d9090-f66e-11e8-8f42-af2e41422cf8", - "type": "index-pattern", - "attributes": { - "title": "logstash-vulnwhisperer-*", - "timeFieldName": "@timestamp", - "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"IpPort\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"asset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"asset.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"assign_ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cve\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination_geo.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"history_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"history_id.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"last_updated\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"message_error\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"opcode\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"path.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"plugin_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_output\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"plugin_output.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"port\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"protocol.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"record_number\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_number\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"risk_number.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"risk_score_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"scan_id.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"scan_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"see_also\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"solution\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source_geo.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"synopsis\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_fingerprint\",\"type\":\"string\",\"count\":1,\"scripted\":true,\"script\":\"doc['asset.keyword']+'_'+doc['plugin_id']\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", - "fieldFormatMap": "{\"plugin_id\":{\"id\":\"number\",\"params\":{\"pattern\":\"00.[000]\"}}}" + } + }, + { + "id": "72051530-448e-11e7-a818-f5f80dfc3590", + "type": "dashboard", + "attributes": { + "title": "VulnWhisperer - Reporting", + "hits": 0, + "description": "", + "panelsJSON": "[{\"embeddableConfig\":{\"vis\":{\"legendOpen\":false}},\"gridData\":{\"h\":20,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":56},\"id\":\"2f979030-44b9-11e7-a818-f5f80dfc3590\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"12\",\"w\":24,\"x\":0,\"y\":36},\"id\":\"8d9592d0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"12\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":16},\"id\":\"67d432e0-44ec-11e7-a05f-d9719b331a27\",\"panelIndex\":\"14\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"15\",\"w\":12,\"x\":36,\"y\":36},\"id\":\"297df800-3f7e-11e7-bd24-6903e3283192\",\"panelIndex\":\"15\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":20,\"i\":\"20\",\"w\":12,\"x\":24,\"y\":36},\"id\":\"471a3580-3f6b-11e7-88e7-df1abe6547fb\",\"panelIndex\":\"20\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"h\":15,\"i\":\"22\",\"w\":8,\"x\":40,\"y\":0},\"id\":\"995e2280-3df3-11e7-a44e-c79ca8efb780\",\"panelIndex\":\"22\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"gridData\":{\"h\":20,\"i\":\"29\",\"w\":24,\"x\":0,\"y\":16},\"id\":\"479deab0-8a39-11e7-a58a-9bfcb3761a3d\",\"panelIndex\":\"29\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"30\",\"w\":10,\"x\":30,\"y\":0},\"id\":\"e6b5b920-f77a-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"30\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 10\":\"#EAB839\"},\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"31\",\"w\":9,\"x\":21,\"y\":0},\"id\":\"61b43c00-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"31\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"10 - 20\":\"#890F02\"},\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"},\"legendOpen\":false}},\"gridData\":{\"h\":16,\"i\":\"32\",\"w\":11,\"x\":0,\"y\":0},\"id\":\"8c9c9430-f77b-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"32\",\"type\":\"visualization\",\"version\":\"6.4.3\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"33\",\"w\":10,\"x\":11,\"y\":0},\"id\":\"c533c120-fe8c-11e8-8f42-af2e41422cf8\",\"panelIndex\":\"33\",\"type\":\"visualization\",\"version\":\"6.4.3\"}]", + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":false}", + "version": 1, + "timeRestore": true, + "timeTo": "now", + "timeFrom": "now-30d", + "refreshInterval": { + "pause": true, + "value": 0 + }, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":{\"match_all\":{}}}}" } - }, - { - "id": "159d2500-f773-11e8-8f42-af2e41422cf8", - "type": "search", - "attributes": { - "title": "VulnWhisperer - High Risk", - "description": "", - "hits": 0, - "columns": [ - "host", - "risk", - "risk_score", - "cve", - "plugin_name", - "solution", - "plugin_output" - ], - "sort": [ - "@timestamp", - "desc" - ], - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}}},\"filter\":[{\"meta\":{\"negate\":false,\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"type\":\"phrase\",\"key\":\"risk\",\"value\":\"High\",\"params\":{\"query\":\"High\",\"type\":\"phrase\"},\"disabled\":false,\"alias\":null},\"query\":{\"match\":{\"risk\":{\"query\":\"High\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647},\"highlightAll\":true,\"version\":true}" - } + } + }, + { + "id": "159d2500-f773-11e8-8f42-af2e41422cf8", + "type": "search", + "attributes": { + "title": "VulnWhisperer - High Risk", + "description": "", + "hits": 0, + "columns": [ + "host", + "risk", + "risk_score", + "cve", + "plugin_name", + "solution", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}}},\"filter\":[{\"meta\":{\"negate\":false,\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"type\":\"phrase\",\"key\":\"risk\",\"value\":\"High\",\"params\":{\"query\":\"High\",\"type\":\"phrase\"},\"disabled\":false,\"alias\":null},\"query\":{\"match\":{\"risk\":{\"query\":\"High\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647},\"highlightAll\":true,\"version\":true}" } - }, - { - "id": "54648700-3f74-11e7-852e-69207a3d0726", - "type": "search", - "attributes": { - "title": "VulnWhisperer - Saved Search", - "description": "", - "hits": 0, - "columns": [ - "host", - "risk", - "risk_score", - "cve", - "plugin_name", - "solution", - "plugin_output" - ], - "sort": [ - "@timestamp", - "desc" - ], - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647}}" - } + } + }, + { + "id": "54648700-3f74-11e7-852e-69207a3d0726", + "type": "search", + "attributes": { + "title": "VulnWhisperer - Saved Search", + "description": "", + "hits": 0, + "columns": [ + "host", + "risk", + "risk_score", + "cve", + "plugin_name", + "solution", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"require_field_match\":false,\"fragment_size\":2147483647}}" } - }, - { - "id": "41a7e430-fdb5-11e8-8f42-af2e41422cf8", - "type": "search", - "attributes": { - "title": "VulnWhisperer - Compliance", - "description": "", - "hits": 0, - "columns": [ - "plugin_id", - "cve", - "cvss", - "risk", - "asset", - "protocol", - "port", - "plugin_name", - "synopsis", - "description", - "solution", - "see_also", - "plugin_output" - ], - "sort": [ - "@timestamp", - "desc" - ], - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" - } + } + }, + { + "id": "41a7e430-fdb5-11e8-8f42-af2e41422cf8", + "type": "search", + "attributes": { + "title": "VulnWhisperer - Compliance", + "description": "", + "hits": 0, + "columns": [ + "plugin_id", + "cve", + "cvss", + "risk", + "asset", + "protocol", + "port", + "plugin_name", + "synopsis", + "description", + "solution", + "see_also", + "plugin_output" + ], + "sort": [ + "@timestamp", + "desc" + ], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" } - }, - { - "id": "465c5820-8977-11e7-857e-e1d56b17746d", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Critical Assets", - "visState": "{\"title\":\"VulnWhisperer - Critical Assets\",\"type\":\"heatmap\",\"params\":{\"addTooltip\":true,\"addLegend\":true,\"enableHover\":true,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Green to Red\",\"setColorRange\":true,\"colorsRange\":[{\"from\":0,\"to\":3},{\"from\":3,\"to\":7},{\"from\":7,\"to\":9},{\"from\":9,\"to\":11}],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"color\":\"white\"}}],\"type\":\"heatmap\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"risk_score\",\"customLabel\":\"Residual Risk Score\"}},{\"id\":\"2\",\"enabled\":false,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"risk_score\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"Date\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"asset.keyword\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Critical Asset\"}}],\"listeners\":{}}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 3\":\"rgb(0,104,55)\",\"3 - 7\":\"rgb(135,203,103)\",\"7 - 9\":\"rgb(255,255,190)\",\"9 - 11\":\"rgb(249,142,82)\"},\"colors\":{\"8 - 10\":\"#BF1B00\",\"9 - 11\":\"#BF1B00\",\"7 - 9\":\"#EF843C\",\"3 - 7\":\"#EAB839\",\"0 - 3\":\"#7EB26D\"},\"legendOpen\":false}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"index\":\"logstash-vulnwhisperer-*\",\"negate\":false,\"disabled\":false,\"alias\":\"Critical Asset\",\"type\":\"phrase\",\"key\":\"tags\",\"value\":\"critical_asset\"},\"query\":{\"match\":{\"tags\":{\"query\":\"critical_asset\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" - } + } + }, + { + "id": "465c5820-8977-11e7-857e-e1d56b17746d", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Critical Assets", + "visState": "{\"title\":\"VulnWhisperer - Critical Assets\",\"type\":\"heatmap\",\"params\":{\"addTooltip\":true,\"addLegend\":true,\"enableHover\":true,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Green to Red\",\"setColorRange\":true,\"colorsRange\":[{\"from\":0,\"to\":3},{\"from\":3,\"to\":7},{\"from\":7,\"to\":9},{\"from\":9,\"to\":11}],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"color\":\"white\"}}],\"type\":\"heatmap\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"risk_score\",\"customLabel\":\"Residual Risk Score\"}},{\"id\":\"2\",\"enabled\":false,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"risk_score\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"Date\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"asset.keyword\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Critical Asset\"}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 3\":\"rgb(0,104,55)\",\"3 - 7\":\"rgb(135,203,103)\",\"7 - 9\":\"rgb(255,255,190)\",\"9 - 11\":\"rgb(249,142,82)\"},\"colors\":{\"8 - 10\":\"#BF1B00\",\"9 - 11\":\"#BF1B00\",\"7 - 9\":\"#EF843C\",\"3 - 7\":\"#EAB839\",\"0 - 3\":\"#7EB26D\"},\"legendOpen\":false}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"index\":\"logstash-vulnwhisperer-*\",\"negate\":false,\"disabled\":false,\"alias\":\"Critical Asset\",\"type\":\"phrase\",\"key\":\"tags\",\"value\":\"critical_asset\"},\"query\":{\"match\":{\"tags\":{\"query\":\"critical_asset\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" } - }, - { - "id": "56f0f5f0-3ebe-11e7-a192-93f36fbd9d05", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer-RiskOverTime", - "visState": "{\"title\":\"VulnWhisperer-RiskOverTime\",\"type\":\"line\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per 12 hours\"},\"type\":\"category\"}],\"defaultYExtents\":false,\"drawLinesBetweenPoints\":true,\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"},\"valueAxis\":\"ValueAxis-1\"},\"interpolate\":\"linear\",\"legendPosition\":\"right\",\"orderBucketsBySum\":false,\"radiusRatio\":9,\"scale\":\"linear\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"interpolate\":\"linear\",\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"setYExtents\":false,\"showCircles\":true,\"times\":[],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"type\":\"line\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:info\"}}},\"label\":\"Info\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:low\"}}},\"label\":\"Low\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:medium\"}}},\"label\":\"Medium\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:high\"}}},\"label\":\"High\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:critical\"}}},\"label\":\"Critical\"}]}}],\"listeners\":{}}", - "uiStateJSON": "{\"vis\":{\"colors\":{\"Critical\":\"#962D82\",\"High\":\"#BF1B00\",\"Low\":\"#629E51\",\"Medium\":\"#EAB839\",\"Info\":\"#65C5DB\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "56f0f5f0-3ebe-11e7-a192-93f36fbd9d05", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-RiskOverTime", + "visState": "{\"title\":\"VulnWhisperer-RiskOverTime\",\"type\":\"line\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per 12 hours\"},\"type\":\"category\"}],\"defaultYExtents\":false,\"drawLinesBetweenPoints\":true,\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"},\"valueAxis\":\"ValueAxis-1\"},\"interpolate\":\"linear\",\"legendPosition\":\"right\",\"orderBucketsBySum\":false,\"radiusRatio\":9,\"scale\":\"linear\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"interpolate\":\"linear\",\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"setYExtents\":false,\"showCircles\":true,\"times\":[],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"type\":\"line\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:info\"}}},\"label\":\"Info\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:low\"}}},\"label\":\"Low\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:medium\"}}},\"label\":\"Medium\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:high\"}}},\"label\":\"High\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"risk_score_name:critical\"}}},\"label\":\"Critical\"}]}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"colors\":{\"Critical\":\"#962D82\",\"High\":\"#BF1B00\",\"Low\":\"#629E51\",\"Medium\":\"#EAB839\",\"Info\":\"#65C5DB\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "5093c620-44e9-11e7-8014-ede06a7e69f8", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Mitigation Readme", - "visState": "{\"title\":\"VulnWhisperer - Mitigation Readme\",\"type\":\"markdown\",\"params\":{\"markdown\":\"** Legend **\\n\\n* [Common Vulnerability Scoring System (CVSS)](https://nvd.nist.gov/vuln-metrics/cvss) is the NIST vulnerability scoring system\\n* Risk Number is residual risk score calculated from CVSS, which is adjusted to be specific to Heartland which accounts for services not in use such as Java and Flash\\n* Vulnerabilities by Tag are systems tagged with HIPAA and PCI identification.\\n\\n\\n** Workflow **\\n* Select 10.0 under Risk Number to identify Critical Vulnerabilities. \\n* For more information about a CVE, scroll down and click the CVE link.\\n* To filter by tags, use one of the following filters:\\n** tags:has_hipaa_data, tags:pci_asset, tags:hipaa_asset, tags:critical_asset**\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "5093c620-44e9-11e7-8014-ede06a7e69f8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Mitigation Readme", + "visState": "{\"title\":\"VulnWhisperer - Mitigation Readme\",\"type\":\"markdown\",\"params\":{\"markdown\":\"** Legend **\\n\\n* [Common Vulnerability Scoring System (CVSS)](https://nvd.nist.gov/vuln-metrics/cvss) is the NIST vulnerability scoring system\\n* Risk Number is residual risk score calculated from CVSS, which is adjusted to be specific to Heartland which accounts for services not in use such as Java and Flash\\n* Vulnerabilities by Tag are systems tagged with HIPAA and PCI identification.\\n\\n\\n** Workflow **\\n* Select 10.0 under Risk Number to identify Critical Vulnerabilities. \\n* For more information about a CVE, scroll down and click the CVE link.\\n* To filter by tags, use one of the following filters:\\n** tags:has_hipaa_data, tags:pci_asset, tags:hipaa_asset, tags:critical_asset**\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "471a3580-3f6b-11e7-88e7-df1abe6547fb", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Vulnerabilities by Tag", - "visState": "{\"title\":\"VulnWhisperer - Vulnerabilities by Tag\",\"type\":\"table\",\"params\":{\"perPage\":3,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"bucket\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:has_hipaa_data\",\"analyze_wildcard\":true}}},\"label\":\"Systems with HIPAA data\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:pci_asset\",\"analyze_wildcard\":true}}},\"label\":\"PCI Systems\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:hipaa_asset\",\"analyze_wildcard\":true}}},\"label\":\"HIPAA Systems\"}]}}],\"listeners\":{}}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "471a3580-3f6b-11e7-88e7-df1abe6547fb", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Vulnerabilities by Tag", + "visState": "{\"title\":\"VulnWhisperer - Vulnerabilities by Tag\",\"type\":\"table\",\"params\":{\"perPage\":3,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"bucket\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:has_hipaa_data\",\"analyze_wildcard\":true}}},\"label\":\"Systems with HIPAA data\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:pci_asset\",\"analyze_wildcard\":true}}},\"label\":\"PCI Systems\"},{\"input\":{\"query\":{\"query_string\":{\"query\":\"tags:hipaa_asset\",\"analyze_wildcard\":true}}},\"label\":\"HIPAA Systems\"}]}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "1de9e550-3df1-11e7-a44e-c79ca8efb780", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer-Description", - "visState": "{\"title\":\"VulnWhisperer-Description\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"description.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Description\"}}],\"listeners\":{}}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "1de9e550-3df1-11e7-a44e-c79ca8efb780", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-Description", + "visState": "{\"title\":\"VulnWhisperer-Description\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"description.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Description\"}}],\"listeners\":{}}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "fb6eb020-49ab-11e7-8f8c-57ad64ec48a6", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Critical Risk Score for Tagged Assets", - "visState": "{\"title\":\"VulnWhisperer - Critical Risk Score for Tagged Assets\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:hipaa_asset').label(\\\"HIPAA Assets\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:pci_asset').label(\\\"PCI Systems\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:has_hipaa_data').label(\\\"Has HIPAA Data\\\")\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "fb6eb020-49ab-11e7-8f8c-57ad64ec48a6", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Critical Risk Score for Tagged Assets", + "visState": "{\"title\":\"VulnWhisperer - Critical Risk Score for Tagged Assets\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:hipaa_asset').label(\\\"HIPAA Assets\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:pci_asset').label(\\\"PCI Systems\\\"),.es(index=logstash-vulnwhisperer-*,q='risk_score:>9 AND tags:has_hipaa_data').label(\\\"Has HIPAA Data\\\")\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "479deab0-8a39-11e7-a58a-9bfcb3761a3d", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - TL - TaggedAssetsPluginNames", - "visState": "{\"title\":\"VulnWhisperer - TL - TaggedAssetsPluginNames\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*', q='tags:critical_asset OR tags:hipaa_asset OR tags:pci_asset', split=\\\"plugin_name.keyword:10\\\").bars(width=4).label(regex=\\\".*:(.+)>.*\\\",label=\\\"$1\\\")\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "13c7d4e0-3df3-11e7-a44e-c79ca8efb780", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-Solution", + "visState": "{\n \"title\": \"VulnWhisperer-Solution\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showMeticsAtAllLevels\": false,\n \"showPartialRows\": false,\n \"showTotal\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"totalFunc\": \"sum\"\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"count\",\n \"schema\": \"metric\",\n \"params\": {}\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"solution\",\n \"size\": 50,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"customLabel\": \"Solution\"\n }\n }\n ],\n \"listeners\": {}\n}", + "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"analyze_wildcard\": true,\n \"query\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } - }, - { - "id": "13c7d4e0-3df3-11e7-a44e-c79ca8efb780", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer-Solution", - "visState": "{\n \"title\": \"VulnWhisperer-Solution\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showMeticsAtAllLevels\": false,\n \"showPartialRows\": false,\n \"showTotal\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"totalFunc\": \"sum\"\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"count\",\n \"schema\": \"metric\",\n \"params\": {}\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"solution\",\n \"size\": 50,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"customLabel\": \"Solution\"\n }\n }\n ],\n \"listeners\": {}\n}", - "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"analyze_wildcard\": true,\n \"query\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" - } + } + }, + { + "id": "f9b68640-fda5-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - AggTest", + "visState": "{\"title\":\"VulnWhisperer - AggTest\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"top_hits\",\"schema\":\"metric\",\"params\":{\"field\":\"@timestamp\",\"aggregate\":\"concat\",\"size\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":1000,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"plugin_id\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "e6b5b920-f77a-11e8-8f42-af2e41422cf8", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Risk: Low", - "visState": "{\"title\":\"VulnWhisperer - Risk: Low\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Greens\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":100}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Low\"},\"label\":\"Low Risk\"}]}}]}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "852816e0-3eb1-11e7-90cb-918f9cb01e3d", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-CVSS", + "visState": "{\"title\":\"VulnWhisperer-CVSS\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Unique Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"cvss\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"CVSS Score\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"asset.keyword\",\"customLabel\":\"# of Assets\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "61b43c00-f77b-11e8-8f42-af2e41422cf8", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Risk: Medium", - "visState": "{\"title\":\"VulnWhisperer - Risk: Medium\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Blues\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10}],\"invertColors\":true,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Medium\"},\"label\":\"Medium Risk\"}]}}]}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "35b6d320-3f7f-11e7-bd24-6903e3283192", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Residual Risk", + "visState": "{\"title\":\"VulnWhisperer - Residual Risk\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Count\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"risk_score\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Risk Number\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "f9b68640-fda5-11e8-8f42-af2e41422cf8", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - AggTest", - "visState": "{\"title\":\"VulnWhisperer - AggTest\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"top_hits\",\"schema\":\"metric\",\"params\":{\"field\":\"@timestamp\",\"aggregate\":\"concat\",\"size\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":1000,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"plugin_id\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "995e2280-3df3-11e7-a44e-c79ca8efb780", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer-Asset", + "visState": "{\"title\":\"VulnWhisperer-Asset\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Asset\"}}]}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "2f979030-44b9-11e7-a818-f5f80dfc3590", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - ScanBarChart", - "visState": "{\"title\":\"VulnWhisperer - ScanBarChart\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[],\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Unique count of scan_fingerprint\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Unique count of scan_fingerprint\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"plugin_name.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Scan Name\"}}]}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "67d432e0-44ec-11e7-a05f-d9719b331a27", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - TL-Critical Risk", + "visState": "{\"title\":\"VulnWhisperer - TL-Critical Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "8c9c9430-f77b-11e8-8f42-af2e41422cf8", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Risk: Critical", - "visState": "{\"title\":\"VulnWhisperer - Risk: Critical\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10},{\"from\":10,\"to\":20}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:Critical\"},\"label\":\"Critical Risk\"}]}}]}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" - } + } + }, + { + "id": "8d9592d0-44ec-11e7-a05f-d9719b331a27", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - TL-High Risk", + "visState": "{\"title\":\"VulnWhisperer - TL-High Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "c533c120-fe8c-11e8-8f42-af2e41422cf8", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Risk: High", - "visState": "{\"title\":\"VulnWhisperer - Risk: High\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":1,\"to\":5},{\"from\":5,\"to\":19999}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Risk: High\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk:High\"},\"label\":\"risk: High\"}]}}]}", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"1 - 5\":\"rgb(255,245,240)\",\"5 - 19999\":\"rgb(103,0,13)\"}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "2f979030-44b9-11e7-a818-f5f80dfc3590", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - ScanBarChart", + "visState": "{\n \"title\": \"VulnWhisperer - ScanBarChart\",\n \"type\": \"histogram\",\n \"params\": {\n \"addLegend\": true,\n \"addTimeMarker\": false,\n \"addTooltip\": true,\n \"defaultYExtents\": false,\n \"legendPosition\": \"right\",\n \"mode\": \"stacked\",\n \"scale\": \"linear\",\n \"setYExtents\": false,\n \"times\": [],\n \"type\": \"histogram\",\n \"grid\": {\n \"categoryLines\": false,\n \"style\": {\n \"color\": \"#eee\"\n }\n },\n \"categoryAxes\": [\n {\n \"id\": \"CategoryAxis-1\",\n \"type\": \"category\",\n \"position\": \"bottom\",\n \"show\": true,\n \"style\": {},\n \"scale\": {\n \"type\": \"linear\"\n },\n \"labels\": {\n \"show\": true,\n \"truncate\": 100\n },\n \"title\": {}\n }\n ],\n \"valueAxes\": [\n {\n \"id\": \"ValueAxis-1\",\n \"name\": \"LeftAxis-1\",\n \"type\": \"value\",\n \"position\": \"left\",\n \"show\": true,\n \"style\": {},\n \"scale\": {\n \"type\": \"linear\",\n \"mode\": \"normal\",\n \"setYExtents\": false,\n \"defaultYExtents\": false\n },\n \"labels\": {\n \"show\": true,\n \"rotate\": 0,\n \"filter\": false,\n \"truncate\": 100\n },\n \"title\": {\n \"text\": \"Unique count of scan_fingerprint\"\n }\n }\n ],\n \"seriesParams\": [\n {\n \"show\": \"true\",\n \"type\": \"histogram\",\n \"mode\": \"stacked\",\n \"data\": {\n \"label\": \"Unique count of scan_fingerprint\",\n \"id\": \"1\"\n },\n \"valueAxis\": \"ValueAxis-1\"\n }\n ]\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"cardinality\",\n \"schema\": \"metric\",\n \"params\": {\n \"field\": \"scan_fingerprint\"\n }\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"segment\",\n \"params\": {\n \"field\": \"plugin_name\",\n \"size\": 10,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"otherBucket\": false,\n \"otherBucketLabel\": \"Other\",\n \"missingBucket\": false,\n \"missingBucketLabel\": \"Missing\",\n \"customLabel\": \"Scan Name\"\n }\n }\n ]\n}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"analyze_wildcard\": true,\n \"query\": \"*\",\n \"default_field\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } - }, - { - "id": "852816e0-3eb1-11e7-90cb-918f9cb01e3d", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer-CVSS", - "visState": "{\"title\":\"VulnWhisperer-CVSS\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Unique Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"cvss\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"CVSS Score\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"asset.keyword\",\"customLabel\":\"# of Assets\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "8c9c9430-f77b-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Risk: Critical", + "visState": "{\"title\":\"VulnWhisperer - Risk: Critical\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10},{\"from\":10,\"to\":20}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk_score_name:critical\"},\"label\":\"Critical Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(255,245,240)\",\"10 - 20\":\"rgb(103,0,13)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" } - }, - { - "id": "35b6d320-3f7f-11e7-bd24-6903e3283192", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Residual Risk", - "visState": "{\"title\":\"VulnWhisperer - Residual Risk\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Count\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"risk_score\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Risk Number\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":0,\"direction\":\"desc\"}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "e6b5b920-f77a-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Risk: Low", + "visState": "{\"title\":\"VulnWhisperer - Risk: Low\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Greens\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":100}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk_score_name:low\"},\"label\":\"Low Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,252,245)\",\"50 - 100\":\"rgb(0,68,27)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "de1a5f40-3f85-11e7-97f9-3777d794626d", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - ScanName", - "visState": "{\"title\":\"VulnWhisperer - ScanName\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"scan_name.keyword\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Scan Name\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "c533c120-fe8c-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Risk: High", + "visState": "{\"title\":\"VulnWhisperer - Risk: High\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Reds\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":1,\"to\":5},{\"from\":5,\"to\":19999}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Risk: High\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk_score_name:high\"},\"label\":\"risk: High\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"1 - 5\":\"rgb(255,245,240)\",\"5 - 19999\":\"rgb(103,0,13)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "995e2280-3df3-11e7-a44e-c79ca8efb780", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer-Asset", - "visState": "{\"title\":\"VulnWhisperer-Asset\",\"type\":\"table\",\"params\":{\"perPage\":15,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"type\":\"table\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Findings\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"asset.keyword\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Asset\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "61b43c00-f77b-11e8-8f42-af2e41422cf8", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Risk: Medium", + "visState": "{\"title\":\"VulnWhisperer - Risk: Medium\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"verticalSplit\":false,\"extendRange\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Blues\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":10}],\"invertColors\":true,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"risk_score_name:medium\"},\"label\":\"Medium Risk\"}]}}]}", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10\":\"rgb(8,48,107)\"}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } - }, - { - "id": "297df800-3f7e-11e7-bd24-6903e3283192", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - Plugin Name", - "visState": "{\"title\":\"VulnWhisperer - Plugin Name\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"scan_fingerprint\",\"customLabel\":\"Count\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"plugin_name.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Plugin Name\"}}]}", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "297df800-3f7e-11e7-bd24-6903e3283192", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - Plugin Name", + "visState": "{\n \"title\": \"VulnWhisperer - Plugin Name\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showPartialRows\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"showTotal\": false,\n \"totalFunc\": \"sum\",\n \"showMetricsAtAllLevels\": false\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"cardinality\",\n \"schema\": \"metric\",\n \"params\": {\n \"field\": \"scan_fingerprint\",\n \"customLabel\": \"Count\"\n }\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"plugin_name\",\n \"size\": 10,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"otherBucket\": false,\n \"otherBucketLabel\": \"Other\",\n \"missingBucket\": false,\n \"missingBucketLabel\": \"Missing\",\n \"customLabel\": \"Plugin Name\"\n }\n }\n ]\n}", + "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"query\": \"*\",\n \"analyze_wildcard\": true,\n \"default_field\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } - }, - { - "id": "67d432e0-44ec-11e7-a05f-d9719b331a27", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - TL-Critical Risk", - "visState": "{\"title\":\"VulnWhisperer - TL-Critical Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=9 AND risk_score:<=10)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "479deab0-8a39-11e7-a58a-9bfcb3761a3d", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - TL - TaggedAssetsPluginNames", + "visState": "{\n \"title\": \"VulnWhisperer - TL - TaggedAssetsPluginNames\",\n \"type\": \"timelion\",\n \"params\": {\n \"expression\": \".es(index='logstash-vulnwhisperer-*', q='tags:critical_asset OR tags:hipaa_asset OR tags:pci_asset', split=\\\"plugin_name:10\\\").bars(width=4).label(regex=\\\".*:(.+)>.*\\\",label=\\\"$1\\\")\",\n \"interval\": \"auto\"\n },\n \"aggs\": [],\n \"listeners\": {}\n}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"query\": \"*\",\n \"analyze_wildcard\": true\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } - }, - { - "id": "8d9592d0-44ec-11e7-a05f-d9719b331a27", - "type": "visualization", - "attributes": { - "title": "VulnWhisperer - TL-High Risk", - "visState": "{\"title\":\"VulnWhisperer - TL-High Risk\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').label(\\\"Original\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w).label(\\\"One week offset\\\"),.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)').subtract(.es(index='logstash-vulnwhisperer-*',q='(risk_score:>=7 AND risk_score:<9)',offset=-1w)).label(\\\"Difference\\\").lines(steps=3,fill=2,width=1)\",\"interval\":\"auto\"},\"aggs\":[],\"listeners\":{}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" - } + } + }, + { + "id": "4a6d9090-f66e-11e8-8f42-af2e41422cf8", + "type": "index-pattern", + "attributes": { + "title": "logstash-vulnwhisperer-*", + "timeFieldName": "@timestamp", + "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"asset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"asset.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"asset_uuid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"assign_ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"category\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cve\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3_base\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3_temporal\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3_temporal_vector\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss3_vector\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss_base\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss_temporal\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss_temporal_vector\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cvss_vector\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"exploitability\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"exploitability.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"fqdn\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"history_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host_end\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host_start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"impact\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"impact.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip_status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"last_updated\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"operating_system\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pci_vuln\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"plugin_output\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"plugin_output.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"port\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"results\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"risk\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_number\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"risk_score_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_reference\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"see_also\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"solution\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ssl\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"synopsis\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vendor_reference\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability_state\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scan_fingerprint\",\"type\":\"string\",\"count\":1,\"scripted\":true,\"script\":\"doc['asset.keyword']+'_'+doc['plugin_id']\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", + "fieldFormatMap": "{\"plugin_id\":{\"id\":\"number\",\"params\":{\"pattern\":\"00.[000]\"}}}" + } + }, + { + "id": "de1a5f40-3f85-11e7-97f9-3777d794626d", + "type": "visualization", + "attributes": { + "title": "VulnWhisperer - ScanName", + "visState": "{\n \"title\": \"VulnWhisperer - ScanName\",\n \"type\": \"table\",\n \"params\": {\n \"perPage\": 10,\n \"showPartialRows\": false,\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n },\n \"showTotal\": false,\n \"totalFunc\": \"sum\",\n \"showMetricsAtAllLevels\": false\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"enabled\": true,\n \"type\": \"count\",\n \"schema\": \"metric\",\n \"params\": {}\n },\n {\n \"id\": \"2\",\n \"enabled\": true,\n \"type\": \"terms\",\n \"schema\": \"bucket\",\n \"params\": {\n \"field\": \"scan_name\",\n \"size\": 20,\n \"order\": \"desc\",\n \"orderBy\": \"1\",\n \"otherBucket\": false,\n \"otherBucketLabel\": \"Other\",\n \"missingBucket\": false,\n \"missingBucketLabel\": \"Missing\",\n \"customLabel\": \"Scan Name\"\n }\n }\n ]\n}", + "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"index\": \"4a6d9090-f66e-11e8-8f42-af2e41422cf8\",\n \"query\": {\n \"query\": {\n \"query_string\": {\n \"query\": \"*\",\n \"analyze_wildcard\": true,\n \"default_field\": \"*\"\n }\n },\n \"language\": \"lucene\"\n },\n \"filter\": []\n}" } } - ] + } +] \ No newline at end of file From 38604389031a626c04ef4edae8f71de299cdacd8 Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 13:18:06 +1000 Subject: [PATCH 81/96] Test travis docker --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2452b5b..f6a2e8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,17 @@ python: - 2.7 env: - TEST_PATH=tests/data + +services: + - docker # - 3.6 #matrix: # allow_failures: # - python: 3.6 - Commenting out testing for Python 3.6 until ready +before_install: + - docker build -t vulnwhisperer-local + - docker-compose -f docker-compose-test.yml up install: - pip install -r requirements.txt - pip install flake8 # pytest # add another testing frameworks later From 0102ccb2f71936e6f621bff50eee6de1f7021c53 Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 13:47:53 +1000 Subject: [PATCH 82/96] Fix build command --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f6a2e8c..795e02e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ services: # - python: 3.6 - Commenting out testing for Python 3.6 until ready before_install: - - docker build -t vulnwhisperer-local + - docker build -t vulnwhisperer-local . - docker-compose -f docker-compose-test.yml up install: - pip install -r requirements.txt From bfcb10ea0ed41d9f6744cd22d59d9147eb7925b8 Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 13:57:53 +1000 Subject: [PATCH 83/96] Fix permissions for ES --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 795e02e..316d5f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,9 @@ services: # - python: 3.6 - Commenting out testing for Python 3.6 until ready before_install: + - mkdir -p ./data/esdata1 + - mkdir -p ./data/es_snapshots + - chown 1000:1000 ./data/es* - docker build -t vulnwhisperer-local . - docker-compose -f docker-compose-test.yml up install: From 5828d05627c3783200e33f7fb7037e75bba3ccd8 Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 14:00:54 +1000 Subject: [PATCH 84/96] fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 316d5f1..ed51366 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ services: before_install: - mkdir -p ./data/esdata1 - mkdir -p ./data/es_snapshots - - chown 1000:1000 ./data/es* + - chown -R 1000:1000 ./data/es* - docker build -t vulnwhisperer-local . - docker-compose -f docker-compose-test.yml up install: From 47a96a2984b2e49a502faa7b0ea4319367db95a1 Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 15:10:45 +1000 Subject: [PATCH 85/96] sudo chown --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ed51366..e7475e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ services: before_install: - mkdir -p ./data/esdata1 - mkdir -p ./data/es_snapshots - - chown -R 1000:1000 ./data/es* + - sudo chown -R 1000:1000 ./data/es* - docker build -t vulnwhisperer-local . - docker-compose -f docker-compose-test.yml up install: From e0de8c6818d1402c4d654f3bd585c062793a1b64 Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 17:24:59 +1000 Subject: [PATCH 86/96] Expose Logstash API port --- docker-compose-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 9beb9fc..a29ecc5 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -67,6 +67,8 @@ services: - xpack.monitoring.enabled=false depends_on: - elasticsearch + ports: + - 9600:9600 networks: esnet: aliases: From a7ae44f981aff5cf850ab0422a2fa0a55aca57ad Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 17:25:44 +1000 Subject: [PATCH 87/96] Add docker test script --- .travis.yml | 3 +- tests/test-docker.sh | 90 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100755 tests/test-docker.sh diff --git a/.travis.yml b/.travis.yml index e7475e2..2627b5e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ before_install: - mkdir -p ./data/es_snapshots - sudo chown -R 1000:1000 ./data/es* - docker build -t vulnwhisperer-local . - - docker-compose -f docker-compose-test.yml up + - docker-compose -f docker-compose-test.yml up -d install: - pip install -r requirements.txt - pip install flake8 # pytest # add another testing frameworks later @@ -48,6 +48,7 @@ script: # Test only qualy_vuln - rm -rf /tmp/VulnWhisperer - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] + - bash tests/test-docker.sh notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down diff --git a/tests/test-docker.sh b/tests/test-docker.sh new file mode 100755 index 0000000..14a7c47 --- /dev/null +++ b/tests/test-docker.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +NORMAL=$(tput sgr0) +GREEN=$(tput setaf 2) +YELLOW=$(tput setaf 3) +RED=$(tput setaf 1) + +function red() { + echo -e "$RED$*$NORMAL" +} + +function green() { + echo -e "$GREEN$*$NORMAL" +} + +function yellow() { + echo -e "$YELLOW$*$NORMAL" +} + +elasticsearch_url="localhost:9200" +logstash_url="localhost:9600" + +until curl -s "$elasticsearch_url/_cluster/health?pretty" | grep '"status"' | grep -qE "green|yellow"; do + curl -s "$elasticsearch_url/_cluster/health?pretty" + yellow "\nWaiting for Elasticsearch..." + sleep 5 +done +curl -s "$elasticsearch_url/_cluster/health?pretty" + +until [[ $(curl -s "$logstash_url/_node/stats" | jq '.events.out') == 1236 ]] ; do + curl -s "$logstash_url/_node/stats" | jq '.events' + yellow "\nWaiting for Logstash load to finish..." + sleep 10 +done + +return_code=0 + +if [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]]; then + green "✅ Passed logstash-vulnwhisperer-2019.03 document count == 1232" +else + red "❌ Failed logstash-vulnwhisperer-2019.03 document count == 1232 was: $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count") instead" + ((return_code = return_code + 1)) +fi + +# Test Nessus plugin_name:Backported Security Patch Detection (FTP) +nessus_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_search?q=plugin_name:%22Backported%20Security%20Patch%20Detection%20(FTP)%22%20AND%20asset:176.28.50.164%20AND%20tags:nessus" | jq '.hits.hits[]._source') +if echo $nessus_doc | jq '.risk' | grep -q "None"; then + green "✅ Passed Nessus risk == None" +else + red "❌ Failed Nessus risk == None was: $(echo $nessus_doc | jq '.risk') instead" + ((return_code = return_code + 1)) +fi + +# Test Tenable plugin_name:Backported Security Patch Detection (FTP) +tenable_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_search?q=plugin_name:%22Backported%20Security%20Patch%20Detection%20(FTP)%22%20AND%20asset:176.28.50.164%20AND%20tags:tenable" | jq '.hits.hits[]._source') +# Test asset +if echo $tenable_doc | jq .asset | grep -q '176.28.50.164'; then + green "✅ Passed Tenable asset == 2019-03-30T10:17:41.000Z" +else + red "❌ Failed Tenable asset == 176.28.50.164 was: $(echo $tenable_doc | jq .asset) instead" + ((return_code = return_code + 1)) +fi + +# Test @timestamp +if echo $tenable_doc | jq '.["@timestamp"]' | grep -q '2019-03-30T15:45:44.000Z'; then + green "✅ Passed Tenable @timestamp == 2019-03-30T10:17:41.000Z" +else + red "❌ Failed Tenable @timestamp == 2019-03-30T15:45:44.000Z was: $(echo $tenable_doc | jq '.["@timestamp"]') instead" + ((return_code = return_code + 1)) +fi + +# Test Qualys plugin_name:OpenSSL Multiple Remote Security Vulnerabilities +qualys_vuln_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_search?q=tags:qualys_vuln%20AND%20ip:%22176.28.50.164%22%20AND%20plugin_name:%22OpenSSL%20Multiple%20Remote%20Security%20Vulnerabilities%22%20AND%20port:465" | jq '.hits.hits[]._source') +# Test @timestamp +if echo $qualys_vuln_doc | jq '.["@timestamp"]' | grep -q '2019-03-30T10:17:41.000Z'; then + green "✅ Passed Qualys VM @timestamp == 2019-03-30T10:17:41.000Z" +else + red "❌ Failed Qualys VM @timestamp == 2019-03-30T10:17:41.000Z was: $(echo $qualys_vuln_doc | jq '.["@timestamp"]') instead" + ((return_code = return_code + 1)) +fi + +# Test @XXXX +if echo $qualys_vuln_doc | jq '.cvss' | grep -q '6.8'; then + green "✅ Passed Qualys VM cvss == 6.8" +else + red "❌ Failed Qualys VM cvss == 6.8 was: $(echo $qualys_vuln_doc | jq '.cvss') instead" + ((return_code = return_code + 1)) +fi + +exit $return_code From c3fb65e67a607cc835b1dd38abef43a79fa13328 Mon Sep 17 00:00:00 2001 From: pemontto Date: Tue, 16 Apr 2019 17:33:30 +1000 Subject: [PATCH 88/96] Update test --- .travis.yml | 12 ++++++------ tests/test-docker.sh | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2627b5e..f16b0b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,23 +31,23 @@ script: - python setup.py install # Test successful scan download and parsing - rm -rf /tmp/VulnWhisperer - - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH} + - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH} # Run a second time with no scans to import - - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH} + - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH} # Test one failed scan - rm -rf /tmp/VulnWhisperer - rm -f ${TEST_PATH}/nessus/GET_scans_exports_164_download - - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] + - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] # Test two failed scans - rm -rf /tmp/VulnWhisperer - rm -f ${TEST_PATH}/qualys_vuln/scan_1553941061.87241 - - vuln_whisperer -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 2 ]] + - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 2 ]] # Test only nessus - rm -rf /tmp/VulnWhisperer - - vuln_whisperer -c configs/test.ini -s nessus --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] + - vuln_whisperer -F -c configs/test.ini -s nessus --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] # Test only qualy_vuln - rm -rf /tmp/VulnWhisperer - - vuln_whisperer -c configs/test.ini -s qualys_vuln --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] + - vuln_whisperer -F -c configs/test.ini -s qualys_vuln --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] - bash tests/test-docker.sh notifications: on_success: change diff --git a/tests/test-docker.sh b/tests/test-docker.sh index 14a7c47..a101513 100755 --- a/tests/test-docker.sh +++ b/tests/test-docker.sh @@ -21,14 +21,14 @@ elasticsearch_url="localhost:9200" logstash_url="localhost:9600" until curl -s "$elasticsearch_url/_cluster/health?pretty" | grep '"status"' | grep -qE "green|yellow"; do - curl -s "$elasticsearch_url/_cluster/health?pretty" + yellow $(curl -s "$elasticsearch_url/_cluster/health?pretty") yellow "\nWaiting for Elasticsearch..." sleep 5 done curl -s "$elasticsearch_url/_cluster/health?pretty" until [[ $(curl -s "$logstash_url/_node/stats" | jq '.events.out') == 1236 ]] ; do - curl -s "$logstash_url/_node/stats" | jq '.events' + yellow $(curl -s "$logstash_url/_node/stats" | jq '.events') yellow "\nWaiting for Logstash load to finish..." sleep 10 done From e30dbe244b3ce0c05b7c9ea4780b62c0d5ab33b6 Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 08:19:49 +1000 Subject: [PATCH 89/96] standardise /tmp to /opt --- .travis.yml | 10 +++++----- Dockerfile | 3 +-- configs/test.ini | 28 ++++++++++++++-------------- resources/elk6/init_kibana.sh | 6 +++--- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index f16b0b3..ea759ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,23 +30,23 @@ before_script: script: - python setup.py install # Test successful scan download and parsing - - rm -rf /tmp/VulnWhisperer + - rm -rf /opt/VulnWhisperer - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH} # Run a second time with no scans to import - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH} # Test one failed scan - - rm -rf /tmp/VulnWhisperer + - rm -rf /opt/VulnWhisperer - rm -f ${TEST_PATH}/nessus/GET_scans_exports_164_download - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] # Test two failed scans - - rm -rf /tmp/VulnWhisperer + - rm -rf /opt/VulnWhisperer - rm -f ${TEST_PATH}/qualys_vuln/scan_1553941061.87241 - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 2 ]] # Test only nessus - - rm -rf /tmp/VulnWhisperer + - rm -rf /opt/VulnWhisperer - vuln_whisperer -F -c configs/test.ini -s nessus --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] # Test only qualy_vuln - - rm -rf /tmp/VulnWhisperer + - rm -rf /opt/VulnWhisperer - vuln_whisperer -F -c configs/test.ini -s qualys_vuln --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] - bash tests/test-docker.sh notifications: diff --git a/Dockerfile b/Dockerfile index a2806ee..667cba1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,8 +20,7 @@ RUN python setup.py clean --all && \ WORKDIR /opt/VulnWhisperer -RUN python setup.py install && \ - ln -s /opt/VulnWhisperer /tmp/VulnWhisperer +RUN python setup.py install CMD vuln_whisperer -c /opt/VulnWhisperer/frameworks_example.ini diff --git a/configs/test.ini b/configs/test.ini index b8ce72f..b5f04b5 100755 --- a/configs/test.ini +++ b/configs/test.ini @@ -4,8 +4,8 @@ hostname=nessus port=443 username=nessus_username password=nessus_password -write_path=/tmp/VulnWhisperer/data/nessus/ -db_path=/tmp/VulnWhisperer/data/database +write_path=/opt/VulnWhisperer/data/nessus/ +db_path=/opt/VulnWhisperer/data/database trash=false verbose=true @@ -15,8 +15,8 @@ hostname=tenable port=443 username=tenable.io_username password=tenable.io_password -write_path=/tmp/VulnWhisperer/data/tenable/ -db_path=/tmp/VulnWhisperer/data/database +write_path=/opt/VulnWhisperer/data/tenable/ +db_path=/opt/VulnWhisperer/data/database trash=false verbose=true @@ -26,8 +26,8 @@ enabled = false hostname = qualys_web username = exampleuser password = examplepass -write_path=/tmp/VulnWhisperer/data/qualys_web/ -db_path=/tmp/VulnWhisperer/data/database +write_path=/opt/VulnWhisperer/data/qualys_web/ +db_path=/opt/VulnWhisperer/data/database verbose=true # Set the maximum number of retries each connection should attempt. @@ -42,8 +42,8 @@ enabled = true hostname = qualys_vuln username = exampleuser password = examplepass -write_path=/tmp/VulnWhisperer/data/qualys_vuln/ -db_path=/tmp/VulnWhisperer/data/database +write_path=/opt/VulnWhisperer/data/qualys_vuln/ +db_path=/opt/VulnWhisperer/data/database verbose=true [detectify] @@ -54,8 +54,8 @@ hostname = detectify username = exampleuser #password variable used as secretKey password = examplepass -write_path =/tmp/VulnWhisperer/data/detectify/ -db_path = /tmp/VulnWhisperer/data/database +write_path =/opt/VulnWhisperer/data/detectify/ +db_path = /opt/VulnWhisperer/data/database verbose = true [openvas] @@ -64,8 +64,8 @@ hostname = openvas port = 4000 username = exampleuser password = examplepass -write_path=/tmp/VulnWhisperer/data/openvas/ -db_path=/tmp/VulnWhisperer/data/database +write_path=/opt/VulnWhisperer/data/openvas/ +db_path=/opt/VulnWhisperer/data/database verbose=true [jira] @@ -73,8 +73,8 @@ enabled = false hostname = jira-host username = username password = password -write_path = /tmp/VulnWhisperer/data/jira/ -db_path = /tmp/VulnWhisperer/data/database +write_path = /opt/VulnWhisperer/data/jira/ +db_path = /opt/VulnWhisperer/data/database verbose = true dns_resolv = False diff --git a/resources/elk6/init_kibana.sh b/resources/elk6/init_kibana.sh index eca079d..656160c 100755 --- a/resources/elk6/init_kibana.sh +++ b/resources/elk6/init_kibana.sh @@ -12,7 +12,7 @@ saved_objects_file="kibana_APIonly.json" until curl -s "$elasticsearch_url/_cluster/health?pretty" | grep '"status"' | grep -qE "green|yellow"; do curl -s "$elasticsearch_url/_cluster/health?pretty" - echo "Waiting for Elasticsearch" + echo "Waiting for Elasticsearch..." sleep 5 done @@ -30,8 +30,8 @@ else fi until [ "`curl -s -I "$kibana_url"/status | head -n1 |cut -d$' ' -f2`" == "200" ]; do - curl -I "$kibana_url"/status - echo "Waiting for Kibana" + curl -s -I "$kibana_url"/status + echo "Waiting for Kibana..." sleep 5 done From bb60fae67e49d6f60f292455445725e5eb31be43 Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 08:24:08 +1000 Subject: [PATCH 90/96] Move vulnwhisperer tests to a script --- .travis.yml | 20 +------- tests/test-docker.sh | 11 ++--- tests/test-vuln_whisperer.sh | 90 ++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 25 deletions(-) create mode 100755 tests/test-vuln_whisperer.sh diff --git a/.travis.yml b/.travis.yml index ea759ab..d98caaa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,25 +29,7 @@ before_script: - flake8 . --count --exit-zero --exclude=deps/qualysapi --max-complexity=10 --max-line-length=127 --statistics script: - python setup.py install - # Test successful scan download and parsing - - rm -rf /opt/VulnWhisperer - - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH} - # Run a second time with no scans to import - - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH} - # Test one failed scan - - rm -rf /opt/VulnWhisperer - - rm -f ${TEST_PATH}/nessus/GET_scans_exports_164_download - - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] - # Test two failed scans - - rm -rf /opt/VulnWhisperer - - rm -f ${TEST_PATH}/qualys_vuln/scan_1553941061.87241 - - vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 2 ]] - # Test only nessus - - rm -rf /opt/VulnWhisperer - - vuln_whisperer -F -c configs/test.ini -s nessus --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] - # Test only qualy_vuln - - rm -rf /opt/VulnWhisperer - - vuln_whisperer -F -c configs/test.ini -s qualys_vuln --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]] + - bash tests/test-vuln_whisperer.sh - bash tests/test-docker.sh notifications: on_success: change diff --git a/tests/test-docker.sh b/tests/test-docker.sh index a101513..c50b881 100755 --- a/tests/test-docker.sh +++ b/tests/test-docker.sh @@ -17,23 +17,22 @@ function yellow() { echo -e "$YELLOW$*$NORMAL" } +return_code=0 + elasticsearch_url="localhost:9200" logstash_url="localhost:9600" until curl -s "$elasticsearch_url/_cluster/health?pretty" | grep '"status"' | grep -qE "green|yellow"; do - yellow $(curl -s "$elasticsearch_url/_cluster/health?pretty") - yellow "\nWaiting for Elasticsearch..." + yellow "Waiting for Elasticsearch..." sleep 5 done curl -s "$elasticsearch_url/_cluster/health?pretty" until [[ $(curl -s "$logstash_url/_node/stats" | jq '.events.out') == 1236 ]] ; do - yellow $(curl -s "$logstash_url/_node/stats" | jq '.events') - yellow "\nWaiting for Logstash load to finish..." + yellow "Waiting for Logstash load to finish..." sleep 10 done - -return_code=0 +curl -s "$logstash_url/_node/stats" | jq '.events' if [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]]; then green "✅ Passed logstash-vulnwhisperer-2019.03 document count == 1232" diff --git a/tests/test-vuln_whisperer.sh b/tests/test-vuln_whisperer.sh new file mode 100755 index 0000000..77f2e72 --- /dev/null +++ b/tests/test-vuln_whisperer.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +NORMAL=$(tput sgr0) +GREEN=$(tput setaf 2) +YELLOW=$(tput setaf 3) +RED=$(tput setaf 1) + +function red() { + echo -e "$RED$*$NORMAL" +} + +function green() { + echo -e "$GREEN$*$NORMAL" +} + +function yellow() { + echo -e "$YELLOW$*$NORMAL" +} + +return_code=0 + +yellow "\n*********************************************" +yellow "* Test successful scan download and parsing *" +yellow "*********************************************" +rm -rf /opt/VulnWhisperer/* +if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; then + green "\n✅ Passed: Test successful scan download and parsing" +else + red "\n❌ Failed: Test successful scan download and parsing" + ((return_code = return_code + 1)) +fi + +yellow "\n*********************************************" +yellow "* Test run with no scans to import *" +yellow "*********************************************" +if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; then + green "\n✅ Passed: Test run with no scans to import" +else + red "\n❌ Failed: Test run with no scans to import" + ((return_code = return_code + 1)) +fi + +yellow "\n*********************************************" +yellow "* Test one failed scan *" +yellow "*********************************************" +rm -rf /opt/VulnWhisperer/* +rm -f ${TEST_PATH}/nessus/GET_scans_exports_164_download +if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]]; then + green "\n✅ Passed: Test one failed scan" +else + red "\n❌ Failed: Test one failed scan" + ((return_code = return_code + 1)) +fi + +yellow "\n*********************************************" +yellow "* Test two failed scans *" +yellow "*********************************************" +rm -rf /opt/VulnWhisperer/* +rm -f ${TEST_PATH}/qualys_vuln/scan_1553941061.87241 +if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 2 ]]; then + green "\n✅ Passed: Test two failed scans" +else + red "\n❌ Failed: Test two failed scans" + ((return_code = return_code + 1)) +fi + +yellow "\n*********************************************" +yellow "* Test only nessus with one failed scan *" +yellow "*********************************************" +rm -rf /opt/VulnWhisperer/* +if vuln_whisperer -F -c configs/test.ini -s nessus --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]]; then + green "\n✅ Passed: Test only nessus with one failed scan" +else + red "\n❌ Failed: Test only nessus with one failed scan" + ((return_code = return_code + 1)) +fi + +echo -e "\n\n" +yellow "*********************************************" +yellow "* Test only Qualys VM with one failed scan *" +yellow "*********************************************" +rm -rf /opt/VulnWhisperer/* +if vuln_whisperer -F -c configs/test.ini -s qualys_vuln --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]]; then + green "\n✅ Passed: Test only Qualys VM with one failed scan" +else + red "\n❌ Failed: Test only Qualys VM with one failed scan" + ((return_code = return_code + 1)) +fi + +exit $return_code \ No newline at end of file From 60b9e2b3d9c32778cc043d28606dfcf600a23cce Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 09:25:36 +1000 Subject: [PATCH 91/96] Test updates --- tests/test-docker.sh | 49 +++++++++++++++++++++++------------- tests/test-vuln_whisperer.sh | 3 ++- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/tests/test-docker.sh b/tests/test-docker.sh index c50b881..e9ed789 100755 --- a/tests/test-docker.sh +++ b/tests/test-docker.sh @@ -26,27 +26,42 @@ until curl -s "$elasticsearch_url/_cluster/health?pretty" | grep '"status"' | gr yellow "Waiting for Elasticsearch..." sleep 5 done -curl -s "$elasticsearch_url/_cluster/health?pretty" +green "✅ Elasticsearch status is green..." +count=0 until [[ $(curl -s "$logstash_url/_node/stats" | jq '.events.out') == 1236 ]] ; do - yellow "Waiting for Logstash load to finish..." + yellow "Waiting for Logstash load to finish... attempt $count of 30" + ((count++)) && ((count!=30)) && break sleep 10 done -curl -s "$logstash_url/_node/stats" | jq '.events' +green "✅ Logstash load finished..." -if [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]]; then - green "✅ Passed logstash-vulnwhisperer-2019.03 document count == 1232" +count=0 +curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count' +until [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]] ; do + yellow "Waiting for Elasticsearch index to sync... $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') of 1232" + ((count++)) && ((count==30)) && break + sleep 2 +done +if [[ count -le 30 ]]; then + green "✅ logstash-vulnwhisperer-2019.03 document count == 1232" else - red "❌ Failed logstash-vulnwhisperer-2019.03 document count == 1232 was: $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count") instead" - ((return_code = return_code + 1)) + red "❌ TIMED OUT waitin for logstash-vulnwhisperer-2019.03 document count: $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq) != 1232" fi +# if [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]]; then +# green "✅ Passed: logstash-vulnwhisperer-2019.03 document count == 1232" +# else +# red "❌ Failed: logstash-vulnwhisperer-2019.03 document count == 1232 was: $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count") instead" +# ((return_code = return_code + 1)) +# fi + # Test Nessus plugin_name:Backported Security Patch Detection (FTP) nessus_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_search?q=plugin_name:%22Backported%20Security%20Patch%20Detection%20(FTP)%22%20AND%20asset:176.28.50.164%20AND%20tags:nessus" | jq '.hits.hits[]._source') if echo $nessus_doc | jq '.risk' | grep -q "None"; then - green "✅ Passed Nessus risk == None" + green "✅ Passed: Nessus risk == None" else - red "❌ Failed Nessus risk == None was: $(echo $nessus_doc | jq '.risk') instead" + red "❌ Failed: Nessus risk == None was: $(echo $nessus_doc | jq '.risk') instead" ((return_code = return_code + 1)) fi @@ -54,17 +69,17 @@ fi tenable_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_search?q=plugin_name:%22Backported%20Security%20Patch%20Detection%20(FTP)%22%20AND%20asset:176.28.50.164%20AND%20tags:tenable" | jq '.hits.hits[]._source') # Test asset if echo $tenable_doc | jq .asset | grep -q '176.28.50.164'; then - green "✅ Passed Tenable asset == 2019-03-30T10:17:41.000Z" + green "✅ Passed: Tenable asset == 2019-03-30T10:17:41.000Z" else - red "❌ Failed Tenable asset == 176.28.50.164 was: $(echo $tenable_doc | jq .asset) instead" + red "❌ Failed: Tenable asset == 176.28.50.164 was: $(echo $tenable_doc | jq .asset) instead" ((return_code = return_code + 1)) fi # Test @timestamp if echo $tenable_doc | jq '.["@timestamp"]' | grep -q '2019-03-30T15:45:44.000Z'; then - green "✅ Passed Tenable @timestamp == 2019-03-30T10:17:41.000Z" + green "✅ Passed: Tenable @timestamp == 2019-03-30T10:17:41.000Z" else - red "❌ Failed Tenable @timestamp == 2019-03-30T15:45:44.000Z was: $(echo $tenable_doc | jq '.["@timestamp"]') instead" + red "❌ Failed: Tenable @timestamp == 2019-03-30T15:45:44.000Z was: $(echo $tenable_doc | jq '.["@timestamp"]') instead" ((return_code = return_code + 1)) fi @@ -72,17 +87,17 @@ fi qualys_vuln_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_search?q=tags:qualys_vuln%20AND%20ip:%22176.28.50.164%22%20AND%20plugin_name:%22OpenSSL%20Multiple%20Remote%20Security%20Vulnerabilities%22%20AND%20port:465" | jq '.hits.hits[]._source') # Test @timestamp if echo $qualys_vuln_doc | jq '.["@timestamp"]' | grep -q '2019-03-30T10:17:41.000Z'; then - green "✅ Passed Qualys VM @timestamp == 2019-03-30T10:17:41.000Z" + green "✅ Passed: Qualys VM @timestamp == 2019-03-30T10:17:41.000Z" else - red "❌ Failed Qualys VM @timestamp == 2019-03-30T10:17:41.000Z was: $(echo $qualys_vuln_doc | jq '.["@timestamp"]') instead" + red "❌ Failed: Qualys VM @timestamp == 2019-03-30T10:17:41.000Z was: $(echo $qualys_vuln_doc | jq '.["@timestamp"]') instead" ((return_code = return_code + 1)) fi # Test @XXXX if echo $qualys_vuln_doc | jq '.cvss' | grep -q '6.8'; then - green "✅ Passed Qualys VM cvss == 6.8" + green "✅ Passed: Qualys VM cvss == 6.8" else - red "❌ Failed Qualys VM cvss == 6.8 was: $(echo $qualys_vuln_doc | jq '.cvss') instead" + red "❌ Failed: Qualys VM cvss == 6.8 was: $(echo $qualys_vuln_doc | jq '.cvss') instead" ((return_code = return_code + 1)) fi diff --git a/tests/test-vuln_whisperer.sh b/tests/test-vuln_whisperer.sh index 77f2e72..22e9edf 100755 --- a/tests/test-vuln_whisperer.sh +++ b/tests/test-vuln_whisperer.sh @@ -44,6 +44,7 @@ yellow "\n*********************************************" yellow "* Test one failed scan *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* +yellow "Removing ${TEST_PATH}/nessus/GET_scans_exports_164_download" rm -f ${TEST_PATH}/nessus/GET_scans_exports_164_download if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]]; then green "\n✅ Passed: Test one failed scan" @@ -56,6 +57,7 @@ yellow "\n*********************************************" yellow "* Test two failed scans *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* +yellow "Removing ${TEST_PATH}/qualys_vuln/scan_1553941061.87241" rm -f ${TEST_PATH}/qualys_vuln/scan_1553941061.87241 if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 2 ]]; then green "\n✅ Passed: Test two failed scans" @@ -75,7 +77,6 @@ else ((return_code = return_code + 1)) fi -echo -e "\n\n" yellow "*********************************************" yellow "* Test only Qualys VM with one failed scan *" yellow "*********************************************" From 2c5fbfc3efc9e508e52b3735755b0635ea5c661b Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 09:48:18 +1000 Subject: [PATCH 92/96] restore deleted files --- tests/test-vuln_whisperer.sh | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/test-vuln_whisperer.sh b/tests/test-vuln_whisperer.sh index 22e9edf..050a56b 100755 --- a/tests/test-vuln_whisperer.sh +++ b/tests/test-vuln_whisperer.sh @@ -23,7 +23,7 @@ yellow "\n*********************************************" yellow "* Test successful scan download and parsing *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* -if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; then +if vuln_whisperer -F -c configs/test.ini --mock --mock_dir "${TEST_PATH}"; then green "\n✅ Passed: Test successful scan download and parsing" else red "\n❌ Failed: Test successful scan download and parsing" @@ -33,7 +33,7 @@ fi yellow "\n*********************************************" yellow "* Test run with no scans to import *" yellow "*********************************************" -if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; then +if vuln_whisperer -F -c configs/test.ini --mock --mock_dir "${TEST_PATH}"; then green "\n✅ Passed: Test run with no scans to import" else red "\n❌ Failed: Test run with no scans to import" @@ -45,8 +45,8 @@ yellow "* Test one failed scan *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* yellow "Removing ${TEST_PATH}/nessus/GET_scans_exports_164_download" -rm -f ${TEST_PATH}/nessus/GET_scans_exports_164_download -if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]]; then +mv "${TEST_PATH}/nessus/GET_scans_exports_164_download"{,.bak} +if vuln_whisperer -F -c configs/test.ini --mock --mock_dir "${TEST_PATH}"; [[ $? -eq 1 ]]; then green "\n✅ Passed: Test one failed scan" else red "\n❌ Failed: Test one failed scan" @@ -58,8 +58,8 @@ yellow "* Test two failed scans *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* yellow "Removing ${TEST_PATH}/qualys_vuln/scan_1553941061.87241" -rm -f ${TEST_PATH}/qualys_vuln/scan_1553941061.87241 -if vuln_whisperer -F -c configs/test.ini --mock --mock_dir ${TEST_PATH}; [[ $? -eq 2 ]]; then +mv "${TEST_PATH}/qualys_vuln/scan_1553941061.87241"{,.bak} +if vuln_whisperer -F -c configs/test.ini --mock --mock_dir "${TEST_PATH}"; [[ $? -eq 2 ]]; then green "\n✅ Passed: Test two failed scans" else red "\n❌ Failed: Test two failed scans" @@ -70,7 +70,7 @@ yellow "\n*********************************************" yellow "* Test only nessus with one failed scan *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* -if vuln_whisperer -F -c configs/test.ini -s nessus --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]]; then +if vuln_whisperer -F -c configs/test.ini -s nessus --mock --mock_dir "${TEST_PATH}"; [[ $? -eq 1 ]]; then green "\n✅ Passed: Test only nessus with one failed scan" else red "\n❌ Failed: Test only nessus with one failed scan" @@ -81,11 +81,15 @@ yellow "*********************************************" yellow "* Test only Qualys VM with one failed scan *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* -if vuln_whisperer -F -c configs/test.ini -s qualys_vuln --mock --mock_dir ${TEST_PATH}; [[ $? -eq 1 ]]; then +if vuln_whisperer -F -c configs/test.ini -s qualys_vuln --mock --mock_dir "${TEST_PATH}"; [[ $? -eq 1 ]]; then green "\n✅ Passed: Test only Qualys VM with one failed scan" else red "\n❌ Failed: Test only Qualys VM with one failed scan" ((return_code = return_code + 1)) fi +# Restore the removed files +mv" ${TEST_PATH}/qualys_vuln/scan_1553941061.87241.bak" "${TEST_PATH}/qualys_vuln/scan_1553941061.87241" +mv "${TEST_PATH}/nessus/GET_scans_exports_164_download.bak" "${TEST_PATH}/nessus/GET_scans_exports_164_download.bak" + exit $return_code \ No newline at end of file From e9aba0796f04ffd8aa97556294fb6651aad67982 Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 09:48:35 +1000 Subject: [PATCH 93/96] increase timeout for ES sync --- tests/test-docker.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test-docker.sh b/tests/test-docker.sh index e9ed789..7a499df 100755 --- a/tests/test-docker.sh +++ b/tests/test-docker.sh @@ -37,16 +37,15 @@ done green "✅ Logstash load finished..." count=0 -curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count' until [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]] ; do yellow "Waiting for Elasticsearch index to sync... $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') of 1232" - ((count++)) && ((count==30)) && break + ((count++)) && ((count==150)) && break sleep 2 done -if [[ count -le 30 ]]; then +if [[ count -le 50 && $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]]; then green "✅ logstash-vulnwhisperer-2019.03 document count == 1232" else - red "❌ TIMED OUT waitin for logstash-vulnwhisperer-2019.03 document count: $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq) != 1232" + red "❌ TIMED OUT waiting for logstash-vulnwhisperer-2019.03 document count: $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq) != 1232" fi # if [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]]; then From 549791470a226766f2777c519098a864fe9968f3 Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 10:02:34 +1000 Subject: [PATCH 94/96] Set limit to bail out on --- tests/test-docker.sh | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/test-docker.sh b/tests/test-docker.sh index 7a499df..5e7f7bd 100755 --- a/tests/test-docker.sh +++ b/tests/test-docker.sh @@ -29,21 +29,27 @@ done green "✅ Elasticsearch status is green..." count=0 -until [[ $(curl -s "$logstash_url/_node/stats" | jq '.events.out') == 1236 ]] ; do - yellow "Waiting for Logstash load to finish... attempt $count of 30" - ((count++)) && ((count!=30)) && break - sleep 10 +until [[ $(curl -s "$logstash_url/_node/stats" | jq '.events.out') -ge 1236 ]]; do + yellow "Waiting for Logstash load to finish... $(curl -s "$logstash_url/_node/stats" | jq '.events.out') of 1236 (attempt $count of 60)" + ((count++)) && ((count==60)) && break + sleep 5 done -green "✅ Logstash load finished..." + +if [[ count -le 60 && $(curl -s "$logstash_url/_node/stats" | jq '.events.out') -ge 1236 ]]; then + green "✅ Logstash load finished..." +else + red "❌ Logstash load didn't complete... $(curl -s "$logstash_url/_node/stats" | jq '.events.out')" +fi + count=0 -until [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]] ; do - yellow "Waiting for Elasticsearch index to sync... $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') of 1232" +until [[ $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') -ge 1232 ]] ; do + yellow "Waiting for Elasticsearch index to sync... $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') of 1232 logs loaded (attempt $count of 150)" ((count++)) && ((count==150)) && break sleep 2 done -if [[ count -le 50 && $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') == 1232 ]]; then - green "✅ logstash-vulnwhisperer-2019.03 document count == 1232" +if [[ count -le 50 && $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq '.count') -ge 1232 ]]; then + green "✅ logstash-vulnwhisperer-2019.03 document count >= 1232" else red "❌ TIMED OUT waiting for logstash-vulnwhisperer-2019.03 document count: $(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_count" | jq) != 1232" fi @@ -68,7 +74,7 @@ fi tenable_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-2019.03/_search?q=plugin_name:%22Backported%20Security%20Patch%20Detection%20(FTP)%22%20AND%20asset:176.28.50.164%20AND%20tags:tenable" | jq '.hits.hits[]._source') # Test asset if echo $tenable_doc | jq .asset | grep -q '176.28.50.164'; then - green "✅ Passed: Tenable asset == 2019-03-30T10:17:41.000Z" + green "✅ Passed: Tenable asset == 176.28.50.164" else red "❌ Failed: Tenable asset == 176.28.50.164 was: $(echo $tenable_doc | jq .asset) instead" ((return_code = return_code + 1)) From 30e3efe2cb2a3532b79a1cc1863c268352f0462e Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 10:12:29 +1000 Subject: [PATCH 95/96] set default path and fix restore --- tests/test-vuln_whisperer.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test-vuln_whisperer.sh b/tests/test-vuln_whisperer.sh index 050a56b..7739e8b 100755 --- a/tests/test-vuln_whisperer.sh +++ b/tests/test-vuln_whisperer.sh @@ -19,6 +19,8 @@ function yellow() { return_code=0 +TEST_PATH=${TEST_PATH:-"tests/data"} + yellow "\n*********************************************" yellow "* Test successful scan download and parsing *" yellow "*********************************************" @@ -89,7 +91,7 @@ else fi # Restore the removed files -mv" ${TEST_PATH}/qualys_vuln/scan_1553941061.87241.bak" "${TEST_PATH}/qualys_vuln/scan_1553941061.87241" -mv "${TEST_PATH}/nessus/GET_scans_exports_164_download.bak" "${TEST_PATH}/nessus/GET_scans_exports_164_download.bak" +mv "${TEST_PATH}/qualys_vuln/scan_1553941061.87241.bak" "${TEST_PATH}/qualys_vuln/scan_1553941061.87241" +mv "${TEST_PATH}/nessus/GET_scans_exports_164_download.bak" "${TEST_PATH}/nessus/GET_scans_exports_164_download" -exit $return_code \ No newline at end of file +exit $return_code From c3167bd76b9225c78cc538611df3cb1217b2d936 Mon Sep 17 00:00:00 2001 From: pemontto Date: Wed, 17 Apr 2019 10:13:33 +1000 Subject: [PATCH 96/96] fix test output --- tests/test-docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-docker.sh b/tests/test-docker.sh index 5e7f7bd..3d15b76 100755 --- a/tests/test-docker.sh +++ b/tests/test-docker.sh @@ -82,7 +82,7 @@ fi # Test @timestamp if echo $tenable_doc | jq '.["@timestamp"]' | grep -q '2019-03-30T15:45:44.000Z'; then - green "✅ Passed: Tenable @timestamp == 2019-03-30T10:17:41.000Z" + green "✅ Passed: Tenable @timestamp == 2019-03-30T15:45:44.000Z" else red "❌ Failed: Tenable @timestamp == 2019-03-30T15:45:44.000Z was: $(echo $tenable_doc | jq '.["@timestamp"]') instead" ((return_code = return_code + 1))