Skip to content

Commit fc6589e

Browse files
authored
feat: support INFO for severity accross the objects using it (#2652)
* feat: support INFO for severity accross the objects using it * fixup * fixup for findings assessment widgets * missing migrtion - will be squashed * fixup * fixup * squashing the migrations (manually) * status alignment for findings
1 parent 59ee5a7 commit fc6589e

8 files changed

Lines changed: 166 additions & 34 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Generated manually
2+
3+
from django.db import migrations, models
4+
5+
6+
def shift_severity_values(apps, schema_editor):
7+
"""
8+
Shift existing severity values up by 1 to make room for INFO level.
9+
OLD -> NEW mapping:
10+
0 (LOW) -> 1
11+
1 (MEDIUM) -> 2
12+
2 (HIGH) -> 3
13+
3 (CRITICAL) -> 4
14+
-1 (UNDEFINED) stays -1
15+
"""
16+
SecurityException = apps.get_model("core", "SecurityException")
17+
Finding = apps.get_model("core", "Finding")
18+
Vulnerability = apps.get_model("core", "Vulnerability")
19+
20+
# Update in reverse order to avoid conflicts
21+
for model in [SecurityException, Finding, Vulnerability]:
22+
model.objects.filter(severity=3).update(severity=4) # CRITICAL: 3 -> 4
23+
model.objects.filter(severity=2).update(severity=3) # HIGH: 2 -> 3
24+
model.objects.filter(severity=1).update(severity=2) # MEDIUM: 1 -> 2
25+
model.objects.filter(severity=0).update(severity=1) # LOW: 0 -> 1
26+
27+
28+
class Migration(migrations.Migration):
29+
dependencies = [
30+
("core", "0103_alter_terminology_field_path"),
31+
]
32+
33+
operations = [
34+
# Update Finding status choices
35+
migrations.AlterField(
36+
model_name="finding",
37+
name="status",
38+
field=models.CharField(
39+
choices=[
40+
("--", "Undefined"),
41+
("identified", "Identified"),
42+
("confirmed", "Confirmed"),
43+
("dismissed", "Dismissed"),
44+
("assigned", "Assigned"),
45+
("in_progress", "In Progress"),
46+
("mitigated", "Mitigated"),
47+
("resolved", "Resolved"),
48+
("closed", "Closed"),
49+
("deprecated", "Deprecated"),
50+
],
51+
default="--",
52+
max_length=32,
53+
verbose_name="Status",
54+
),
55+
),
56+
# Shift existing severity data
57+
migrations.RunPython(shift_severity_values, migrations.RunPython.noop),
58+
# Update severity field choices with INFO level
59+
migrations.AlterField(
60+
model_name="finding",
61+
name="severity",
62+
field=models.SmallIntegerField(
63+
choices=[
64+
(-1, "undefined"),
65+
(0, "info"),
66+
(1, "low"),
67+
(2, "medium"),
68+
(3, "high"),
69+
(4, "critical"),
70+
],
71+
default=-1,
72+
verbose_name="Severity",
73+
),
74+
),
75+
migrations.AlterField(
76+
model_name="securityexception",
77+
name="severity",
78+
field=models.SmallIntegerField(
79+
choices=[
80+
(-1, "undefined"),
81+
(0, "info"),
82+
(1, "low"),
83+
(2, "medium"),
84+
(3, "high"),
85+
(4, "critical"),
86+
],
87+
default=-1,
88+
verbose_name="Severity",
89+
),
90+
),
91+
migrations.AlterField(
92+
model_name="vulnerability",
93+
name="severity",
94+
field=models.SmallIntegerField(
95+
choices=[
96+
(-1, "undefined"),
97+
(0, "info"),
98+
(1, "low"),
99+
(2, "medium"),
100+
(3, "high"),
101+
(4, "critical"),
102+
],
103+
default=-1,
104+
verbose_name="Severity",
105+
),
106+
),
107+
]

backend/core/models.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,11 @@ def get_locales(self):
227227

228228
class Severity(models.IntegerChoices):
229229
UNDEFINED = -1, "undefined"
230-
LOW = 0, "low"
231-
MEDIUM = 1, "medium"
232-
HIGH = 2, "high"
233-
CRITICAL = 3, "critical"
230+
INFO = 0, "info"
231+
LOW = 1, "low"
232+
MEDIUM = 2, "medium"
233+
HIGH = 3, "high"
234+
CRITICAL = 4, "critical"
234235

235236

236237
class StoredLibrary(LibraryMixin):
@@ -5529,27 +5530,29 @@ def get_findings_metrics(self):
55295530
# Severity distribution using the defined severity levels - we need a better way for this
55305531
severity_values = {
55315532
-1: "undefined",
5532-
0: "low",
5533-
1: "medium",
5534-
2: "high",
5535-
3: "critical",
5533+
0: "info",
5534+
1: "low",
5535+
2: "medium",
5536+
3: "high",
5537+
4: "critical",
55365538
}
55375539

55385540
severity_distribution = {}
55395541
for value, label in severity_values.items():
55405542
severity_distribution[label] = findings.filter(severity=value).count()
55415543

55425544
# Count of unresolved important findings (severity is HIGH or CRITICAL)
5543-
# Excludes findings that are mitigated, resolved, or dismissed
5545+
# Excludes findings that are mitigated, resolved, dismissed, or closed
55445546
unresolved_important = (
55455547
findings.filter(
5546-
severity__gte=2 # HIGH or CRITICAL (>=2)
5548+
severity__gte=3 # HIGH or CRITICAL (>=3)
55475549
)
55485550
.exclude(
55495551
status__in=[
55505552
Finding.Status.MITIGATED,
55515553
Finding.Status.RESOLVED,
55525554
Finding.Status.DISMISSED,
5555+
Finding.Status.CLOSED,
55535556
]
55545557
)
55555558
.count()
@@ -5573,6 +5576,7 @@ class Status(models.TextChoices):
55735576
IN_PROGRESS = "in_progress", _("In Progress")
55745577
MITIGATED = "mitigated", _("Mitigated")
55755578
RESOLVED = "resolved", _("Resolved")
5579+
CLOSED = "closed", _("Closed")
55765580
DEPRECATED = "deprecated", _("Deprecated")
55775581

55785582
findings_assessment = models.ForeignKey(

backend/core/tests/test_vulnerability.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def test_serialization(self, domain_perimeter_fixture, applied_controls):
9090
description="Cross-site scripting vulnerability in comments section",
9191
folder=folder,
9292
status="open",
93-
severity=3,
93+
severity=4,
9494
created_at="2025-01-13T10:00:00Z",
9595
updated_at="2025-01-13T10:00:00Z",
9696
)
@@ -101,7 +101,7 @@ def test_serialization(self, domain_perimeter_fixture, applied_controls):
101101

102102
assert data["ref_id"] == "VULN-2025-002"
103103
assert data["name"] == "XSS in Comments"
104-
assert data["severity"] == 3
104+
assert data["severity"] == 4
105105
assert (
106106
data["folder"]
107107
== hashlib.sha256(str(getattr(folder, "pk")).encode()).hexdigest()[:12]

backend/core/views.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6724,6 +6724,7 @@ def metrics(self, request, pk=None):
67246724

67256725
def format_severity_data(metrics):
67266726
severity_colors = {
6727+
"info": "#3B82F6",
67276728
"low": "#59BBB2",
67286729
"medium": "#F5C481",
67296730
"high": "#E6686D",
@@ -6906,6 +6907,7 @@ def md(self, request, pk):
69066907
Finding.Status.DISMISSED,
69076908
Finding.Status.MITIGATED,
69086909
Finding.Status.RESOLVED,
6910+
Finding.Status.CLOSED,
69096911
Finding.Status.DEPRECATED,
69106912
]
69116913
open_statuses = [
@@ -7030,6 +7032,7 @@ def pdf(self, request, pk):
70307032
Finding.Status.DISMISSED,
70317033
Finding.Status.MITIGATED,
70327034
Finding.Status.RESOLVED,
7035+
Finding.Status.CLOSED,
70337036
Finding.Status.DEPRECATED,
70347037
]
70357038
open_statuses = [

backend/data_wizard/views.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,11 @@ def _process_findings_assessment(self, request, records, folder_id, perimeter_id
368368
)
369369

370370
SEVERITY_MAP = {
371-
"low": 0,
372-
"medium": 1,
373-
"high": 2,
374-
"critical": 3,
371+
"info": 0,
372+
"low": 1,
373+
"medium": 2,
374+
"high": 3,
375+
"critical": 4,
375376
}
376377

377378
for record in records:

frontend/messages/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,7 @@
953953
"tooManyLoginAttempts": "Too many login attempts. Please try again later.",
954954
"step": "Step {number}",
955955
"critical": "Critical",
956+
"info": "Information",
956957
"vulnerabilities": "Vulnerabilities",
957958
"severity": "Severity",
958959
"potential": "Potential",

frontend/messages/fr.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,7 @@
929929
"tooManyLoginAttempts": "Trop de tentatives de connexion. Veuillez réessayer plus tard.",
930930
"step": "Étape {number}",
931931
"critical": "Critique",
932+
"info": "Information",
932933
"vulnerabilities": "Vulnérabilités",
933934
"severity": "Niveau de gravité",
934935
"potential": "Potentielle",

frontend/src/routes/(app)/(internal)/findings-assessments/[id=uuid]/+page.svelte

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@
1515
1616
let { data, form }: Props = $props();
1717
let exportPopupOpen = $state(false);
18+
let chartKey = $state(0);
19+
20+
function resizeObserver(node: HTMLElement) {
21+
const observer = new ResizeObserver(() => {
22+
chartKey = chartKey + 1;
23+
});
24+
observer.observe(node);
25+
return {
26+
destroy() {
27+
observer.disconnect();
28+
}
29+
};
30+
}
1831
</script>
1932

2033
{#if data.data?.is_locked}
@@ -93,24 +106,26 @@
93106
</div>
94107
</div>
95108

96-
<div class="card p-4 bg-gray-50 shadow-xs grow">
97-
<div class="h-1/2">
98-
<HalfDonutChart
99-
name="current_h"
100-
title={m.severity()}
101-
classesContainer="flex-1 card p-4 bg-white"
102-
values={data.findings_metrics.severity_chart_data}
103-
colors={data.findings_metrics.severity_chart_data.map((object) => object.color)}
104-
/>
105-
</div>
106-
<div class="h-1/2">
107-
<DonutChart
108-
classesContainer="flex-1 card p-4 bg-white"
109-
name="f_treatment_progress"
110-
title={m.progress()}
111-
values={data.findings_metrics.status_chart_data.values}
112-
/>
113-
</div>
109+
<div class="card p-2 bg-gray-50 shadow-xs flex-1 flex flex-col gap-2" use:resizeObserver>
110+
{#key chartKey}
111+
<div class="flex-1 min-h-0">
112+
<HalfDonutChart
113+
name="current_h"
114+
title={m.severity()}
115+
classesContainer="card p-2 bg-white h-full"
116+
values={data.findings_metrics.severity_chart_data}
117+
colors={data.findings_metrics.severity_chart_data.map((object) => object.color)}
118+
/>
119+
</div>
120+
<div class="flex-1 min-h-0">
121+
<DonutChart
122+
classesContainer="card p-2 bg-white h-full"
123+
name="f_treatment_progress"
124+
title={m.progress()}
125+
values={data.findings_metrics.status_chart_data.values}
126+
/>
127+
</div>
128+
{/key}
114129
</div>
115130
</div>
116131
{/key}

0 commit comments

Comments
 (0)