1+ from typing import Annotated , Optional
12from pydantic import Field , computed_field
2- from typing import Annotated , Optional , Union
33
4- from bbot_server .errors import BBOTServerValueError
4+ from bbot_server .models . base import BaseScore
55from bbot_server .models .asset_models import BaseAssetFacet
66
77# Severity levels as constants
88SEVERITY_LEVELS = {"INFO" : 1 , "LOW" : 2 , "MEDIUM" : 3 , "HIGH" : 4 , "CRITICAL" : 5 }
99
10+ # Confidence levels as constants
11+ CONFIDENCE_LEVELS = {"UNKNOWN" : 1 , "LOW" : 2 , "MODERATE" : 3 , "HIGH" : 4 , "CONFIRMED" : 5 }
12+
1013# severity colors for rich, etc. (bash color names)
1114SEVERITY_COLORS = {
1215 1 : "deep_sky_blue1" , # INFO = blue
1720}
1821
1922
20- class SeverityScore :
23+ class SeverityScore ( BaseScore ) :
2124 """Maps severity levels to numeric scores and provides conversion methods."""
2225
23- @classmethod
24- def to_score (cls , severity : Union [str , int ]) -> int :
25- """Convert a severity level to its numeric score."""
26- if isinstance (severity , int ):
27- if severity not in SEVERITY_LEVELS .values ():
28- raise BBOTServerValueError (f'Invalid severity score: "{ severity } ". Must be between 1 and 5.' )
29- return severity
30- if isinstance (severity , str ):
31- severity = severity .upper ()
32- if severity not in SEVERITY_LEVELS :
33- raise BBOTServerValueError (
34- f'Invalid severity string: "{ severity } ". Must be one of { list (SEVERITY_LEVELS .keys ())} '
35- )
36- return SEVERITY_LEVELS [severity ]
37-
38- @classmethod
39- def to_severity (cls , score : int ) -> str :
40- """Convert a numeric score to its string equivalent."""
41- for level , value in SEVERITY_LEVELS .items ():
42- if value == score :
43- return level
44- raise BBOTServerValueError (f"Invalid severity score: { score } . Must be between 1 and 5." )
26+ levels = SEVERITY_LEVELS
27+ name = "severity"
28+
29+
30+ class ConfidenceScore (BaseScore ):
31+ """Maps confidence levels to numeric scores and provides conversion methods."""
32+
33+ levels = CONFIDENCE_LEVELS
34+ name = "confidence"
4535
4636
4737class Finding (BaseAssetFacet ):
@@ -53,11 +43,12 @@ class Finding(BaseAssetFacet):
5343 ge = 1 ,
5444 le = 5 ,
5545 )
56- confidence : Annotated [int , "indexed" ] = Field (
57- description = "Confidence level of the vulnerability (1-5)" ,
46+ confidence_score : Annotated [int , "indexed" ] = Field (
47+ description = "Numeric confidence score of the vulnerability (1-5)" ,
5848 ge = 1 ,
5949 le = 5 ,
6050 default = 1 ,
51+ alias = "confidence" ,
6152 )
6253 temptation : Optional [Annotated [int , "indexed" ]] = Field (
6354 description = "Likelihood of an attacker taking interest in this finding (1-5)" ,
@@ -75,6 +66,10 @@ def __init__(self, **kwargs):
7566 severity = kwargs .pop ("severity" , None )
7667 if severity is not None :
7768 kwargs ["severity_score" ] = SeverityScore .to_score (severity )
69+ # convert confidence to confidence_score
70+ confidence = kwargs .pop ("confidence" , None )
71+ if confidence is not None :
72+ kwargs ["confidence_score" ] = ConfidenceScore .to_score (confidence )
7873 super ().__init__ (** kwargs )
7974
8075 @computed_field
@@ -83,7 +78,15 @@ def severity(self) -> str:
8378 """
8479 The string version of the severity score, e.g. 3 -> "MEDIUM", 4 -> "HIGH", etc.
8580 """
86- return SeverityScore .to_severity (self .severity_score )
81+ return SeverityScore .to_str (self .severity_score )
82+
83+ @computed_field
84+ @property
85+ def confidence (self ) -> str :
86+ """
87+ The string version of the confidence score, e.g. 1 -> "UNKNOWN", 5 -> "CONFIRMED", etc.
88+ """
89+ return ConfidenceScore .to_str (self .confidence_score )
8790
8891 @computed_field
8992 @property
0 commit comments