From 6c1d1d6b48fbc4e5e21a2a85bbe794f0900c4ea4 Mon Sep 17 00:00:00 2001 From: Harold Blankenship <36673698+hblankenship@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:02:10 -0600 Subject: [PATCH 1/6] Add a filter for Findings for Has Any JIRA (grouped or single) (#11313) * initial attempt * filter by jira * Update dojo/filters.py * Less confusing all return --------- Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> --- dojo/filters.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/dojo/filters.py b/dojo/filters.py index a2c40685cd..6a1228865b 100644 --- a/dojo/filters.py +++ b/dojo/filters.py @@ -235,6 +235,35 @@ def filter(self, qs, value): return self.options[value][1](self, qs, self.field_name) +class FindingHasJIRAFilter(ChoiceFilter): + def no_jira(self, qs, name): + return qs.filter(Q(jira_issue=None) & Q(finding_group__jira_issue=None)) + + def any_jira(self, qs, name): + return qs.filter(~Q(jira_issue=None) | ~Q(finding_group__jira_issue=None)) + + def all_items(self, qs, name): + return qs + + options = { + 0: (_("Yes"), any_jira), + 1: (_("No"), no_jira), + } + + def __init__(self, *args, **kwargs): + kwargs["choices"] = [ + (key, value[0]) for key, value in six.iteritems(self.options)] + super().__init__(*args, **kwargs) + + def filter(self, qs, value): + try: + value = int(value) + except (ValueError, TypeError): + return self.all_items(qs, self.field_name) + + return self.options[value][1](self, qs, self.field_name) + + class ProductSLAFilter(ChoiceFilter): def any(self, qs, name): return qs @@ -1576,6 +1605,7 @@ class FindingFilterHelper(FilterSet): test_import_finding_action__test_import = NumberFilter(widget=HiddenInput()) endpoints = NumberFilter(widget=HiddenInput()) status = FindingStatusFilter(label="Status") + has_component = BooleanFilter( field_name="component_name", lookup_expr="isnull", @@ -1610,6 +1640,7 @@ class FindingFilterHelper(FilterSet): lookup_expr="isnull", exclude=True, label="Has Group JIRA") + has_any_jira = FindingHasJIRAFilter(label="Has Any JIRA") outside_of_sla = FindingSLAFilter(label="Outside of SLA") has_tags = BooleanFilter(field_name="tags", lookup_expr="isnull", exclude=True, label="Has tags") From 100a52dcef642f3afdf9956a82b291989c9603bc Mon Sep 17 00:00:00 2001 From: Harold Blankenship <36673698+hblankenship@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:03:39 -0600 Subject: [PATCH 2/6] Request Review Notification Update to Usernames (#11295) * add username * Update dojo/finding/views.py * Update dojo/finding/views.py --------- Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> --- dojo/finding/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dojo/finding/views.py b/dojo/finding/views.py index a5d6824329..18faed336b 100644 --- a/dojo/finding/views.py +++ b/dojo/finding/views.py @@ -1696,7 +1696,7 @@ def request_finding_review(request, fid): jira_helper.push_to_jira(finding.finding_group) reviewers = Dojo_User.objects.filter(id__in=form.cleaned_data["reviewers"]) - reviewers_string = ", ".join([str(user) for user in reviewers]) + reviewers_string = ", ".join([f"{user} ({user.id})" for user in reviewers]) reviewers_usernames = [user.username for user in reviewers] logger.debug(f"Asking {reviewers_string} for review") @@ -1708,7 +1708,7 @@ def request_finding_review(request, fid): finding=finding, reviewers=reviewers, recipients=reviewers_usernames, - description=f'User {user.get_full_name()} has requested that user(s) {reviewers_string} review the finding "{finding.title}" for accuracy:\n\n{new_note}', + description=f'User {user.get_full_name()}({user.id}) has requested that user(s) {reviewers_string} review the finding "{finding.title}" for accuracy:\n\n{new_note}', icon="check", url=reverse("view_finding", args=(finding.id,)), ) From cdc060ecb3ab60fae4e93cccc9ae19d1b1596688 Mon Sep 17 00:00:00 2001 From: Harold Blankenship <36673698+hblankenship@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:03:57 -0600 Subject: [PATCH 3/6] dissallow already linked issue (#11298) * dissallow already linked issue * Update dojo/jira_link/helper.py * Update dojo/api_v2/serializers.py --------- Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> --- dojo/api_v2/serializers.py | 5 +++++ dojo/jira_link/helper.py | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/dojo/api_v2/serializers.py b/dojo/api_v2/serializers.py index cdc951fdb8..5cdc2db4d8 100644 --- a/dojo/api_v2/serializers.py +++ b/dojo/api_v2/serializers.py @@ -1324,6 +1324,11 @@ def validate(self, data): msg = "Either engagement or finding or finding_group has to be set." raise serializers.ValidationError(msg) + if finding: + if (linked_finding := jira_helper.jira_already_linked(finding, data.get("jira_key"), data.get("jira_id"))) is not None: + msg = "JIRA issue " + data.get("jira_key") + " already linked to " + reverse("view_finding", args=(linked_finding.id,)) + raise serializers.ValidationError(msg) + return data diff --git a/dojo/jira_link/helper.py b/dojo/jira_link/helper.py index f10ea69916..308331987a 100644 --- a/dojo/jira_link/helper.py +++ b/dojo/jira_link/helper.py @@ -1428,6 +1428,13 @@ def add_simple_jira_comment(jira_instance, jira_issue, comment): return False +def jira_already_linked(finding, jira_issue_key, jira_id) -> Finding | None: + jira_issues = JIRA_Issue.objects.filter(jira_id=jira_id, jira_key=jira_issue_key).exclude(engagement__isnull=False) + jira_issues = jira_issues.exclude(finding=finding) + + return jira_issues.first() + + def finding_link_jira(request, finding, new_jira_issue_key): logger.debug("linking existing jira issue %s for finding %i", new_jira_issue_key, finding.id) From e959831000bf005db5a2eb94ece5a8324dd9633b Mon Sep 17 00:00:00 2001 From: manuelsommer <47991713+manuel-sommer@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:04:22 +0100 Subject: [PATCH 4/6] Add DTSA to vulnid (#11302) * Add DTSA to vulnid * sha sum * sha sum * sha sum --- dojo/settings/.settings.dist.py.sha256sum | 2 +- dojo/settings/settings.dist.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dojo/settings/.settings.dist.py.sha256sum b/dojo/settings/.settings.dist.py.sha256sum index 0582378329..5354615b1c 100644 --- a/dojo/settings/.settings.dist.py.sha256sum +++ b/dojo/settings/.settings.dist.py.sha256sum @@ -1 +1 @@ -5172af16b842adfccbedc14bea15ff1da2ee45c10e129e905f156dcdffd27396 +1b1f0b7210b79790c2bf1a3fdb62e24521544600bb4b460ed6a15cfd26f68640 diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index 3bc3372c13..9d364b353f 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1763,6 +1763,9 @@ def saml2_attrib_map_format(dict): "ALSA": "https://osv.dev/vulnerability/", # e.g. https://osv.dev/vulnerability/ALSA-2024:0827 "USN": "https://ubuntu.com/security/notices/", # e.g. https://ubuntu.com/security/notices/USN-6642-1 "DLA": "https://security-tracker.debian.org/tracker/", # e.g. https://security-tracker.debian.org/tracker/DLA-3917-1 + "DSA": "https://security-tracker.debian.org/tracker/", # e.g. https://security-tracker.debian.org/tracker/DSA-5791-1 + "DTSA": "https://security-tracker.debian.org/tracker/", # e.g. https://security-tracker.debian.org/tracker/DTSA-41-1 + "TEMP": "https://security-tracker.debian.org/tracker/", # e.g. https://security-tracker.debian.org/tracker/TEMP-0841856-B18BAF "ELSA": "https://linux.oracle.com/errata/&&.html", # e.g. https://linux.oracle.com/errata/ELSA-2024-12714.html "ELBA": "https://linux.oracle.com/errata/&&.html", # e.g. https://linux.oracle.com/errata/ELBA-2024-7457.html "RXSA": "https://errata.rockylinux.org/", # e.g. https://errata.rockylinux.org/RXSA-2024:4928 @@ -1771,8 +1774,6 @@ def saml2_attrib_map_format(dict): "KHV": "https://avd.aquasec.com/misconfig/kubernetes/", # e.g. https://avd.aquasec.com/misconfig/kubernetes/khv045 "CAPEC": "https://capec.mitre.org/data/definitions/&&.html", # e.g. https://capec.mitre.org/data/definitions/157.html "CWE": "https://cwe.mitre.org/data/definitions/&&.html", # e.g. https://cwe.mitre.org/data/definitions/79.html - "TEMP": "https://security-tracker.debian.org/tracker/", # e.g. https://security-tracker.debian.org/tracker/TEMP-0841856-B18BAF - "DSA": "https://security-tracker.debian.org/tracker/", # e.g. https://security-tracker.debian.org/tracker/DSA-5791-1 "RLSA": "https://errata.rockylinux.org/", # e.g. https://errata.rockylinux.org/RLSA-2024:7001 "RLBA": "https://errata.rockylinux.org/", # e.g. https://errata.rockylinux.org/RLBA-2024:6968 } From dd85ea387910cd599e5dc5b65f0ffe4f9539c0df Mon Sep 17 00:00:00 2001 From: Harold Blankenship <36673698+hblankenship@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:04:41 -0600 Subject: [PATCH 5/6] use minTick and by month (#11304) --- dojo/static/dojo/js/metrics.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dojo/static/dojo/js/metrics.js b/dojo/static/dojo/js/metrics.js index 2fd518aa3a..140c46c1b2 100644 --- a/dojo/static/dojo/js/metrics.js +++ b/dojo/static/dojo/js/metrics.js @@ -57,7 +57,8 @@ function homepage_pie_chart(critical, high, medium, low, info) { function homepage_severity_plot(critical, high, medium, low) { var options = { xaxes: [{ - mode: 'time' + mode: 'time', + minTickSize: [1, "month"] }], yaxes: [{ min: 0 From 08823208b6386255a092856c4408e29ce101438f Mon Sep 17 00:00:00 2001 From: Harold Blankenship <36673698+hblankenship@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:04:57 -0600 Subject: [PATCH 6/6] Disallow multiple single-use notes on a single object (#11306) * do not allow two single use * note type can be null --- dojo/api_v2/views.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index 9676983684..384fc91c97 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -503,6 +503,10 @@ def notes(self, request, pk=None): new_note.errors, status=status.HTTP_400_BAD_REQUEST, ) + notes = engagement.notes.filter(note_type=note_type).first() + if notes and note_type and note_type.is_single: + return Response("Only one instance of this note_type allowed on an engagement.", status=status.HTTP_400_BAD_REQUEST) + author = request.user note = Notes( entry=entry, @@ -1078,6 +1082,11 @@ def notes(self, request, pk=None): new_note.errors, status=status.HTTP_400_BAD_REQUEST, ) + if finding.notes: + notes = finding.notes.filter(note_type=note_type).first() + if notes and note_type and note_type.is_single: + return Response("Only one instance of this note_type allowed on a finding.", status=status.HTTP_400_BAD_REQUEST) + author = request.user note = Notes( entry=entry, @@ -2131,6 +2140,10 @@ def notes(self, request, pk=None): new_note.errors, status=status.HTTP_400_BAD_REQUEST, ) + notes = test.notes.filter(note_type=note_type).first() + if notes and note_type and note_type.is_single: + return Response("Only one instance of this note_type allowed on a test.", status=status.HTTP_400_BAD_REQUEST) + author = request.user note = Notes( entry=entry,