Coverage for product_risk_suite / product / models.py: 98%
64 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-13 23:42 +0000
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-13 23:42 +0000
1from django.db import models
3from collections import OrderedDict
5from risk_assessment.models import Risk, RiskRating, RiskMitigation, Evidence, Risk5x5, Status
6from threat_model.shared_models import ThreatModelConnectionName
8class ProductRiskEntry(models.Model):
9 id = models.AutoField(primary_key=True)
10 risk = models.ForeignKey(Risk, on_delete=models.PROTECT)
11 risk_accepted = models.BooleanField(default=False)
12 risk_rating_initial = models.ForeignKey(RiskRating, on_delete=models.PROTECT, related_name='initial')
13 risk_mitigation = models.ForeignKey(RiskMitigation, on_delete=models.PROTECT, blank=True, null=True)
14 evidences = models.ManyToManyField(Evidence, blank=True)
15 risk_rating_after_mitigation = models.ForeignKey(RiskRating, on_delete=models.PROTECT, related_name='after_mitigation', blank=True, null=True)
17 svg_id = models.ManyToManyField(ThreatModelConnectionName, blank=True)
19 def __str__(self):
20 if self.risk_accepted:
21 return f"Accepted Risk: {self.risk.custom_id} - {self.risk.title}"
22 if self.risk_mitigation:
23 r = self.risk_rating_after_mitigation.risk if self.risk_rating_after_mitigation else "forgotten"
24 return f"Mitigated Risk: {self.risk.custom_id} - {self.risk.title} Risk: {r}"
25 return f"*Un*mitigated Risk: {self.risk.custom_id} - {self.risk.title} Open risk: {self.risk_rating_initial.risk}"
27 @property
28 def list_svg_ids(self):
29 return [id.name for id in self.svg_id.all()]
31class ProductRiskAnalysis(models.Model):
32 id = models.AutoField(primary_key=True)
33 name = models.CharField(max_length=200, unique=True)
34 risk_entries = models.ManyToManyField(ProductRiskEntry)
35 slug = models.SlugField(default="", null=False, unique=True)
37 def __str__(self):
38 return self.name
40 @staticmethod
41 def get_risk_summary(risk_entries: ProductRiskEntry):
42 retval = {
43 "risk_severities_before_mitigation" : { Risk5x5.LOW.name : 0, Risk5x5.MID.name: 0, Risk5x5.HIGH.name: 0},
44 "risk_severities_after_mitigation" : { Risk5x5.LOW.name : 0, Risk5x5.MID.name: 0, Risk5x5.HIGH.name: 0},
45 "risk_severity_colors": { Risk5x5.LOW.name : "#d0e6dc", Risk5x5.MID.name: "#fff3cc", Risk5x5.HIGH.name: "#f8d6d9"},
46 "n_risks": len(risk_entries.all()),
47 "unmitigated_risks": [],
48 "mitigation_stati": {},
49 "covered_sec_reqs" : []
50 }
51 sec_reqs = {}
52 mitigation_stati = {}
53 for k,v in Status.STATUS_CHOICES.items():
54 mitigation_stati[Status.web_idx_name(k)] = 0
55 for e in risk_entries.all():
56 rate = RiskRating.risk_level(e.risk_rating_initial.risk)
57 retval["risk_severities_before_mitigation"][rate.name] = retval["risk_severities_before_mitigation"][rate.name] + 1
58 if e.risk_mitigation:
59 if e.risk_rating_after_mitigation:
60 rate = RiskRating.risk_level(e.risk_rating_after_mitigation.risk)
61 else:
62 retval["unmitigated_risks"].append(e.risk.custom_id)
63 retval["risk_severities_after_mitigation"][rate.name] = retval["risk_severities_after_mitigation"][rate.name] + 1
64 if e.risk_mitigation:
65 for sr in e.risk_mitigation.list_security_requirements:
66 sec_reqs[sr["str"]] = sr["slug"]
67 if e.evidences:
68 for ev in e.evidences.all():
69 mitigation_stati[Status.web_idx_name(ev.status.status)] = mitigation_stati[Status.web_idx_name(ev.status.status)] + 1
70 retval["covered_sec_reqs"] = OrderedDict(sorted(sec_reqs.items()))
71 retval["mitigation_stati"] = mitigation_stati
73 return retval
75class Product(models.Model):
76 id = models.AutoField(primary_key=True)
77 title = models.CharField(max_length=200, unique=True)
78 description = models.TextField()
79 slug = models.SlugField(default="", null=False, unique=True)
81 analyzes = models.ManyToManyField(ProductRiskAnalysis, blank=True)
83 class Meta:
84 permissions = ()
86 def __str__(self):
87 return self.title