Skip to content

Commit 8ff0705

Browse files
committed
changing names and finding_group logic
1 parent 230c9f4 commit 8ff0705

File tree

5 files changed

+83
-75
lines changed

5 files changed

+83
-75
lines changed

dojo/filters.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,15 +2088,15 @@ class DynamicFindingGroupsFindingsFilter(FilterSet):
20882088
],
20892089
label="Severity",
20902090
)
2091-
script_id = CharFilter(lookup_expr="icontains", label="Script ID")
2091+
vuln_id_from_tool = CharFilter(lookup_expr="icontains", label="Vulnerability Id From Tool")
20922092
reporter = ModelMultipleChoiceFilter(queryset=Dojo_User.objects.none(), label="Reporter")
2093-
status = ChoiceFilter(choices=[("Yes", "Yes"), ("No", "No")], label="Active")
2093+
active = ChoiceFilter(choices=[("Yes", "Yes"), ("No", "No")], label="Active")
20942094
engagement = ModelMultipleChoiceFilter(queryset=Engagement.objects.none(), label="Engagement")
20952095
product = ModelMultipleChoiceFilter(queryset=Product.objects.none(), label="Product")
20962096

20972097
class Meta:
20982098
model = Finding
2099-
fields = ["name", "severity", "script_id", "reporter", "status", "engagement", "product"]
2099+
fields = ["name", "severity", "vuln_id_from_tool", "reporter", "active", "engagement", "product"]
21002100

21012101
def __init__(self, *args, **kwargs):
21022102
self.user = kwargs.pop("user", None)

dojo/finding_group/redis.py

Lines changed: 30 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,16 @@
1010

1111
import redis
1212
from django.conf import settings
13-
from django.utils.safestring import mark_safe
13+
from django.utils.functional import cached_property
1414

1515
from dojo.models import Finding
1616

1717
logger = logging.getLogger(__name__)
1818

1919
DD_TEST = os.getenv("DD_TEST", "False").lower() == "true"
20-
SEVERITY_ORDER = {
21-
"Critical": 5,
22-
"High": 4,
23-
"Medium": 3,
24-
"Low": 2,
25-
"Info": 1,
26-
}
2720
USER_MODES_KEY = "finding_groups_user_modes"
28-
SYSTEM_CHANGE = "finding_groups_last_finding_change"
29-
LAST_UPDATE = "finding_groups_last_update"
21+
LAST_FINDING_CHANGE = "finding_groups_last_finding_change"
22+
LAST_FINDING_UPDATE = "finding_groups_last_update"
3023

3124

3225
class GroupMode(StrEnum):
@@ -41,7 +34,6 @@ class DynamicFindingGroups:
4134
name: str = ""
4235
severity: str = "Info"
4336
main_finding_id: int | None = None
44-
sla_finding_id: int | None = None
4537
finding_ids: set[int] = field(default_factory=set)
4638

4739
def to_dict(self) -> dict:
@@ -63,12 +55,9 @@ def load_from_id(finding_group_id: str, fg_key: str) -> Self | None:
6355
return None
6456

6557
def update_sev_sla(self, finding: Finding) -> None:
66-
if SEVERITY_ORDER[finding.severity] > SEVERITY_ORDER[self.severity]:
58+
if Finding.get_number_severity(finding.severity) > Finding.get_number_severity(self.severity):
6759
self.severity = finding.severity
6860
self.main_finding_id = finding.id
69-
if finding.active and finding.sla_days_remaining():
70-
if not self.sla_finding_id or finding.sla_days_remaining() < Finding.objects.get(id=self.sla_finding_id).sla_days_remaining():
71-
self.sla_finding_id = finding.id
7261

7362
def add(self, finding: Finding) -> None:
7463
self.update_sev_sla(finding)
@@ -77,21 +66,23 @@ def add(self, finding: Finding) -> None:
7766
# This method is used when we filter findings in a finding group
7867
def reconfig_finding_group(self) -> None:
7968
self.severity = "Info"
80-
self.sla_finding_id = None
8169
findings = Finding.objects.filter(id__in=self.finding_ids)
8270
for finding in findings:
8371
self.update_sev_sla(finding)
8472

8573
@staticmethod
8674
def get_group_names(finding: Finding, mode: GroupMode) -> list[str] | None:
8775
if mode == GroupMode.VULN_ID_FROM_TOOL:
88-
return [finding.vuln_id_from_tool]
76+
if finding.vuln_id_from_tool:
77+
return [finding.vuln_id_from_tool]
8978
if mode == GroupMode.TITLE:
90-
return [finding.title]
79+
if finding.title:
80+
return [finding.title]
9181
if mode == GroupMode.CVE:
92-
cves = list(
93-
finding.vulnerability_id_set.values_list("vulnerability_id", flat=True),
94-
)
82+
cves = [
83+
cve for cve in finding.vulnerability_id_set.values_list("vulnerability_id", flat=True)
84+
if cve
85+
]
9586
if cves:
9687
return cves
9788
return None
@@ -110,14 +101,14 @@ def set_last_finding_change() -> None:
110101
logger.info("Redis is not used in test environment, skipping.")
111102
return
112103
redis_client = get_redis_client()
113-
redis_client.set(SYSTEM_CHANGE, datetime.now().isoformat())
104+
redis_client.set(LAST_FINDING_CHANGE, datetime.now().isoformat())
114105

115106
@staticmethod
116107
def set_last_update(mode: GroupMode, timestamp: datetime | None = None) -> None:
117108
if timestamp is None:
118109
return
119110
redis_client = get_redis_client()
120-
redis_client.hset(LAST_UPDATE, mode.value, timestamp.isoformat())
111+
redis_client.hset(LAST_FINDING_UPDATE, mode.value, timestamp.isoformat())
121112

122113
@staticmethod
123114
def add_finding(finding: Finding, mode: GroupMode) -> None:
@@ -147,37 +138,16 @@ def add_finding(finding: Finding, mode: GroupMode) -> None:
147138
group_ids.append(finding_group_id)
148139
redis_client.hset(id_map_key, finding.id, json.dumps(group_ids))
149140

150-
# This method is used in finding_groups table to show SLA
151-
def get_days_remaining(self) -> str:
152-
if self.sla_finding_id:
153-
finding = Finding.objects.filter(id=self.sla_finding_id).first()
154-
days_remaining = finding.sla_days_remaining()
155-
severity = finding.severity
156-
sla_start_date = finding.get_sla_start_date().strftime("%b %d, %Y")
157-
status = "age-green"
158-
status_text = f"Remediation for {severity.lower()} findings due in {days_remaining} days or less (started {sla_start_date})"
159-
if days_remaining and days_remaining < 0:
160-
status = "age-red"
161-
status_text = f"Overdue: Remediation for {severity.lower()} findings overdue {days_remaining} days (started {sla_start_date})"
162-
days_remaining = abs(days_remaining)
163-
elif any(
164-
Finding.objects.filter(
165-
id__in=self.finding_ids,
166-
active=True,
167-
),
168-
):
169-
status = "severity-Info"
170-
status_text = "No SLA set, but at least one finding is active"
171-
days_remaining = "No SLA"
172-
else:
173-
status = "age-blue"
174-
status_text = "No active finding"
175-
days_remaining = "Concluded"
176-
title = (
177-
f'<a class="has-popover" data-toggle="tooltip" data-placement="bottom" title="" href="#" data-content="{status_text}">'
178-
f'<span class="label severity {status}">{days_remaining}</span></a>'
179-
)
180-
return mark_safe(title)
141+
@cached_property
142+
def sla_days_remaining_internal(self):
143+
findings = Finding.objects.filter(id__in=self.finding_ids, active=True)
144+
if not findings:
145+
return None
146+
return min([find.sla_days_remaining() for find in findings if find.sla_days_remaining()], default=None)
147+
148+
@property
149+
def sla_days_remaining(self) -> int | None:
150+
return self.sla_days_remaining_internal
181151

182152

183153
@lru_cache(maxsize=1)
@@ -208,22 +178,22 @@ def load_or_rebuild_finding_groups(mode: GroupMode) -> dict[str, DynamicFindingG
208178
fg_key = DynamicFindingGroups.get_fg_key(mode)
209179
id_map_key = DynamicFindingGroups.get_id_map_key(mode)
210180

211-
if not redis_client.exists(SYSTEM_CHANGE):
181+
if not redis_client.exists(LAST_FINDING_CHANGE):
212182
DynamicFindingGroups.set_last_finding_change()
213-
last_finding_change_raw = redis_client.get(SYSTEM_CHANGE)
183+
last_finding_change_raw = redis_client.get(LAST_FINDING_CHANGE)
214184
try:
215185
last_finding_change_time = datetime.fromisoformat(last_finding_change_raw)
216186
except ValueError:
217-
logger.warning(f"Invalid datetime format in Redis for {SYSTEM_CHANGE}: {last_finding_change_raw}, resetting last finding change.")
187+
logger.warning(f"Invalid datetime format in Redis for {LAST_FINDING_CHANGE}: {last_finding_change_raw}, resetting last finding change.")
218188
DynamicFindingGroups.set_last_finding_change()
219-
last_finding_change_raw = redis_client.get(SYSTEM_CHANGE)
189+
last_finding_change_raw = redis_client.get(LAST_FINDING_CHANGE)
220190
last_finding_change_time = datetime.fromisoformat(last_finding_change_raw) if last_finding_change_raw else None
221191

222192
try:
223-
last_groups_update_time = redis_client.hget(LAST_UPDATE, mode.value)
193+
last_groups_update_time = redis_client.hget(LAST_FINDING_UPDATE, mode.value)
224194
last_groups_update_time = datetime.fromisoformat(last_groups_update_time) if last_groups_update_time else None
225195
except ValueError:
226-
logger.warning(f"Invalid datetime format in Redis for {LAST_UPDATE}: {last_groups_update_time}")
196+
logger.warning(f"Invalid datetime format in Redis for {LAST_FINDING_UPDATE}: {last_groups_update_time}")
227197
last_groups_update_time = None
228198

229199
# Check if finding_groups and id_map exist in Redis

dojo/finding_group/views_dynamic.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from dojo.authorization.roles_permissions import Permissions
99
from dojo.filters import DynamicFindingGroupsFilter, DynamicFindingGroupsFindingsFilter
1010
from dojo.finding_group.redis import (
11-
SEVERITY_ORDER,
1211
GroupMode,
1312
get_user_mode,
1413
load_or_rebuild_finding_groups,
@@ -68,7 +67,7 @@ def filter_finding_group(self, finding_group, request: HttpRequest):
6867
finding_group.reconfig_finding_group()
6968
if name_filter and name_filter not in finding_group.name.lower():
7069
add_finding_group = False
71-
if min_severity_filter and SEVERITY_ORDER.get(finding_group.severity) < SEVERITY_ORDER[min_severity_filter]:
70+
if min_severity_filter and Finding.get_number_severity(finding_group.severity) < Finding.get_number_severity(min_severity_filter):
7271
add_finding_group = False
7372
if not finding_group.finding_ids:
7473
add_finding_group = False
@@ -89,6 +88,19 @@ def get_findings(self, products):
8988
return user_fids, active_fids
9089

9190
def get_finding_groups(self, request: HttpRequest, products=None):
91+
"""
92+
Retrieve all dynamic finding groups for the current user.
93+
94+
Steps:
95+
1. Retrieve finding IDs relevant for the user (optionally filtered by products).
96+
2. Iterate over all finding groups in self.finding_groups_map.
97+
3. For each group:
98+
- Restrict the group's findings to those the user can see.
99+
- Apply additional filters based on the request.
100+
- No additional filtering for active findings.
101+
4. Append groups that pass all filters to the result list.
102+
5. Order the resulting list according to the request via order_field and return.
103+
"""
92104
user_fids, _ = self.get_findings(products)
93105
list_finding_group = []
94106
for finding_group in self.finding_groups_map.values():
@@ -134,6 +146,19 @@ class ListOpenDynamicFindingGroups(ListDynamicFindingGroups):
134146
filter_name = "Open"
135147

136148
def get_finding_groups(self, request: HttpRequest, products=None):
149+
"""
150+
Retrieve dynamic finding groups containing at least one active finding.
151+
152+
Steps:
153+
1. Retrieve finding IDs relevant for the user and the active subset.
154+
2. Iterate over all finding groups in self.finding_groups_map.
155+
3. For each group:
156+
- Restrict the group's findings to those the user can see.
157+
- Apply additional filters based on the request.
158+
- Keep only groups with at least one active finding.
159+
4. Append groups that pass all filters to the result list.
160+
5. Order the resulting list according to the request via order_field and return.
161+
"""
137162
user_fids, active_fids = self.get_findings(products)
138163
list_finding_group = []
139164
for finding_group in self.finding_groups_map.values():
@@ -148,6 +173,19 @@ class ListClosedDynamicFindingGroups(ListDynamicFindingGroups):
148173
filter_name = "Closed"
149174

150175
def get_finding_groups(self, request: HttpRequest, products=None):
176+
"""
177+
Retrieve dynamic finding groups containing no active findings.
178+
179+
Steps:
180+
1. Retrieve finding IDs relevant for the user and the active subset.
181+
2. Iterate over all finding groups in self.finding_groups_map.
182+
3. For each group:
183+
- Restrict the group's findings to those the user can see.
184+
- Apply additional filters based on the request.
185+
- Keep only groups with no active findings.
186+
4. Append groups that pass all filters to the result list.
187+
5. Order the resulting list according to the request via order_field and return.
188+
"""
151189
user_fids, active_fids = self.get_findings(products)
152190
list_finding_group = []
153191
for finding_group in self.finding_groups_map.values():
@@ -177,26 +215,26 @@ def order_field(self, request: HttpRequest, finding_groups_findings_list):
177215
def filters(self, request: HttpRequest):
178216
name_filter = request.GET.get("name", "").lower()
179217
severity_filter = request.GET.getlist("severity")
180-
script_id_filter = request.GET.get("script_id")
218+
vuln_id_from_tool_filter = request.GET.get("vuln_id_from_tool")
181219
reporter_filter = request.GET.getlist("reporter")
182-
status_filter = request.GET.get("status")
220+
active_filter = request.GET.get("active")
183221
engagement_filter = request.GET.getlist("engagement")
184222
product_filter = request.GET.getlist("product")
185-
return name_filter, severity_filter, script_id_filter, reporter_filter, status_filter, engagement_filter, product_filter
223+
return name_filter, severity_filter, vuln_id_from_tool_filter, reporter_filter, active_filter, engagement_filter, product_filter
186224

187225
def filter_findings(self, findings, request: HttpRequest):
188-
name_filter, severity_filter, script_id_filter, reporter_filter, status_filter, engagement_filter, product_filter = self.filters(request)
226+
name_filter, severity_filter, vuln_id_from_tool_filter, reporter_filter, active_filter, engagement_filter, product_filter = self.filters(request)
189227
filter_kwargs = {}
190228
if name_filter:
191229
filter_kwargs["title__icontains"] = name_filter
192230
if severity_filter:
193231
filter_kwargs["severity__in"] = severity_filter
194-
if script_id_filter:
195-
filter_kwargs["vuln_id_from_tool__icontains"] = script_id_filter
232+
if vuln_id_from_tool_filter:
233+
filter_kwargs["vuln_id_from_tool__icontains"] = vuln_id_from_tool_filter
196234
if reporter_filter:
197235
filter_kwargs["reporter__id__in"] = reporter_filter
198-
if status_filter:
199-
filter_kwargs["active"] = (status_filter == "Yes")
236+
if active_filter:
237+
filter_kwargs["active"] = (active_filter == "Yes")
200238
if engagement_filter:
201239
filter_kwargs["test__engagement__id__in"] = engagement_filter
202240
if product_filter:

dojo/templates/dojo/finding_group_dynamic_findings_list_snippet.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ <h3 class="has-filters">
292292
{% trans "Severity" %}
293293
</th>
294294
<th>{% trans "SLA" %}</th>
295-
<th>{% trans "Script ID" %}</th>
295+
<th>{% trans "Vulnerability Id From Tool" %}</th>
296296
<th>{% trans "Reporter" %}</th>
297297
<th>{% dojo_sort request "Found By" "found_by" %}</th>
298298
<th>{% trans "Status" %}</th>

dojo/templates/dojo/finding_groups_dynamic_list_snippet.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ <h3 class="has-filters">
6666
</span>
6767
</td>
6868
<td class="centered">
69-
{{ finding_group.get_days_remaining }}
69+
{{ finding_group.sla_days_remaining }}
7070
</td>
7171
<td class="centered">
7272
{{ finding_group.finding_ids|length }}

0 commit comments

Comments
 (0)