diff --git a/components/package.json b/components/package.json index 3496b28c137..f36765a9a80 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "defectdojo", - "version": "2.35.0", + "version": "2.35.1", "license" : "BSD-3-Clause", "private": true, "dependencies": { diff --git a/dojo/__init__.py b/dojo/__init__.py index 8c662903c28..523f51fdd80 100644 --- a/dojo/__init__.py +++ b/dojo/__init__.py @@ -4,6 +4,6 @@ # Django starts so that shared_task will use this app. from .celery import app as celery_app # noqa: F401 -__version__ = '2.35.0' +__version__ = '2.35.1' __url__ = 'https://github.com/DefectDojo/django-DefectDojo' __docs__ = 'https://documentation.defectdojo.com' diff --git a/dojo/api_v2/serializers.py b/dojo/api_v2/serializers.py index 6c9b3ee48f8..51dd30fa0b0 100644 --- a/dojo/api_v2/serializers.py +++ b/dojo/api_v2/serializers.py @@ -3013,8 +3013,8 @@ def validate(self, data): class UserProfileSerializer(serializers.Serializer): user = UserSerializer(many=False) - user_contact_info = UserContactInfoSerializer(many=False) - global_role = GlobalRoleSerializer(many=False) + user_contact_info = UserContactInfoSerializer(many=False, required=False) + global_role = GlobalRoleSerializer(many=False, required=False) dojo_group_member = DojoGroupMemberSerializer(many=True) product_type_member = ProductTypeMemberSerializer(many=True) product_member = ProductMemberSerializer(many=True) diff --git a/dojo/filters.py b/dojo/filters.py index d7ae4b6fef5..0b12cc3961f 100644 --- a/dojo/filters.py +++ b/dojo/filters.py @@ -1663,6 +1663,8 @@ def set_date_fields(self, *args: list, **kwargs: dict): class FindingFilterWithoutObjectLookups(FindingFilterHelper, FindingTagStringFilter): + test__engagement__product__prod_type = NumberFilter(widget=HiddenInput()) + test__engagement__product = NumberFilter(widget=HiddenInput()) reporter = CharFilter( field_name="reporter__username", lookup_expr="iexact", @@ -1673,13 +1675,13 @@ class FindingFilterWithoutObjectLookups(FindingFilterHelper, FindingTagStringFil lookup_expr="icontains", label="Reporter Username Contains", help_text="Search for Reporter names that contain a given pattern") - reviewer = CharFilter( - field_name="reviewer__username", + reviewers = CharFilter( + field_name="reviewers__username", lookup_expr="iexact", label="Reviewer Username", help_text="Search for Reviewer names that are an exact match") - reviewer_contains = CharFilter( - field_name="reviewer__username", + reviewers_contains = CharFilter( + field_name="reviewers__username", lookup_expr="icontains", label="Reviewer Username Contains", help_text="Search for Reviewer usernames that contain a given pattern") @@ -2471,6 +2473,7 @@ class Meta: class EndpointFilterWithoutObjectLookups(EndpointFilterHelper): + product = NumberFilter(widget=HiddenInput()) product__name = CharFilter( field_name="product__name", lookup_expr="iexact", diff --git a/dojo/importers/default_importer.py b/dojo/importers/default_importer.py index 70c71ab31e9..5cf38f38a73 100644 --- a/dojo/importers/default_importer.py +++ b/dojo/importers/default_importer.py @@ -215,7 +215,7 @@ def process_findings( group_name, findings, self.group_by, - self.create_finding_groups_for_all_findings, + create_finding_groups_for_all_findings=self.create_finding_groups_for_all_findings, **kwargs ) if self.push_to_jira: diff --git a/dojo/importers/default_reimporter.py b/dojo/importers/default_reimporter.py index 6d24854d64b..e313afbd1d7 100644 --- a/dojo/importers/default_reimporter.py +++ b/dojo/importers/default_reimporter.py @@ -688,6 +688,8 @@ def process_groups_for_all_findings( finding_helper.add_findings_to_auto_group( group_name, findings, + self.group_by, + create_finding_groups_for_all_findings=self.create_finding_groups_for_all_findings, **kwargs ) if self.push_to_jira: diff --git a/dojo/importers/options.py b/dojo/importers/options.py index ec73488e928..52b291ecaf1 100644 --- a/dojo/importers/options.py +++ b/dojo/importers/options.py @@ -15,9 +15,9 @@ Endpoint, Engagement, Finding, + Product_API_Scan_Configuration, Test, Test_Import, - Tool_Configuration, ) from dojo.utils import get_current_user, is_finding_groups_enabled @@ -45,7 +45,7 @@ def load_base_options( **kwargs: dict, ): self.active: bool = self.validate_active(*args, **kwargs) - self.api_scan_configuration: Tool_Configuration | None = self.validate_api_scan_configuration(*args, **kwargs) + self.api_scan_configuration: Product_API_Scan_Configuration | None = self.validate_api_scan_configuration(*args, **kwargs) self.apply_tags_to_endpoints: bool = self.validate_apply_tags_to_endpoints(*args, **kwargs) self.apply_tags_to_findings: bool = self.validate_apply_tags_to_findings(*args, **kwargs) self.branch_tag: str = self.validate_branch_tag(*args, **kwargs) @@ -227,10 +227,10 @@ def validate_api_scan_configuration( self, *args: list, **kwargs: dict, - ) -> Tool_Configuration | None: + ) -> Product_API_Scan_Configuration | None: return self.validate( "api_scan_configuration", - expected_types=[Tool_Configuration], + expected_types=[Product_API_Scan_Configuration], required=False, default=None, **kwargs, diff --git a/dojo/tools/netsparker/parser.py b/dojo/tools/netsparker/parser.py index 1a61276fda8..e0cbce557cb 100644 --- a/dojo/tools/netsparker/parser.py +++ b/dojo/tools/netsparker/parser.py @@ -51,8 +51,8 @@ def get_findings(self, filename, test): url = item["Url"] impact = html2text.html2text(item.get("Impact", "")) dupe_key = title - request = item["HttpRequest"]["Content"] - response = item["HttpResponse"]["Content"] + request = item["HttpRequest"].get("Content", None) + response = item["HttpResponse"].get("Content", None) finding = Finding( title=title, @@ -89,7 +89,7 @@ def get_findings(self, filename, test): ) if len(cvss_objects) > 0: finding.cvssv3 = cvss_objects[0].clean_vector() - finding.unsaved_req_resp = [{"req": request, "resp": response}] + finding.unsaved_req_resp = [{"req": str(request), "resp": str(response)}] finding.unsaved_endpoints = [Endpoint.from_uri(url)] if dupe_key in dupes: diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index 31b548564ca..8403b51cc38 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 -appVersion: "2.35.0" +appVersion: "2.35.1" description: A Helm chart for Kubernetes to install DefectDojo name: defectdojo -version: 1.6.132 +version: 1.6.133 icon: https://www.defectdojo.org/img/favicon.ico maintainers: - name: madchap diff --git a/unittests/scans/netsparker/issue_10311.json b/unittests/scans/netsparker/issue_10311.json new file mode 100644 index 00000000000..3157fafb142 --- /dev/null +++ b/unittests/scans/netsparker/issue_10311.json @@ -0,0 +1,173 @@ +{ + "Generated": "03/02/2019 15:50:29 (UTC-06:00)", + "Target": { + "ScanId": "fg49hk5", + "Url": "https://www.sampleweb.org/", + "Initiated": "03/02/2019 15:48:23 (UTC-06:00)", + "Duration": "00:01:20.4322725" + }, + "Vulnerabilities": [ + { + "Url": "https://www.sampleweb.org/", + "Type": "CookieNotMarkedAsSecure", + "Name": "Cookie Not Marked as Secure", + "Severity": "High", + "Certainty": 100, + "Confirmed": true, + "Classification": { + "Owasp": "A6", + "Owasp2017": "A3", + "Wasc": "15", + "Cwe": "614", + "Capec": "102", + "Pci31": "6.5.10", + "Pci32": "6.5.10", + "Hipaa": null + }, + "HttpRequest": { + "Method": "GET", + "Content": "GET / HTTP/1.1\r\nHost: www.sampleweb.org\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: en-us,en;q=0.5\r\nCache-Control: no-cache\r\nUser-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)\r\nX-Scanner: Netsparker\r\n\r\n", + "Parameters": [ ] + }, + "HttpResponse": { + "StatusCode": 200, + "Duration": 644.6389, + "Content": "HTTP/1.1 200 OK\r\nX-Cache: MISS\r\nX-Timer: S1551563304.277046,VS0,VE20\r\nAge: 0\r\nCache-Control: max-age=600\r\nETag: W/\"5b8fd2e9-6807\"\r\nAccess-Control-Allow-Origin: *\r\nX-Fastly-Request-ID: 0345654a04250c6d1c420d386643c1f6dc7c3c24\r\nX-Served-By: cache-chi21166-CHI\r\nConnection: keep-alive\r\nExpires: Sat, 02 Mar 2019 21:58:24 GMT\r\nAccept-Ranges: bytes\r\nX-Cache-Hits: 0\r\nContent-Length: 5954\r\nX-GitHub-Request-Id: 0820:594C:6A9400:84F805:5C7AFA26\r\nVary: Accept-Encoding\r\nVia: 1.1 varnish\r\nLast-Modified: Wed, 05 Sep 2018 12:58:17 GMT\r\nContent-Type: text/html; charset=utf-8\r\nServer: GitHub.com\r\nDate: Sat, 02 Mar 2019 21:48:24 GMT\r\nContent-Encoding: \r\n\r\n" + }, + "ExtraInformation": [ + { + "Name": "Identified Cookie(s)", + "Value": "cookieconsent_status" + }, + { + "Name": "Cookie Source", + "Value": "JavaScript" + } + ], + "KnownVulnerabilities": [ ], + "Description": "

Netsparker identified a cookie not marked as secure, and transmitted over HTTPS.

This means the cookie could potentially be stolen by an attacker who can successfully intercept and decrypt the traffic, or following a successful man-in-the-middle attack.

", + "Impact": "
This cookie will be transmitted over a HTTP connection, therefore if this cookie is important (such as a session cookie), an attacker might intercept it and hijack a victim's session. If the attacker can carry out a man-in-the-middle attack, he/she can force the victim to make an HTTP request to steal the cookie.
", + "RemedialActions": "
  1. See the remedy for solution.
  2. Mark all cookies used within the application as secure. (If the cookie is not related to authentication or does not carry any personal information, you do not have to mark it as secure.)
", + "ExploitationSkills": "
To exploit this issue, the attacker needs to be able to intercept traffic. This generally requires local access to the web server or to the victim's network. Attackers need to be understand layer 2, have physical access to systems either as waypoints for the traffic, or have locally gained access to to a system between the victim and the web server.
", + "RemedialProcedure": "
Mark all cookies used within the application as secure.
", + "RemedyReferences": "", + "ExternalReferences": "
", + "ProofOfConcept": "" + }, + { + "Url": "https://www.sampleweb.org/", + "Type": "BootstrapjsOutOfDate", + "Name": "Out-of-date Version (Bootstrap)", + "Severity": "Medium", + "Certainty": 90, + "Confirmed": false, + "Classification": { + "Owasp": "A9", + "Owasp2017": "A9", + "Wasc": null, + "Cwe": null, + "Capec": "310", + "Pci31": "6.2", + "Pci32": "6.2", + "Hipaa": null + }, + "HttpRequest": { + "Method": "GET", + "Content": "GET / HTTP/1.1\r\nHost: www.sampleweb.org\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: en-us,en;q=0.5\r\nCache-Control: no-cache\r\nUser-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)\r\nX-Scanner: Netsparker\r\n\r\n", + "Parameters": [ ] + }, + "HttpResponse": { + "StatusCode": 200, + "Duration": 644.6389, + "Content": "HTTP/1.1 200 OK\r\nX-Cache: MISS\r\nX-Timer: S1551563304.277046,VS0,VE20\r\nAge: 0\r\nCache-Control: max-age=600\r\nETag: W/\"5b8fd2e9-6807\"\r\nAccess-Control-Allow-Origin: *\r\nX-Fastly-Request-ID: 0345654a04250c6d1c420d386643c1f6dc7c3c24\r\nX-Served-By: cache-chi21166-CHI\r\nConnection: keep-alive\r\nExpires: Sat, 02 Mar 2019 21:58:24 GMT\r\nAccept-Ranges: bytes\r\nX-Cache-Hits: 0\r\nContent-Length: 5954\r\nX-GitHub-Request-Id: 0820:594C:6A9400:84F805:5C7AFA26\r\nVary: Accept-Encoding\r\nVia: 1.1 varnish\r\nLast-Modified: Wed, 05 Sep 2018 12:58:17 GMT\r\nContent-Type: text/html; charset=utf-8\r\nServer: GitHub.com\r\nDate: Sat, 02 Mar 2019 21:48:24 GMT\r\nContent-Encoding: \r\n\r\n" + }, + "ExtraInformation": [ + { + "Name": "Identified Version", + "Value": "4.0.0" + }, + { + "Name": "Latest Version", + "Value": "4.3.1" + }, + { + "Name": "Vulnerability Database", + "Value": "Result is based on 3/1/2019 vulnerability database content." + } + ], + "KnownVulnerabilities": [ + { + "Title": "bootstrap.js Cross-Site Scripting (XSS) Vulnerability", + "Severity": "Medium" + }, + { + "Title": "bootstrap.js Cross-Site Scripting (XSS) Vulnerability", + "Severity": "Medium" + }, + { + "Title": "bootstrap.js Cross-Site Scripting (XSS) Vulnerability", + "Severity": "Medium" + }, + { + "Title": "bootstrap.js Cross-Site Scripting (XSS) Vulnerability", + "Severity": "Medium" + } + ], + "Description": "

Netsparker identified that the target web site is using Bootstrap and detected that it is out of date.

", + "Impact": "
Since this is an old version of the software, it may be vulnerable to attacks.
", + "RemedialActions": "", + "ExploitationSkills": "", + "RemedialProcedure": "
\n

Please upgrade your installation of Bootstrap to the latest stable version.

\n
", + "RemedyReferences": "
", + "ExternalReferences": "", + "ProofOfConcept": "" + }, + { + "Url": "https://www.sampleweb.org/", + "Type": "CookieNotMarkedAsHttpOnly", + "Name": "Cookie Not Marked as HttpOnly", + "Severity": "Low", + "Certainty": 100, + "Confirmed": true, + "Classification": { + "Owasp": "A5", + "Owasp2017": "A6", + "Wasc": "15", + "Cwe": "16", + "Capec": "107", + "Pci31": null, + "Pci32": null, + "Hipaa": null + }, + "HttpRequest": { + "Method": "GET", + "Content": "GET / HTTP/1.1\r\nHost: www.sampleweb.org\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: en-us,en;q=0.5\r\nCache-Control: no-cache\r\nUser-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)\r\nX-Scanner: Netsparker\r\n\r\n", + "Parameters": [ ] + }, + "HttpResponse": { + "StatusCode": 200, + "Duration": 644.6389, + "Content": null + }, + "ExtraInformation": [ + { + "Name": "Identified Cookie(s)", + "Value": "cookieconsent_status" + }, + { + "Name": "Cookie Source", + "Value": "JavaScript" + } + ], + "KnownVulnerabilities": [ ], + "Description": "

Netsparker identified a cookie not marked as HTTPOnly.

HTTPOnly cookies cannot be read by client-side scripts, therefore marking a cookie as HTTPOnly can provide an additional layer of protection against cross-site scripting attacks.

", + "Impact": "
During a cross-site scripting attack, an attacker might easily access cookies and hijack the victim's session.
", + "RemedialActions": "
  1. See the remedy for solution.
  2. Consider marking all of the cookies used by the application as HTTPOnly. (After these changes javascript code will not be able to read cookies.)
", + "ExploitationSkills": "", + "RemedialProcedure": "
Mark the cookie as HTTPOnly. This will be an extra layer of defense against XSS. However this is not a silver bullet and will not protect the system against cross-site scripting attacks. An attacker can use a tool such as XSS Tunnel to bypass HTTPOnly protection.
", + "RemedyReferences": "", + "ExternalReferences": "
", + "ProofOfConcept": "" + } + ] +} \ No newline at end of file diff --git a/unittests/tools/test_netsparker_parser.py b/unittests/tools/test_netsparker_parser.py index cf8b9837b84..c3c3f33604b 100644 --- a/unittests/tools/test_netsparker_parser.py +++ b/unittests/tools/test_netsparker_parser.py @@ -83,3 +83,17 @@ def test_parse_file_issue_9816(self): self.assertEqual("High", finding.severity) self.assertEqual(614, finding.cwe) self.assertEqual("03/02/2019", finding.date.strftime("%d/%m/%Y")) + + def test_parse_file_issue_10311(self): + with open("unittests/scans/netsparker/issue_10311.json") as testfile: + parser = NetsparkerParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(3, len(findings)) + for finding in findings: + for endpoint in finding.unsaved_endpoints: + endpoint.clean() + with self.subTest(i=0): + finding = findings[0] + self.assertEqual("High", finding.severity) + self.assertEqual(614, finding.cwe) + self.assertEqual("03/02/2019", finding.date.strftime("%d/%m/%Y"))