From 4844bb2f6ac848d370b53a4f085e9b64bc64b8fd Mon Sep 17 00:00:00 2001
From: Aaron Weaver <aaronweaver@users.noreply.github.com>
Date: Tue, 5 Dec 2017 12:39:16 -0500
Subject: [PATCH] Detailed error checking on API and CI/CD example

---
 defectdojo_api/__init__.py      |   2 +-
 defectdojo_api/__init__.pyc     | Bin 179 -> 179 bytes
 defectdojo_api/defectdojo.py    |  10 ++-
 defectdojo_api/defectdojo.pyc   | Bin 28952 -> 29066 bytes
 examples/dojo_ci_cd.py          |  47 ++++++-----
 examples/reports/junit_dojo.xml | 140 +++++++++++---------------------
 6 files changed, 81 insertions(+), 118 deletions(-)

diff --git a/defectdojo_api/__init__.py b/defectdojo_api/__init__.py
index cd7ca49..a6221b3 100644
--- a/defectdojo_api/__init__.py
+++ b/defectdojo_api/__init__.py
@@ -1 +1 @@
-__version__ = '1.0.1'
+__version__ = '1.0.2'
diff --git a/defectdojo_api/__init__.pyc b/defectdojo_api/__init__.pyc
index 5f38ea2182acbfceababe7c606f570079949a091..c08c5a16b1ac3d5d52545e457d74e87a69876fd3 100644
GIT binary patch
delta 20
bcmdnYxS5fi`7<vUV<gu^c6~;}iRLu`HtGc)

delta 20
bcmdnYxS5fi`7<w<6Eo*Tc6~;JiRLu`HcSNv

diff --git a/defectdojo_api/defectdojo.py b/defectdojo_api/defectdojo.py
index eb341fc..74d1c08 100644
--- a/defectdojo_api/defectdojo.py
+++ b/defectdojo_api/defectdojo.py
@@ -873,19 +873,21 @@ class DefectDojoAPI(object):
                     return DefectDojoResponse(message="Upload complete", data=data, success=True)
                 elif response.status_code == 204: #Object updates
                     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
-                    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:
-                    return DefectDojoResponse(message="Unauthorized.", success=False)
+                    return DefectDojoResponse(message="Unauthorized.", success=False, data=response.text)
                 elif response.status_code == 414:
                     return DefectDojoResponse(message="Request-URI Too Large.", success=False)
                 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:
                     data = response.json()
                     return DefectDojoResponse(message="Success", data=data, success=True, response_code=response.status_code)
             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:
             return DefectDojoResponse(message='An SSL error occurred.', success=False)
         except requests.exceptions.ConnectionError:
diff --git a/defectdojo_api/defectdojo.pyc b/defectdojo_api/defectdojo.pyc
index 6c04adba708cf3fcb6eafca7f12fc865a2465078..badd30575f3e36980753985618f98ed84d464224 100644
GIT binary patch
delta 566
zcmbR7h_UN2BRlhFUM{PfY8%=A#xwRzW=-f}44ynGVG84f$?A#o>Tffa2r;CHGo;8e
zq)0N9h%lt^Fw}4`WbrXHGcX5(SiB4=atzr_3`J~AP^I#Clq!Ig8p4z+;!&!^kj=!*
zP!v2_CrP57mmx(3thElNRTY<328Lz^hFTT|sB6@~PG$kC7G_9M2Romap_zdtI6O~_
zfu%&0A)AY#=q3{*Bh0TFV8zIOWny5SJTXZmlZ6?fN)xK8nSm*orG%X!ON=2)0&JER
zk}54MswBax5|LDCGn7a%)G#n)aWOPAGKi<>Oy*0Ll5a;+p^IcrM2a3mu!jC-_hdUp
z*$Ipc48@`h3=FPCMfpVv`N_$pMX4zYnRyD10iK(iQ>2+03ntG`_mq@j<Y$CoVGx^%
zhf$4@mr-%^-}De>#-7PR*+&>BZ5Gc-W@MZ(xjJ_*<J`@bd7g}n3nn+`&t+_ztXS}k
Gu>k-h8+bzi

delta 429
zcmeBr%sAr_BRlhFUasT-u8r(};~8xyvnKQ~vP_<oFon@;vU=jY`b&%@LJTS53@Ne<
zDUu8&A`Hz8Ou;oA3|YJkDRK<iObkU|U@GJ>RVaW}h%rIUP{dTB1Xf`IQ=yEe!k2-e
znSr5}g#l`U3PTDHLk$a9l`un!Dg(qR%?vET;dzP-EG420*<1`oT}+IOFlVWO6@wi<
z`9YEh?^Yx^^~sFMl8m<}A50RS%#qBaED6>v#Ej6b$xtH2P{Y8G#l_If$RM7gH90m}
zO5OrVg*L+c;D{6*hF}fd&C`<Y7&m`Tkz{7fnarEvDJsm!&j`m%JdA3Lyo?f?^D{!2
v8M`JQ$v(n3VRL6rG9%-($!~M_GS1$-I?t1lao*&w`EwatCQmN-##jdc#fM@%

diff --git a/examples/dojo_ci_cd.py b/examples/dojo_ci_cd.py
index 8f6cfca..15dd0ba 100644
--- a/examples/dojo_ci_cd.py
+++ b/examples/dojo_ci_cd.py
@@ -9,9 +9,6 @@ from datetime import datetime, timedelta
 import os, sys
 import argparse
 import time
-import junit_xml_output
-
-DEBUG = True
 
 test_cases = []
 
@@ -22,18 +19,17 @@ def junit(toolName, file):
         print "Writing Junit test files"
         file.write(junit_xml.dump())
 
-def dojo_connection(host, api_key, user):
+def dojo_connection(host, api_key, user, proxy):
     #Optionally, specify a proxy
-    proxies = {
-      'http': 'http://localhost:8081',
-      'https': 'http://localhost:8081',
-    }
-
-    #if DEBUG:
-        # Instantiate the DefectDojo api wrapper
+    proxies = None
+    if proxy:
+        proxies = {
+          'http': proxy,
+          'https': proxy,
+        }
+
+    # Instantiate the DefectDojo api wrapper
     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
     # Workflow as follows:
@@ -42,9 +38,10 @@ def dojo_connection(host, api_key, user):
     # 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
 
-def return_engagement(dd, product_id):
+def return_engagement(dd, product_id, user):
     #Specify the product id
     product_id = product_id
+    engagement_id = None
 
     # Check for a CI/CD engagement_id
     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):
             print "Uploading " + scannerName + " scan: " + file
             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
-    #print os.path.basename(full_path)
 
 def create_findings(dd, engagement_id, scanner, file, build=None):
     # Upload the scanner export
@@ -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
         #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()
-        for i in range(15):
+        for i in range(5):
             time.sleep(2)
             sys.stdout.write(".")
             sys.stdout.flush()
 
         findings = dd.list_findings(test_id_in=test_ids, duplicate="false", limit=500)
         if findings.count() > 0:
+            """
             for finding in findings.data["objects"]:
                 test_cases.append(junit_xml_output.TestCase(finding["title"] + " Severity: " + finding["severity"], finding["description"],"failure"))
             if not os.path.exists("reports"):
                 os.mkdir("reports")
             junit("DefectDojo", "reports/junit_dojo.xml")
+            """
 
         print"\n=============================================="
         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=
         print
         print"=============================================="
 
-        strFail = ""
+        strFail = None
         if max_critical is not None:
             if sum_new_findings[4] > max_critical:
                 strFail =  "Build Failed: Max Critical"
@@ -234,10 +235,11 @@ def print_findings(findings):
 class Main:
     if __name__ == "__main__":
         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('--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('--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)
@@ -261,10 +263,11 @@ class Main:
         max_high = args["high"]
         max_medium = args["medium"]
         build = args["build"]
+        proxy = args["proxy"]
 
         if dir is not None or file is not None:
-            dd = dojo_connection(host, api_key, user)
-            engagement_id = return_engagement(dd, product_id)
+            dd = dojo_connection(host, api_key, user, proxy=proxy)
+            engagement_id = return_engagement(dd, product_id, user)
             test_ids = None
             if file is not None:
                 if scanner is not None:
diff --git a/examples/reports/junit_dojo.xml b/examples/reports/junit_dojo.xml
index 642ff46..4922d28 100644
--- a/examples/reports/junit_dojo.xml
+++ b/examples/reports/junit_dojo.xml
@@ -1,97 +1,55 @@
 <?xml version="1.0" ?>
-<testsuite failures="4" name="DefectDojo" tests="4">
-	<testcase name="blacklist Severity: Info">
-		<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>
+<testsuite failures="13" name="DefectDojo" tests="13">
+	<testcase name="Application Error Disclosure Severity: Medium">
+		<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 name="SQL injection Severity: High">
-		<failure>SQL injection vulnerabilities arise when user-controllable data is
-incorporated into database SQL queries in an unsafe manner. An attacker can
-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 name="HTTP Parameter Override Severity: Medium">
+		<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;
+Reference: &lt;p&gt;http://download.oracle.com/javaee-archive/servlet-spec.java.net/jsr340-experts/att-0317/OnParameterPollutionAttacks.pdf&lt;/p&gt;</failure>
 	</testcase>
-	<testcase name="Cross-origin resource sharing Severity: Info">
-		<failure>An HTML5 cross-origin resource sharing (CORS) policy controls whether and how
-content running on other domains can perform two-way interaction with the
-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 name="Weak Authentication Method Severity: Medium">
+		<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;
+Reference: &lt;p&gt;www.owasp.org/index.php/Category:Authentication_Vulnerability&lt;/p&gt;</failure>
 	</testcase>
-	<testcase name="Cross-origin resource sharing: arbitrary origin trusted Severity: Info">
-		<failure>An HTML5 cross-origin resource sharing (CORS) policy controls whether and how
-content running on other domains can perform two-way interaction with the
-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.
-
-Trusting arbitrary origins effectively disables the same-origin policy,
-allowing two-way interaction by third-party web sites. Unless the response
-consists only of unprotected public content, this policy is likely to present
-a security risk.
-
-If the site specifies the header Access-Control-Allow-Credentials: true,
-third-party sites may be able to carry out privileged actions and retrieve
-sensitive information. Even if it does not, attackers may be able to bypass
-any IP-based access controls by proxying through users' browsers.
-
-
-
-The application implements an HTML5 cross-origin resource sharing (CORS)
-policy for this request that allows access from any domain.  
-  
-The application allowed access from the requested origin
-**https://pfcxuvwamstc.com**  
-  
-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 name="X-Frame-Options Header Not Set Severity: Medium">
+		<failure>&lt;p&gt;X-Frame-Options header is not included in the HTTP response to protect against 'ClickJacking' attacks.&lt;/p&gt;
+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>
+	</testcase>
+	<testcase name="Absence of Anti-CSRF Tokens Severity: Low">
+		<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>
+	</testcase>
+	<testcase name="Content-Type Header Missing Severity: Low">
+		<failure>&lt;p&gt;The Content-Type header was either missing or empty.&lt;/p&gt;
+Reference: &lt;p&gt;http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx&lt;/p&gt;</failure>
+	</testcase>
+	<testcase name="Cookie No HttpOnly Flag Severity: Low">
+		<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;
+Reference: &lt;p&gt;http://www.owasp.org/index.php/HttpOnly&lt;/p&gt;</failure>
+	</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>
+	</testcase>
+	<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;
+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>
+	</testcase>
+	<testcase name="Private IP Disclosure Severity: Low">
+		<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;
+Reference: &lt;p&gt;https://tools.ietf.org/html/rfc1918&lt;/p&gt;</failure>
+	</testcase>
+	<testcase name="Web Browser XSS Protection Not Enabled Severity: Low">
+		<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;
+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>
+	<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>
 </testsuite>
-- 
GitLab