Skip to content

Commit 0422cf2

Browse files
feat(contracts): declare expected security platform types on expectations (#313) (#314)
1 parent 6d649c7 commit 0422cf2

2 files changed

Lines changed: 86 additions & 1 deletion

File tree

pyoaev/contracts/contract_config.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,25 @@ class ExpectationType(str, Enum):
7474
vulnerability: str = "VULNERABILITY"
7575

7676

77+
class SecurityPlatformType(str, Enum):
78+
"""Categories of security platform expected to fulfil a technical expectation.
79+
80+
When an expectation declares one or more of these, the platform focuses the
81+
technical (DETECTION / PREVENTION / VULNERABILITY) result on collectors of
82+
those types only (instead of every connected security platform). An empty
83+
list means "any security platform".
84+
"""
85+
86+
EDR: str = "EDR"
87+
XDR: str = "XDR"
88+
SIEM: str = "SIEM"
89+
SOAR: str = "SOAR"
90+
NDR: str = "NDR"
91+
ISPM: str = "ISPM"
92+
LLM_FIREWALL: str = "LLM_FIREWALL"
93+
AI_GATEWAY: str = "AI_GATEWAY"
94+
95+
7796
@dataclass
7897
class Expectation:
7998
expectation_type: ExpectationType
@@ -83,6 +102,12 @@ class Expectation:
83102
expectation_expectation_group: bool
84103
expectation_is_predefined: bool = False
85104
expectation_is_multi_selectable: bool = False
105+
# Security platform types expected to fulfil this expectation. Empty = any
106+
# platform (unchanged behaviour). Typically set for technical expectations
107+
# (DETECTION / PREVENTION / VULNERABILITY), left empty for MANUAL ones.
108+
expectation_expected_security_platform_types: List[SecurityPlatformType] = field(
109+
default_factory=list
110+
)
86111

87112

88113
@dataclass

test/contracts/test_contract_expectations.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@
77
ContractExpectations,
88
Expectation,
99
ExpectationType,
10+
SecurityPlatformType,
1011
)
1112

1213

13-
def _expectation(expectation_type, name, is_predefined):
14+
def _expectation(expectation_type, name, is_predefined, expected_platforms=None):
15+
# Only pass the field when declared so the default (None) path exercises
16+
# the dataclass default_factory instead of an explicit empty list.
17+
kwargs = {}
18+
if expected_platforms is not None:
19+
kwargs["expectation_expected_security_platform_types"] = expected_platforms
1420
return Expectation(
1521
expectation_type=expectation_type,
1622
expectation_name=name,
1723
expectation_description="",
1824
expectation_score=100,
1925
expectation_expectation_group=False,
2026
expectation_is_predefined=is_predefined,
27+
**kwargs,
2128
)
2229

2330

@@ -104,6 +111,59 @@ def test_no_flag_yields_no_predefined(self):
104111

105112
self.assertEqual([], data["predefinedExpectations"])
106113

114+
def test_expected_security_platform_types_default_empty(self):
115+
"""An expectation without declared platforms serializes an empty list."""
116+
field = ContractExpectations(
117+
key="expectations",
118+
label="Expectations",
119+
availableExpectations=[
120+
_expectation(ExpectationType.detection, "Detection", True),
121+
],
122+
)
123+
124+
data = _serialize(field)
125+
126+
self.assertEqual(
127+
[],
128+
data["availableExpectations"][0][
129+
"expectation_expected_security_platform_types"
130+
],
131+
)
132+
133+
def test_expected_security_platform_types_serialize(self):
134+
"""Declared platform types serialize as their string values on both
135+
the available and predefined arrays."""
136+
field = ContractExpectations(
137+
key="expectations",
138+
label="Expectations",
139+
availableExpectations=[
140+
_expectation(
141+
ExpectationType.detection,
142+
"Detection",
143+
True,
144+
expected_platforms=[
145+
SecurityPlatformType.EDR,
146+
SecurityPlatformType.XDR,
147+
],
148+
),
149+
],
150+
)
151+
152+
data = _serialize(field)
153+
154+
self.assertEqual(
155+
["EDR", "XDR"],
156+
data["availableExpectations"][0][
157+
"expectation_expected_security_platform_types"
158+
],
159+
)
160+
self.assertEqual(
161+
["EDR", "XDR"],
162+
data["predefinedExpectations"][0][
163+
"expectation_expected_security_platform_types"
164+
],
165+
)
166+
107167

108168
if __name__ == "__main__":
109169
unittest.main()

0 commit comments

Comments
 (0)