Coverage for product_risk_suite/threat_model/models.py: 98%
49 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-07 12:45 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-07 12:45 +0000
1from django.db import models
2from django.utils.text import slugify
4from bs4 import BeautifulSoup
6from product.models import Product
7from .shared_models import ThreatModelConnectionName
9class ThreatModel(models.Model):
10 id = models.AutoField(primary_key=True)
11 product = models.ForeignKey(Product, on_delete=models.PROTECT)
12 slug = models.SlugField(default="", null=False, unique=True)
13 title = models.CharField(max_length=200)
14 description = models.TextField(blank=True, null=True)
15 svg = models.FileField(upload_to='uploads/')
16 connection_names = models.ManyToManyField(ThreatModelConnectionName, blank=True)
18 def __str__(self):
19 return self.title
21 def __remove_svg_attribute(self, svg, tag, attr):
22 for element in svg.findAll(tag):
23 if attr in element.attrs:
24 del element.attrs[attr]
25 return svg
27 def __remove_dark_mode_from_style(self, svg):
28 for element in svg.findAll('svg'):
29 if 'style' in element.attrs:
30 s = element.attrs['style']
31 element.attrs['style'] = s.replace('color-scheme: light dark;', '')
32 return svg
34 def get_clean_svg_content(self):
35 svg_content = None
36 with self.svg.open("r") as f:
37 soup = BeautifulSoup(f, 'xml')
38 soup = self.__remove_svg_attribute(soup, 'svg', 'content')
39 svg_content = str(self.__remove_dark_mode_from_style(soup))
40 return svg_content
42 @property
43 def list_connection_names(self):
44 return [cn.tech_name for cn in self.connection_names.all()]
46 def save(self, *args, **kwargs):
47 self.slug = slugify(f"{self.product.slug} {self.title}")
49 super().save(*args, **kwargs)
51 if self.svg:
52 with self.svg.open('r') as f:
53 soup = BeautifulSoup(f, 'xml')
54 connection_names = []
55 elements = soup.find_all(attrs={"data-cell-id": True})
56 for e in elements:
57 dc = e['data-cell-id']
58 connection_name, _ = ThreatModelConnectionName.objects.get_or_create(product_id=self.product.id, tech_name=dc)
59 connection_names.append(connection_name)
60 self.connection_names.set(connection_names)