Skip to content

Commit 1af9f3a

Browse files
committed
define serialization functions
for rba and risk bits in line with normal pydantic workflow
1 parent 3e2b421 commit 1af9f3a

File tree

2 files changed

+67
-19
lines changed

2 files changed

+67
-19
lines changed

contentctl/objects/abstract_security_content_objects/detection_abstract.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -383,21 +383,17 @@ def providing_technologies(self) -> List[ProvidingTechnology]:
383383
@computed_field
384384
@property
385385
def risk(self) -> list[dict[str, Any]]:
386-
risk_objects: list[dict[str, str | int]] = []
387-
388-
for entity in self.rba.risk_objects:
389-
risk_object: dict[str, str | int] = dict()
390-
risk_object["risk_object_type"] = entity.type
391-
risk_object["risk_object_field"] = entity.field
392-
risk_object["risk_score"] = entity.score
393-
risk_objects.append(risk_object)
394-
395-
for entity in self.rba.threat_objects:
396-
threat_object: dict[str, str] = dict()
397-
threat_object["threat_object_field"] = entity.field
398-
threat_object["threat_object_type"] = entity.type
399-
risk_objects.append(threat_object)
400-
return risk_objects
386+
if self.rba is None:
387+
raise Exception(
388+
f"Attempting to serialize rba section of [{self.name}], however RBA section is None"
389+
)
390+
"""
391+
action.risk.param._risk
392+
of the conf file only contains a list of dicts. We do not eant to
393+
include the message here, so we do not return it.
394+
"""
395+
rba_dict = self.rba.model_dump()
396+
return rba_dict["risk_objects"] + rba_dict["threat_objects"]
401397

402398
@computed_field
403399
@property

contentctl/objects/rba.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
from enum import Enum
2-
from pydantic import BaseModel, computed_field, Field
1+
from __future__ import annotations
2+
33
from abc import ABC
4-
from typing import Set, Annotated
5-
from contentctl.objects.enums import RiskSeverity
4+
from enum import Enum
5+
from typing import Annotated, Any, Set
66

7+
from pydantic import BaseModel, Field, computed_field, model_serializer
8+
9+
from contentctl.objects.enums import RiskSeverity
710

811
RiskScoreValue_Type = Annotated[int, Field(ge=1, le=100)]
912

@@ -42,6 +45,7 @@ class ThreatObjectType(str, Enum):
4245
TLS_HASH = "tls_hash"
4346
URL = "url"
4447

48+
#[{"risk_object_field": "dest", "risk_object_type": "system", "risk_score": 64}, {"threat_object_field": "src_ip", "threat_object_type": "ip_address"}]
4549

4650
class RiskObject(BaseModel):
4751
field: str
@@ -51,6 +55,28 @@ class RiskObject(BaseModel):
5155
def __hash__(self):
5256
return hash((self.field, self.type, self.score))
5357

58+
def __lt__(self, other: RiskObject) -> bool:
59+
if (
60+
f"{self.field}{self.type}{self.score}"
61+
< f"{other.field}{other.type}{other.score}"
62+
):
63+
return True
64+
return False
65+
66+
@model_serializer
67+
def serialize_risk_object(self) -> dict[str, str | int]:
68+
'''
69+
We define this explicitly for two reasons, even though the automatic
70+
serialization works correctly. First we want to enforce a specific
71+
field order for reasons of readability. Second, some of the fields
72+
actually have different names than they do in the object.
73+
'''
74+
return{
75+
"risk_object_field": self.field,
76+
"risk_object_type": self.type,
77+
"risk_score": self.score
78+
}
79+
5480

5581
class ThreatObject(BaseModel):
5682
field: str
@@ -59,6 +85,24 @@ class ThreatObject(BaseModel):
5985
def __hash__(self):
6086
return hash((self.field, self.type))
6187

88+
def __lt__(self, other: ThreatObject) -> bool:
89+
if f"{self.field}{self.type}" < f"{other.field}{other.type}":
90+
return True
91+
return False
92+
93+
@model_serializer
94+
def serialize_threat_object(self) -> dict[str, str]:
95+
'''
96+
We define this explicitly for two reasons, even though the automatic
97+
serialization works correctly. First we want to enforce a specific
98+
field order for reasons of readability. Second, some of the fields
99+
actually have different names than they do in the object.
100+
'''
101+
return{
102+
"threat_object_field": self.field,
103+
"threat_object_type": self.type,
104+
}
105+
62106

63107
class RBAObject(BaseModel, ABC):
64108
message: str
@@ -94,3 +138,11 @@ def severity(self) -> RiskSeverity:
94138
raise Exception(
95139
f"Error getting severity - risk_score must be between 0-100, but was actually {self.risk_score}"
96140
)
141+
142+
@model_serializer
143+
def serialize_rba(self) -> dict[str, str | list[dict[str, str | int]]]:
144+
return {
145+
"message": self.message,
146+
"risk_objects": [obj.model_dump() for obj in sorted(self.risk_objects)],
147+
"threat_objects": [obj.model_dump() for obj in sorted(self.threat_objects)],
148+
}

0 commit comments

Comments
 (0)