diff --git a/docs/content/en/integrations/source-code-repositories.md b/docs/content/en/integrations/source-code-repositories.md index 99f5d386b2d..743ec6b4272 100644 --- a/docs/content/en/integrations/source-code-repositories.md +++ b/docs/content/en/integrations/source-code-repositories.md @@ -13,13 +13,13 @@ Findings can have a filepath and a line number as the location of the vulnerabil While editing the Engagement, users can set the URL of the specific SCM repo. For Interactive Engagement it needs to be the URL including the branch: - for GitHub - like https://github.com/DefectDojo/django-DefectDojo/tree/dev -![Edit Engagement (GitHub)](../../../static/images/source-code-repositories_1.png) +![Edit Engagement (GitHub)](../../images/source-code-repositories_1.png) - for GitLab - like https://gitlab.com/gitlab-org/gitlab/-/tree/master -![Edit Engagement (Gitlab)](../../../static/images/source-code-repositories-gitlab_1.png) +![Edit Engagement (Gitlab)](../../images/source-code-repositories-gitlab_1.png) - for public BitBucket - like (like git clone url) -![Edit Engagement (Bitbucket public)](../../../static/images/source-code-repositories-bitbucket_1.png) +![Edit Engagement (Bitbucket public)](../../images/source-code-repositories-bitbucket_1.png) - for standalone/onpremise BitBucket https://bb.example.com/scm/some-project/some-repo.git or https://bb.example.com/scm/some-user-name/some-repo.git for user public repo (like git clone url) -![Edit Engagement (Bitbucket standalone)](../../../static/images/source-code-repositories-bitbucket-onpremise_1.png) +![Edit Engagement (Bitbucket standalone)](../../images/source-code-repositories-bitbucket-onpremise_1.png) For CI/CD Engagement, where user could set commit hash, branch/tag and code line it should look like examples below: - for GitHub - like https://github.com/DefectDojo/django-DefectDojo @@ -33,11 +33,11 @@ SCM navigation URL is composed from Repo URL using SCM Type. Github/Gitlab SCM t Product custom fields: -![Product custom fields](../../../static/images/product-custom-fields_1.png) +![Product custom fields](../../images/product-custom-fields_1.png) Product SCM type add: -![Product scm type](../../../static/images/product-scm-type_1.png) +![Product scm type](../../images/product-scm-type_1.png) Possible SCM types could be 'github', 'gitlab', 'bitbucket', 'bitbucket-standalone', 'gitea', 'codeberg' or nothing (for default github). @@ -46,8 +46,8 @@ Possible SCM types could be 'github', 'gitlab', 'bitbucket', 'bitbucket-standalo When viewing a finding, the location will be presented as a link, if the repository of the source code has been set in the Engagement: -![Link to location](../../../static/images/source-code-repositories_2.png) +![Link to location](../../images/source-code-repositories_2.png) Clicking on this link will open a new tab in the browser, with the source file of the vulnerability at the corresponding line number: -![View in repository](../../../static/images/source-code-repositories_3.png) +![View in repository](../../images/source-code-repositories_3.png) diff --git a/dojo/tools/ms_defender/parser.py b/dojo/tools/ms_defender/parser.py index 3ce7c9a562d..3255d586ad1 100644 --- a/dojo/tools/ms_defender/parser.py +++ b/dojo/tools/ms_defender/parser.py @@ -43,7 +43,7 @@ def get_findings(self, file, test): if "machines/" in content and "machines/" != content: machinefiles.append(content) vulnerabilities = [] - machines = [] + machines = {} for vulnerabilityfile in vulnerabilityfiles: output = json.loads(zipdata[vulnerabilityfile].decode('ascii'))['value'] for data in output: @@ -51,12 +51,11 @@ def get_findings(self, file, test): for machinefile in machinefiles: output = json.loads(zipdata[machinefile].decode('ascii'))['value'] for data in output: - machines.append(data) + machines[data.get('id')] = data for vulnerability in vulnerabilities: try: - machine = list(filter(lambda m: m['id'] == vulnerability['machineId'], machines))[0] - self.process_zip(vulnerability, machine) - except IndexError: + self.process_zip(vulnerability, machines[vulnerability['machineId']]) + except (IndexError, KeyError): self.process_json(vulnerability) else: return [] @@ -73,7 +72,7 @@ def process_json(self, vulnerability): title = str(vulnerability['cveId']) finding = Finding( title=title + "_" + vulnerability["machineId"], - severity=vulnerability['severity'], + severity=self.severity_check(vulnerability['severity']), description=description, static_finding=False, dynamic_finding=True, @@ -81,7 +80,8 @@ def process_json(self, vulnerability): if vulnerability['fixingKbId'] is not None: finding.mitigation = vulnerability['fixingKbId'] if vulnerability['cveId'] is not None: - finding.cve = vulnerability['cveId'] + finding.unsaved_vulnerability_ids = [] + finding.unsaved_vulnerability_ids.append(vulnerability['cveId']) self.findings.append(finding) finding.unsaved_endpoints = [] @@ -123,7 +123,7 @@ def process_zip(self, vulnerability, machine): title = title + "_" + str(machine['osPlatform']) finding = Finding( title=title + "_" + vulnerability["machineId"], - severity=vulnerability['severity'], + severity=self.severity_check(vulnerability['severity']), description=description, static_finding=False, dynamic_finding=True, @@ -131,7 +131,8 @@ def process_zip(self, vulnerability, machine): if vulnerability['fixingKbId'] is not None: finding.mitigation = vulnerability['fixingKbId'] if vulnerability['cveId'] is not None: - finding.cve = vulnerability['cveId'] + finding.unsaved_vulnerability_ids = [] + finding.unsaved_vulnerability_ids.append(vulnerability['cveId']) self.findings.append(finding) finding.unsaved_endpoints = [] if machine['computerDnsName'] is not None: @@ -140,3 +141,9 @@ def process_zip(self, vulnerability, machine): finding.unsaved_endpoints.append(Endpoint(host=str(machine['lastIpAddress']))) if machine['lastExternalIpAddress'] is not None: finding.unsaved_endpoints.append(Endpoint(host=str(machine['lastExternalIpAddress']))) + + def severity_check(self, input): + if input in ['Informational', 'Low', 'Medium', 'High', 'Critical']: + return input + else: + return "Informational" diff --git a/helm/defectdojo/Chart.lock b/helm/defectdojo/Chart.lock index e294c5f4721..1c8ee0ad70d 100644 --- a/helm/defectdojo/Chart.lock +++ b/helm/defectdojo/Chart.lock @@ -4,15 +4,15 @@ dependencies: version: 9.19.1 - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 15.4.0 + version: 15.4.2 - name: postgresql-ha repository: https://charts.bitnami.com/bitnami version: 9.4.11 - name: rabbitmq repository: https://charts.bitnami.com/bitnami - version: 14.3.0 + version: 14.3.1 - name: redis repository: https://charts.bitnami.com/bitnami version: 19.4.0 -digest: sha256:5eabae7691eb49cdc15b3924fd5ed661b4eeb8867761a9f5dda68d750a8deaed -generated: "2024-05-22T18:47:38.838187053Z" +digest: sha256:b3de89f81c87a6801052b0a9367a1cf72921b45326436925d87664ff924505d8 +generated: "2024-05-28T18:02:09.89737001Z" diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index ecfd9be4e77..827f8b386a1 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 appVersion: "2.35.0-dev" description: A Helm chart for Kubernetes to install DefectDojo name: defectdojo -version: 1.6.131-dev +version: 1.6.132-dev icon: https://www.defectdojo.org/img/favicon.ico maintainers: - name: madchap diff --git a/unittests/scans/generic/test_import_report1.json b/unittests/scans/generic/test_import_report1.json new file mode 100644 index 00000000000..d3904face82 --- /dev/null +++ b/unittests/scans/generic/test_import_report1.json @@ -0,0 +1,38 @@ +{ + "name": "Unicorn", + "type": "Unicorn", + "findings": [ + { + "title": "Henry Smith", + "description": "small", + "severity": "Critical", + "active": false, + "verified": false, + "is_mitigated": true + }, + { + "title": "Emma Jones", + "description": "small", + "severity": "Critical", + "active": false, + "verified": false, + "is_mitigated": true + }, + { + "title": "Emma Jones", + "description": "small", + "severity": "Critical", + "active": true, + "verified": true, + "is_mitigated": false + }, + { + "title": "Emma Jones", + "description": "small", + "severity": "Critical", + "active": false, + "verified": false, + "is_mitigated": true + } + ] +} \ No newline at end of file diff --git a/unittests/scans/generic/test_import_report2.json b/unittests/scans/generic/test_import_report2.json new file mode 100644 index 00000000000..7cb918277ff --- /dev/null +++ b/unittests/scans/generic/test_import_report2.json @@ -0,0 +1,38 @@ +{ + "name": "Unicorn", + "type": "Unicorn", + "findings": [ + { + "title": "Henry Smith", + "description": "small", + "severity": "Critical", + "active": false, + "verified": false, + "is_mitigated": true + }, + { + "title": "Henry Smith", + "description": "small", + "severity": "Critical", + "active": false, + "verified": false, + "is_mitigated": true + }, + { + "title": "Henry Smith", + "description": "small", + "severity": "Critical", + "active": false, + "verified": false, + "is_mitigated": true + }, + { + "title": "Henry Smith", + "description": "small", + "severity": "Critical", + "active": true, + "verified": true, + "is_mitigated": false + } + ] +} \ No newline at end of file diff --git a/unittests/test_import_reimport.py b/unittests/test_import_reimport.py index 342e771c65b..c2e3aadf568 100644 --- a/unittests/test_import_reimport.py +++ b/unittests/test_import_reimport.py @@ -85,7 +85,10 @@ def __init__(self, *args, **kwargs): self.clair_empty = self.scans_path + 'clair/clair_empty.json' self.scan_type_clair = 'Clair Scan' + self.scan_type_generic = "Generic Findings Import" self.generic_filename_with_file = self.scans_path + "generic/test_with_image.json" + self.generic_import_1 = self.scans_path + "generic/test_import_report1.json" + self.generic_import_2 = self.scans_path + "generic/test_import_report2.json" self.aws_prowler_file_name = self.scans_path + 'aws_prowler/many_vuln.json' self.aws_prowler_file_name_plus_one = self.scans_path + 'aws_prowler/many_vuln_plus_one.json' @@ -1445,6 +1448,15 @@ def test_import_reimport_vulnerability_ids(self): self.assertEqual('GHSA-v6rh-hp5x-86rv', findings[3].vulnerability_ids[0]) self.assertEqual('CVE-2021-44420', findings[3].vulnerability_ids[1]) + def test_import_history_reactivated_and_untouched_findings_do_not_mix(self): + import0 = self.import_scan_with_params(self.generic_import_1, scan_type=self.scan_type_generic) + test_id = import0['test'] + # reimport the second report + self.reimport_scan_with_params(test_id, self.generic_import_2, scan_type=self.scan_type_generic) + # reimport the first report again + self.reimport_scan_with_params(test_id, self.generic_import_1, scan_type=self.scan_type_generic) + # Passing this test means an exception does not occur + class ImportReimportTestAPI(DojoAPITestCase, ImportReimportMixin): fixtures = ['dojo_testdata.json'] diff --git a/unittests/tools/test_ms_defender_parser.py b/unittests/tools/test_ms_defender_parser.py index f8365757fc6..1f8f73ee3fa 100644 --- a/unittests/tools/test_ms_defender_parser.py +++ b/unittests/tools/test_ms_defender_parser.py @@ -4,7 +4,7 @@ from ..dojo_test_case import DojoTestCase -class TestSDefenderParser(DojoTestCase): +class TestMSDefenderParser(DojoTestCase): def test_parse_many_findings(self): testfile = open("unittests/scans/ms_defender/report_many_vulns.json") @@ -25,7 +25,7 @@ def test_parse_one_finding(self): finding = findings[0] self.assertEqual("Low", finding.severity) self.assertEqual("CVE-1234-5678_fjweoifjewiofjweoifjeowifjowei", finding.title) - self.assertEqual("CVE-1234-5678", finding.cve) + self.assertEqual("CVE-1234-5678", finding.unsaved_vulnerability_ids[0]) def test_parse_no_finding(self): testfile = open("unittests/scans/ms_defender/report_no_vuln.json")