Skip to content
Snippets Groups Projects
Commit 4844bb2f authored by Aaron Weaver's avatar Aaron Weaver
Browse files

Detailed error checking on API and CI/CD example

parent 4baf4797
Branches
Tags
No related merge requests found
__version__ = '1.0.1' __version__ = '1.0.2'
No preview for this file type
...@@ -873,19 +873,21 @@ class DefectDojoAPI(object): ...@@ -873,19 +873,21 @@ class DefectDojoAPI(object):
return DefectDojoResponse(message="Upload complete", data=data, success=True) return DefectDojoResponse(message="Upload complete", data=data, success=True)
elif response.status_code == 204: #Object updates elif response.status_code == 204: #Object updates
return DefectDojoResponse(message="Object updated.", success=True) return DefectDojoResponse(message="Object updated.", success=True)
elif response.status_code == 400: #Object not created
return DefectDojoResponse(message="Error occured in API.", success=False, data=response.text)
elif response.status_code == 404: #Object not created elif response.status_code == 404: #Object not created
return DefectDojoResponse(message="Object id does not exist.", success=False) return DefectDojoResponse(message="Object id does not exist.", success=False, data=response.text)
elif response.status_code == 401: elif response.status_code == 401:
return DefectDojoResponse(message="Unauthorized.", success=False) return DefectDojoResponse(message="Unauthorized.", success=False, data=response.text)
elif response.status_code == 414: elif response.status_code == 414:
return DefectDojoResponse(message="Request-URI Too Large.", success=False) return DefectDojoResponse(message="Request-URI Too Large.", success=False)
elif response.status_code == 500: elif response.status_code == 500:
return DefectDojoResponse(message="An error 500 occured in the API.", success=False) return DefectDojoResponse(message="An error 500 occured in the API.", success=False, data=response.text)
else: else:
data = response.json() data = response.json()
return DefectDojoResponse(message="Success", data=data, success=True, response_code=response.status_code) return DefectDojoResponse(message="Success", data=data, success=True, response_code=response.status_code)
except ValueError: except ValueError:
return DefectDojoResponse(message='JSON response could not be decoded.', success=False) return DefectDojoResponse(message='JSON response could not be decoded.', success=False, data=response.text)
except requests.exceptions.SSLError: except requests.exceptions.SSLError:
return DefectDojoResponse(message='An SSL error occurred.', success=False) return DefectDojoResponse(message='An SSL error occurred.', success=False)
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
......
No preview for this file type
...@@ -9,9 +9,6 @@ from datetime import datetime, timedelta ...@@ -9,9 +9,6 @@ from datetime import datetime, timedelta
import os, sys import os, sys
import argparse import argparse
import time import time
import junit_xml_output
DEBUG = True
test_cases = [] test_cases = []
...@@ -22,18 +19,17 @@ def junit(toolName, file): ...@@ -22,18 +19,17 @@ def junit(toolName, file):
print "Writing Junit test files" print "Writing Junit test files"
file.write(junit_xml.dump()) file.write(junit_xml.dump())
def dojo_connection(host, api_key, user): def dojo_connection(host, api_key, user, proxy):
#Optionally, specify a proxy #Optionally, specify a proxy
proxies = None
if proxy:
proxies = { proxies = {
'http': 'http://localhost:8081', 'http': proxy,
'https': 'http://localhost:8081', 'https': proxy,
} }
#if DEBUG:
# Instantiate the DefectDojo api wrapper # Instantiate the DefectDojo api wrapper
dd = defectdojo.DefectDojoAPI(host, api_key, user, proxies=proxies, verify_ssl=False, timeout=360, debug=False) dd = defectdojo.DefectDojoAPI(host, api_key, user, proxies=proxies, verify_ssl=False, timeout=360, debug=False)
#else:
# dd = defectdojo.DefectDojoAPI(host, api_key, user, verify_ssl=False, timeout=360, debug=False)
return dd return dd
# Workflow as follows: # Workflow as follows:
...@@ -42,9 +38,10 @@ def dojo_connection(host, api_key, user): ...@@ -42,9 +38,10 @@ def dojo_connection(host, api_key, user):
# 3. Call this script to load scan data, specifying scanner type # 3. Call this script to load scan data, specifying scanner type
# 4. Script returns along with a pass or fail results: Example: 2 new critical vulns, 1 low out of 10 vulnerabilities # 4. Script returns along with a pass or fail results: Example: 2 new critical vulns, 1 low out of 10 vulnerabilities
def return_engagement(dd, product_id): def return_engagement(dd, product_id, user):
#Specify the product id #Specify the product id
product_id = product_id product_id = product_id
engagement_id = None
# Check for a CI/CD engagement_id # Check for a CI/CD engagement_id
engagements = dd.list_engagements(product_in=product_id, status="In Progress") engagements = dd.list_engagements(product_in=product_id, status="In Progress")
...@@ -138,8 +135,10 @@ def processFiles(dd, engagement_id, file, scanner=None, build=None): ...@@ -138,8 +135,10 @@ def processFiles(dd, engagement_id, file, scanner=None, build=None):
print "Uploading " + scannerName + " scan: " + file print "Uploading " + scannerName + " scan: " + file
test_id = dd.upload_scan(engagement_id, scannerName, file, "true", dojoDate, build) test_id = dd.upload_scan(engagement_id, scannerName, file, "true", dojoDate, build)
if test_id.success == False:
print "Upload failed: Detailed error message: " + test_id.data
return test_id return test_id
#print os.path.basename(full_path)
def create_findings(dd, engagement_id, scanner, file, build=None): def create_findings(dd, engagement_id, scanner, file, build=None):
# Upload the scanner export # Upload the scanner export
...@@ -169,20 +168,22 @@ def summary(dd, engagement_id, test_ids, max_critical=0, max_high=0, max_medium= ...@@ -169,20 +168,22 @@ def summary(dd, engagement_id, test_ids, max_critical=0, max_high=0, max_medium=
print_findings(sum_severity(findings)) print_findings(sum_severity(findings))
print print
#Delay while de-dupes #Delay while de-dupes
sys.stdout.write("Sleeping for 30 seconds for de-dupe celery process:") sys.stdout.write("Sleeping for 10 seconds for de-dupe celery process:")
sys.stdout.flush() sys.stdout.flush()
for i in range(15): for i in range(5):
time.sleep(2) time.sleep(2)
sys.stdout.write(".") sys.stdout.write(".")
sys.stdout.flush() sys.stdout.flush()
findings = dd.list_findings(test_id_in=test_ids, duplicate="false", limit=500) findings = dd.list_findings(test_id_in=test_ids, duplicate="false", limit=500)
if findings.count() > 0: if findings.count() > 0:
"""
for finding in findings.data["objects"]: for finding in findings.data["objects"]:
test_cases.append(junit_xml_output.TestCase(finding["title"] + " Severity: " + finding["severity"], finding["description"],"failure")) test_cases.append(junit_xml_output.TestCase(finding["title"] + " Severity: " + finding["severity"], finding["description"],"failure"))
if not os.path.exists("reports"): if not os.path.exists("reports"):
os.mkdir("reports") os.mkdir("reports")
junit("DefectDojo", "reports/junit_dojo.xml") junit("DefectDojo", "reports/junit_dojo.xml")
"""
print"\n==============================================" print"\n=============================================="
print "Total Number of New Findings: " + str(findings.data["meta"]["total_count"]) print "Total Number of New Findings: " + str(findings.data["meta"]["total_count"])
...@@ -192,7 +193,7 @@ def summary(dd, engagement_id, test_ids, max_critical=0, max_high=0, max_medium= ...@@ -192,7 +193,7 @@ def summary(dd, engagement_id, test_ids, max_critical=0, max_high=0, max_medium=
print print
print"==============================================" print"=============================================="
strFail = "" strFail = None
if max_critical is not None: if max_critical is not None:
if sum_new_findings[4] > max_critical: if sum_new_findings[4] > max_critical:
strFail = "Build Failed: Max Critical" strFail = "Build Failed: Max Critical"
...@@ -234,10 +235,11 @@ def print_findings(findings): ...@@ -234,10 +235,11 @@ def print_findings(findings):
class Main: class Main:
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description='CI/CD integration for DefectDojo') parser = argparse.ArgumentParser(description='CI/CD integration for DefectDojo')
parser.add_argument('--host', help="Dojo Hostname", required=True) parser.add_argument('--host', help="DefectDojo Hostname", required=True)
parser.add_argument('--proxy', help="Proxy ex:localhost:8080", required=False, default=None)
parser.add_argument('--api_key', help="API Key", required=True) parser.add_argument('--api_key', help="API Key", required=True)
parser.add_argument('--user', help="User", required=True) parser.add_argument('--user', help="User", required=True)
parser.add_argument('--product', help="Dojo Product ID", required=True) parser.add_argument('--product', help="DefectDojo Product ID", required=True)
parser.add_argument('--file', help="Scanner file", required=False) parser.add_argument('--file', help="Scanner file", required=False)
parser.add_argument('--dir', help="Scanner directory, needs to have the scanner name with the scan file in the folder. Ex: reports/nmap/nmap.csv", required=False) parser.add_argument('--dir', help="Scanner directory, needs to have the scanner name with the scan file in the folder. Ex: reports/nmap/nmap.csv", required=False)
parser.add_argument('--scanner', help="Type of scanner", required=False) parser.add_argument('--scanner', help="Type of scanner", required=False)
...@@ -261,10 +263,11 @@ class Main: ...@@ -261,10 +263,11 @@ class Main:
max_high = args["high"] max_high = args["high"]
max_medium = args["medium"] max_medium = args["medium"]
build = args["build"] build = args["build"]
proxy = args["proxy"]
if dir is not None or file is not None: if dir is not None or file is not None:
dd = dojo_connection(host, api_key, user) dd = dojo_connection(host, api_key, user, proxy=proxy)
engagement_id = return_engagement(dd, product_id) engagement_id = return_engagement(dd, product_id, user)
test_ids = None test_ids = None
if file is not None: if file is not None:
if scanner is not None: if scanner is not None:
......
<?xml version="1.0" ?> <?xml version="1.0" ?>
<testsuite failures="4" name="DefectDojo" tests="4"> <testsuite failures="13" name="DefectDojo" tests="13">
<testcase name="blacklist Severity: Info"> <testcase name="Application Error Disclosure Severity: Medium">
<failure>Using cElementTree to parse untrusted XML data is known to be vulnerable to XML attacks. Replace cElementTree with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called. Filename: PyBitBucket.py Line number: 6 Line range: [6, 7, 8, 9] Issue Confidence: HIGH</failure> <failure>&lt;p&gt;This page contains an error/warning message that may disclose sensitive information like the location of the file that produced the unhandled exception. This information can be used to launch further attacks against the web application. The alert could be a false positive if the error message is found inside a documentation page.&lt;/p&gt;
Reference: &lt;p&gt;&lt;/p&gt;</failure>
</testcase> </testcase>
<testcase name="SQL injection Severity: High"> <testcase name="HTTP Parameter Override Severity: Medium">
<failure>SQL injection vulnerabilities arise when user-controllable data is <failure>&lt;p&gt;Unspecified form action: HTTP parameter override attack potentially possible. This is a known problem with Java Servlets but other platforms may also be vulnerable.&lt;/p&gt;
incorporated into database SQL queries in an unsafe manner. An attacker can Reference: &lt;p&gt;http://download.oracle.com/javaee-archive/servlet-spec.java.net/jsr340-experts/att-0317/OnParameterPollutionAttacks.pdf&lt;/p&gt;</failure>
supply crafted input to break out of the data context in which their input
appears and interfere with the structure of the surrounding query.
A wide range of damaging attacks can often be delivered via SQL injection,
including reading or modifying critical application data, interfering with
application logic, escalating privileges within the database and taking
control of the database server.
The **sort_column** parameter appears to be vulnerable to SQL injection
attacks. The payload **,(select*from(select(sleep(20)))a)** was submitted in
the sort_column parameter. The application took **20562** milliseconds to
respond to the request, compared with **1980** milliseconds for the original
request, indicating that the injected SQL command caused a time delay.
The database appears to be MySQL.
</failure>
</testcase> </testcase>
<testcase name="Cross-origin resource sharing Severity: Info"> <testcase name="Weak Authentication Method Severity: Medium">
<failure>An HTML5 cross-origin resource sharing (CORS) policy controls whether and how <failure>&lt;p&gt;HTTP basic or digest authentication has been used over an unsecured connection. The credentials can be read and then reused by someone with access to the network.&lt;/p&gt;
content running on other domains can perform two-way interaction with the Reference: &lt;p&gt;www.owasp.org/index.php/Category:Authentication_Vulnerability&lt;/p&gt;</failure>
domain that publishes the policy. The policy is fine-grained and can apply
access controls per-request based on the URL and other features of the
request.
If another domain is allowed by the policy, then that domain can potentially
attack users of the application. If a user is logged in to the application,
and visits a domain allowed by the policy, then any malicious content running
on that domain can potentially retrieve content from the application, and
sometimes carry out actions within the security context of the logged in user.
Even if an allowed domain is not overtly malicious in itself, security
vulnerabilities within that domain could potentially be leveraged by an
attacker to exploit the trust relationship and attack the application that
allows access. CORS policies on pages containing sensitive information should
be reviewed to determine whether it is appropriate for the application to
trust both the intentions and security posture of any domains granted access.
The application implements an HTML5 cross-origin resource sharing (CORS)
policy for this request.
If the application relies on network firewalls or other IP-based access
controls, this policy is likely to present a security risk.
Since the Vary: Origin header was not present in the response, reverse proxies
and intermediate servers may cache it. This may enable an attacker to carry
out cache poisoning attacks.
</failure>
</testcase> </testcase>
<testcase name="Cross-origin resource sharing: arbitrary origin trusted Severity: Info"> <testcase name="X-Frame-Options Header Not Set Severity: Medium">
<failure>An HTML5 cross-origin resource sharing (CORS) policy controls whether and how <failure>&lt;p&gt;X-Frame-Options header is not included in the HTTP response to protect against 'ClickJacking' attacks.&lt;/p&gt;
content running on other domains can perform two-way interaction with the Reference: &lt;p&gt;http://blogs.msdn.com/b/ieinternals/archive/2010/03/30/combating-clickjacking-with-x-frame-options.aspx&lt;/p&gt;</failure>
domain that publishes the policy. The policy is fine-grained and can apply </testcase>
access controls per-request based on the URL and other features of the <testcase name="Absence of Anti-CSRF Tokens Severity: Low">
request. <failure>&lt;p&gt;No Anti-CSRF tokens were found in a HTML submission form.&lt;/p&gt;&lt;p&gt;A cross-site request forgery is an attack that involves forcing a victim to send an HTTP request to a target destination without their knowledge or intent in order to perform an action as the victim. The underlying cause is application functionality using predictable URL/form actions in a repeatable way. The nature of the attack is that CSRF exploits the trust that a web site has for a user. By contrast, cross-site scripting (XSS) exploits the trust that a user has for a web site. Like XSS, CSRF attacks are not necessarily cross-site, but they can be. Cross-site request forgery is also known as CSRF, XSRF, one-click attack, session riding, confused deputy, and sea surf.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;CSRF attacks are effective in a number of situations, including:&lt;/p&gt;&lt;p&gt; * The victim has an active session on the target site.&lt;/p&gt;&lt;p&gt; * The victim is authenticated via HTTP auth on the target site.&lt;/p&gt;&lt;p&gt; * The victim is on the same local network as the target site.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;CSRF has primarily been used to perform an action against a target site using the victim's privileges, but recent techniques have been discovered to disclose information by gaining access to the response. The risk of information disclosure is dramatically increased when the target site is vulnerable to XSS, because XSS can be used as a platform for CSRF, allowing the attack to operate within the bounds of the same-origin policy.&lt;/p&gt;
Reference: &lt;p&gt;http://projects.webappsec.org/Cross-Site-Request-Forgery&lt;/p&gt;&lt;p&gt;http://cwe.mitre.org/data/definitions/352.html&lt;/p&gt;</failure>
Trusting arbitrary origins effectively disables the same-origin policy, </testcase>
allowing two-way interaction by third-party web sites. Unless the response <testcase name="Content-Type Header Missing Severity: Low">
consists only of unprotected public content, this policy is likely to present <failure>&lt;p&gt;The Content-Type header was either missing or empty.&lt;/p&gt;
a security risk. Reference: &lt;p&gt;http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx&lt;/p&gt;</failure>
</testcase>
If the site specifies the header Access-Control-Allow-Credentials: true, <testcase name="Cookie No HttpOnly Flag Severity: Low">
third-party sites may be able to carry out privileged actions and retrieve <failure>&lt;p&gt;A cookie has been set without the HttpOnly flag, which means that the cookie can be accessed by JavaScript. If a malicious script can be run on this page then the cookie will be accessible and can be transmitted to another site. If this is a session cookie then session hijacking may be possible.&lt;/p&gt;
sensitive information. Even if it does not, attackers may be able to bypass Reference: &lt;p&gt;http://www.owasp.org/index.php/HttpOnly&lt;/p&gt;</failure>
any IP-based access controls by proxying through users' browsers. </testcase>
<testcase name="Information Disclosure - Debug Error Messages Severity: Low">
<failure>&lt;p&gt;The response appeared to contain common error messages returned by platforms such as ASP.NET, and Web-servers such as IIS and Apache. You can configure the list of common debug messages.&lt;/p&gt;
Reference: &lt;p&gt;&lt;/p&gt;</failure>
The application implements an HTML5 cross-origin resource sharing (CORS) </testcase>
policy for this request that allows access from any domain. <testcase name="Password Autocomplete in Browser Severity: Low">
<failure>&lt;p&gt;The AUTOCOMPLETE attribute is not disabled on an HTML FORM/INPUT element containing password type input. Passwords may be stored in browsers and retrieved.&lt;/p&gt;
The application allowed access from the requested origin Reference: &lt;p&gt;http://www.w3schools.com/tags/att_input_autocomplete.asp&lt;/p&gt;&lt;p&gt;https://msdn.microsoft.com/en-us/library/ms533486%28v=vs.85%29.aspx&lt;/p&gt;</failure>
**https://pfcxuvwamstc.com** </testcase>
<testcase name="Private IP Disclosure Severity: Low">
If the application relies on network firewalls or other IP-based access <failure>&lt;p&gt;A private IP (such as 10.x.x.x, 172.x.x.x, 192.168.x.x) or an Amazon EC2 private hostname (for example, ip-10-0-56-78) has been found in the HTTP response body. This information might be helpful for further attacks targeting internal systems.&lt;/p&gt;
controls, this policy is likely to present a security risk. Reference: &lt;p&gt;https://tools.ietf.org/html/rfc1918&lt;/p&gt;</failure>
</testcase>
Since the Vary: Origin header was not present in the response, reverse proxies <testcase name="Web Browser XSS Protection Not Enabled Severity: Low">
and intermediate servers may cache it. This may enable an attacker to carry <failure>&lt;p&gt;Web Browser XSS Protection is not enabled, or is disabled by the configuration of the 'X-XSS-Protection' HTTP response header on the web server&lt;/p&gt;
out cache poisoning attacks. Reference: &lt;p&gt;https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet&lt;/p&gt;&lt;p&gt;https://blog.veracode.com/2014/03/guidelines-for-setting-security-headers/&lt;/p&gt;</failure>
</testcase>
</failure> <testcase name="X-Content-Type-Options Header Missing Severity: Low">
<failure>&lt;p&gt;The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.&lt;/p&gt;
Reference: &lt;p&gt;http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx&lt;/p&gt;&lt;p&gt;https://www.owasp.org/index.php/List_of_useful_HTTP_headers&lt;/p&gt;</failure>
</testcase>
<testcase name="Information Disclosure - Suspicious Comments Severity: Info">
<failure>&lt;p&gt;The response appears to contain suspicious comments which may help an attacker.&lt;/p&gt;
Reference: &lt;p&gt;&lt;/p&gt;</failure>
</testcase> </testcase>
</testsuite> </testsuite>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment