Compare commits
11 Commits
2to3
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
dfb5c98cf6 | |||
691f45a1dc | |||
80197454a3 | |||
841cd09f2d | |||
e7183864d0 | |||
12ac3dbf62 | |||
e41ec93058 | |||
8a86e3142a | |||
9d003d12b4 | |||
63c638751b | |||
a3e85b7207 |
@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
VulnWhisperer is an open-source community funded project. VulnWhisperer currently works but is due for a documentation overhaul and code review. This is on the roadmap for the next month or two (February or March of 2022 - hopefully). Please note, crowd funding is an option. If you would like help getting VulnWhisperer up and running, are interested in new features, or are looking for paid support (for those of you that require commercial support contracts to implement open-source solutions), please reach out to **info@hasecuritysolutions.com**.
|
||||||
|
|
||||||
[](https://travis-ci.org/HASecuritySolutions/VulnWhisperer)
|
[](https://travis-ci.org/HASecuritySolutions/VulnWhisperer)
|
||||||
[](http://choosealicense.com/licenses/mit/)
|
[](https://github.com/HASecuritySolutions/VulnWhisperer/blob/master/LICENSE)
|
||||||
[](https://twitter.com/VulnWhisperer)
|
[](https://twitter.com/VulnWhisperer)
|
||||||
|
|
||||||
Currently Supports
|
Currently Supports
|
||||||
@ -30,7 +32,8 @@ Currently Supports
|
|||||||
|
|
||||||
### Reporting Frameworks
|
### Reporting Frameworks
|
||||||
|
|
||||||
- [X] [ELK (**v6**/**v7**)](https://www.elastic.co/elk-stack)
|
- [X] [Elastic Stack (**v6**/**v7**)](https://www.elastic.co/elk-stack)
|
||||||
|
- [ ] [OpenSearch - Being considered for next update](https://opensearch.org/)
|
||||||
- [X] [Jira](https://www.atlassian.com/software/jira)
|
- [X] [Jira](https://www.atlassian.com/software/jira)
|
||||||
- [ ] [Splunk](https://www.splunk.com/)
|
- [ ] [Splunk](https://www.splunk.com/)
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
pandas==0.20.3
|
pandas==0.20.3
|
||||||
setuptools==40.4.3
|
setuptools==65.5.1
|
||||||
pytz==2017.2
|
pytz==2017.2
|
||||||
Requests==2.20.0
|
Requests==2.20.0
|
||||||
lxml==4.1.1
|
lxml==4.6.5
|
||||||
future-fstrings
|
future-fstrings
|
||||||
bs4
|
bs4
|
||||||
jira
|
jira
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# Email: austin@hasecuritysolutions.com
|
# Email: austin@hasecuritysolutions.com
|
||||||
# Last Update: 03/04/2018
|
# Last Update: 03/04/2018
|
||||||
# Version 0.3
|
# Version 0.3
|
||||||
# Description: Take in qualys web scan reports from vulnWhisperer and pumps into logstash
|
# Description: Take in Openvas web scan reports from vulnWhisperer and pumps into logstash
|
||||||
|
|
||||||
input {
|
input {
|
||||||
file {
|
file {
|
||||||
|
@ -67,18 +67,23 @@ class JiraAPI(object):
|
|||||||
if not exists:
|
if not exists:
|
||||||
self.logger.error("Error creating Ticket: component {} not found".format(component))
|
self.logger.error("Error creating Ticket: component {} not found".format(component))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
new_issue = self.jira.create_issue(project=project,
|
|
||||||
summary=title,
|
|
||||||
description=desc,
|
|
||||||
issuetype={'name': 'Bug'},
|
|
||||||
labels=labels,
|
|
||||||
components=components_ticket)
|
|
||||||
|
|
||||||
self.logger.info("Ticket {} created successfully".format(new_issue))
|
try:
|
||||||
|
new_issue = self.jira.create_issue(project=project,
|
||||||
|
summary=title,
|
||||||
|
description=desc,
|
||||||
|
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)
|
||||||
|
|
||||||
if attachment_contents:
|
except Exception as e:
|
||||||
self.add_content_as_attachment(new_issue, attachment_contents)
|
self.logger.error("Failed to create ticket on Jira Project '{}'. Error: {}".format(project, e))
|
||||||
|
new_issue = False
|
||||||
|
|
||||||
return new_issue
|
return new_issue
|
||||||
|
|
||||||
@ -485,7 +490,7 @@ class JiraAPI(object):
|
|||||||
if transition.get('name') == self.JIRA_REOPEN_ISSUE:
|
if transition.get('name') == self.JIRA_REOPEN_ISSUE:
|
||||||
self.logger.debug("Ticket is reopenable")
|
self.logger.debug("Ticket is reopenable")
|
||||||
return True
|
return True
|
||||||
self.logger.warn("Ticket can't be opened. Check Jira transitions.")
|
self.logger.error("Ticket {} can't be opened. Check Jira transitions.".format(ticket_obj))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_ticket_closeable(self, ticket_obj):
|
def is_ticket_closeable(self, ticket_obj):
|
||||||
@ -493,7 +498,7 @@ class JiraAPI(object):
|
|||||||
for transition in transitions:
|
for transition in transitions:
|
||||||
if transition.get('name') == self.JIRA_CLOSE_ISSUE:
|
if transition.get('name') == self.JIRA_CLOSE_ISSUE:
|
||||||
return True
|
return True
|
||||||
self.logger.warn("Ticket can't closed. Check Jira transitions.")
|
self.logger.error("Ticket {} can't closed. Check Jira transitions.".format(ticket_obj))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_ticket_resolved(self, ticket_obj):
|
def is_ticket_resolved(self, ticket_obj):
|
||||||
|
@ -1247,16 +1247,21 @@ class vulnWhispererJIRA(vulnWhispererBase):
|
|||||||
vulnerabilities = self.parse_qualys_vuln_vulnerabilities(fullpath, source, scan_name, min_critical, dns_resolv)
|
vulnerabilities = self.parse_qualys_vuln_vulnerabilities(fullpath, source, scan_name, min_critical, dns_resolv)
|
||||||
|
|
||||||
#***JIRA sync***
|
#***JIRA sync***
|
||||||
if vulnerabilities:
|
try:
|
||||||
self.logger.info('{source} data has been successfuly parsed'.format(source=source.upper()))
|
if vulnerabilities:
|
||||||
self.logger.info('Starting JIRA sync')
|
self.logger.info('{source} data has been successfuly parsed'.format(source=source.upper()))
|
||||||
|
self.logger.info('Starting JIRA sync')
|
||||||
|
|
||||||
self.jira.sync(vulnerabilities, project, components)
|
self.jira.sync(vulnerabilities, project, components)
|
||||||
else:
|
else:
|
||||||
self.logger.info("[{source}.{scan_name}] No vulnerabilities or vulnerabilities not parsed.".format(source=source, scan_name=scan_name))
|
self.logger.info("[{source}.{scan_name}] No vulnerabilities or vulnerabilities not parsed.".format(source=source, scan_name=scan_name))
|
||||||
self.set_latest_scan_reported(fullpath.split("/")[-1])
|
self.set_latest_scan_reported(fullpath.split("/")[-1])
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error("Error: {}".format(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
#writing to file those assets without DNS resolution
|
#writing to file those assets without DNS resolution
|
||||||
#if its not empty
|
#if its not empty
|
||||||
if self.host_no_resolv:
|
if self.host_no_resolv:
|
||||||
@ -1276,7 +1281,10 @@ class vulnWhispererJIRA(vulnWhispererBase):
|
|||||||
try:
|
try:
|
||||||
self.jira_sync(self.config.get(scan, 'source'), self.config.get(scan, 'scan_name'))
|
self.jira_sync(self.config.get(scan, 'source'), self.config.get(scan, 'scan_name'))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error("VulnWhisperer wasn't able to report the vulnerabilities from the '{}'s source".format(self.config.get(scan, 'source')))
|
self.logger.error(
|
||||||
|
"VulnWhisperer wasn't able to report the vulnerabilities from the '{}'s source, section {}.\
|
||||||
|
\nError: {}".format(
|
||||||
|
self.config.get(scan, 'source'), self.config.get(scan, 'scan_name'), e))
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -1318,9 +1326,9 @@ class vulnWhisperer(object):
|
|||||||
self.exit_code += vw.process_web_assets()
|
self.exit_code += vw.process_web_assets()
|
||||||
|
|
||||||
elif self.profile == 'openvas':
|
elif self.profile == 'openvas':
|
||||||
vw_openvas = vulnWhispererOpenVAS(config=self.config)
|
vw = vulnWhispererOpenVAS(config=self.config)
|
||||||
if vw:
|
if vw:
|
||||||
self.exit_code += vw_openvas.process_openvas_scans()
|
self.exit_code += vw.process_openvas_scans()
|
||||||
|
|
||||||
elif self.profile == 'tenable':
|
elif self.profile == 'tenable':
|
||||||
vw = vulnWhispererNessus(config=self.config,
|
vw = vulnWhispererNessus(config=self.config,
|
||||||
|
Reference in New Issue
Block a user