1
1
from django .db import models
2
+ from django .contrib .contenttypes import models as ct_models
2
3
3
4
4
5
class Person (models .Model ):
@@ -12,3 +13,72 @@ class Application(models.Model):
12
13
director = models .ForeignKey ('Person' , blank = True , null = True , on_delete = models .SET_NULL , related_name = '+' )
13
14
director_direct = models .ForeignKey ('Person' , blank = True , null = True , on_delete = models .SET_NULL , related_name = '+' )
14
15
dev_lead = models .ForeignKey ('Person' , blank = True , null = True , on_delete = models .SET_NULL , related_name = '+' )
16
+
17
+
18
+ class FindingInheritanceQS (models .QuerySet ):
19
+ def get_children (self ) -> list :
20
+ return [
21
+ getattr (m , m .content_source .model )
22
+ for m in self .prefetch_related ("content_source__model__finding_ptr" ).select_related ('content_source' )
23
+ ]
24
+
25
+
26
+ class Finding (models .Model ):
27
+ class Severity (models .IntegerChoices ):
28
+ INFORMATIVE = 1
29
+ LOW = 2
30
+ MEDIUM = 3
31
+ HIGH = 4
32
+ CRITICAL = 5
33
+
34
+ class State (models .IntegerChoices ):
35
+ """
36
+ States represent a point in the workflow.
37
+ States are not Status.
38
+ Do not add a state if the transitions for that state are the same as an existing one.
39
+ """
40
+
41
+ # to be reviewed by Security Testing: NEW -> OPEN/CLOSED
42
+ NEW = 1
43
+ # viewed by the teams, included in score: OPEN -> CLOSED
44
+ OPEN = 2
45
+ # no score, nothing to do. Final state.
46
+ CLOSED = 3
47
+ # resolved/mitigated, can be re-open: RESOLVED -> NEW/OPEN
48
+ RESOLVED = 4
49
+
50
+ content_source = models .ForeignKey (ct_models .ContentType , on_delete = models .CASCADE )
51
+
52
+ title = models .TextField (blank = True )
53
+ summary = models .TextField (null = True , blank = True )
54
+ severity = models .IntegerField (null = True , blank = True , choices = Severity .choices , db_index = True )
55
+ state = models .IntegerField (choices = State .choices , default = State .NEW , db_index = True )
56
+
57
+ first_seen = models .DateTimeField (auto_now_add = True )
58
+ last_seen_date = models .DateTimeField (blank = True , null = True )
59
+
60
+ application = models .ForeignKey (
61
+ 'inventory.Application' , blank = True , null = True , on_delete = models .SET_NULL , verbose_name = "Application"
62
+ )
63
+
64
+ related_to = models .ManyToManyField ('self' , blank = True , help_text = 'Other findings related to this one' )
65
+
66
+ objects = FindingInheritanceQS .as_manager ()
67
+
68
+ def __init__ (self , * args , ** kwargs ):
69
+ if 'content_source' not in kwargs :
70
+ kwargs ['content_source' ] = self .content_type ()
71
+ super ().__init__ (* args , ** kwargs )
72
+
73
+ @classmethod
74
+ def content_type (cls ):
75
+ return ct_models .ContentType .objects .get_for_model (cls )
76
+
77
+ @property
78
+ def cached_content_source (self ):
79
+ if self .content_source_id is not None and not Finding .content_source .is_cached (self ):
80
+ self .content_source = ct_models .ContentType .objects .get_for_id (self .content_source_id )
81
+ return self .content_source
82
+
83
+ def __str__ (self ):
84
+ return f'{ self .pk } [{ self .cached_content_source .app_label } ] - { self .title } '
0 commit comments