nihalaninihal commited on
Commit
5014574
·
1 Parent(s): 5efcc1b

Add SLA policy drift support for ticketing system

Browse files

- Add apply_policy_drift() to TicketingSystem that modifies SLA rules
- Update AttackManager to route policy_drift to any system with the method
- Add TICKETING to VALID_TARGETS for policy_drift in RandomizedAttacker
- Add SLA-specific drift configs (e.g., high: 6->3, medium: 12->6)

sentinelops_arena/attacks.py CHANGED
@@ -113,17 +113,22 @@ class AttackManager:
113
  def _execute_policy_drift(
114
  self, target: TargetSystem, params: Dict[str, Any], tick: int
115
  ) -> Dict[str, Any]:
116
- """Modify refund policy fields on the billing system."""
117
  changes = params.get("changes", {})
118
  if not changes:
119
  return {"success": False, "error": "changes dict required"}
120
 
121
- billing = self.systems[TargetSystem.BILLING]
122
- billing.apply_policy_drift(changes)
 
 
 
 
 
123
  return {
124
  "success": True,
125
  "attack": "policy_drift",
126
- "detail": f"Policy changed: {changes}",
127
  }
128
 
129
  def _execute_social_engineering(
 
113
  def _execute_policy_drift(
114
  self, target: TargetSystem, params: Dict[str, Any], tick: int
115
  ) -> Dict[str, Any]:
116
+ """Modify policy fields on the target system (billing or ticketing)."""
117
  changes = params.get("changes", {})
118
  if not changes:
119
  return {"success": False, "error": "changes dict required"}
120
 
121
+ system = self.systems[target]
122
+ if not hasattr(system, "apply_policy_drift"):
123
+ return {
124
+ "success": False,
125
+ "error": f"{target.value} does not support policy drift",
126
+ }
127
+ system.apply_policy_drift(changes)
128
  return {
129
  "success": True,
130
  "attack": "policy_drift",
131
+ "detail": f"Policy changed on {target.value}: {changes}",
132
  }
133
 
134
  def _execute_social_engineering(
sentinelops_arena/demo.py CHANGED
@@ -130,7 +130,7 @@ class RandomizedAttacker:
130
  {"old_field": "notes", "new_field": "annotations"},
131
  ]
132
 
133
- POLICY_DRIFT_CHANGES = [
134
  {"window_ticks": 4, "requires_approval": True, "max_amount": 2000},
135
  {"window_ticks": 2, "requires_approval": True, "max_amount": 500},
136
  {"window_ticks": 6, "requires_approval": False, "max_amount": 10000},
@@ -138,6 +138,14 @@ class RandomizedAttacker:
138
  {"window_ticks": 3, "requires_approval": False, "max_amount": 5000},
139
  ]
140
 
 
 
 
 
 
 
 
 
141
  RATE_LIMIT_OPTIONS = [
142
  {"max_calls_per_tick": 1},
143
  {"max_calls_per_tick": 2},
@@ -158,7 +166,10 @@ class RandomizedAttacker:
158
  **rename,
159
  }
160
  if atype == AttackType.POLICY_DRIFT:
161
- changes = self.rng.choice(self.POLICY_DRIFT_CHANGES)
 
 
 
162
  return {
163
  "attack_type": atype.value,
164
  "target_system": target.value,
@@ -182,7 +193,7 @@ class RandomizedAttacker:
182
  # Valid target systems per attack type (not all systems support all attacks)
183
  VALID_TARGETS = {
184
  AttackType.SCHEMA_DRIFT: [TargetSystem.CRM], # only CRM has apply_schema_drift
185
- AttackType.POLICY_DRIFT: [TargetSystem.BILLING], # only Billing has apply_policy_drift
186
  AttackType.SOCIAL_ENGINEERING: [TargetSystem.CRM, TargetSystem.BILLING, TargetSystem.TICKETING],
187
  AttackType.RATE_LIMIT: [TargetSystem.CRM, TargetSystem.BILLING, TargetSystem.TICKETING],
188
  }
 
130
  {"old_field": "notes", "new_field": "annotations"},
131
  ]
132
 
133
+ POLICY_DRIFT_CHANGES_BILLING = [
134
  {"window_ticks": 4, "requires_approval": True, "max_amount": 2000},
135
  {"window_ticks": 2, "requires_approval": True, "max_amount": 500},
136
  {"window_ticks": 6, "requires_approval": False, "max_amount": 10000},
 
138
  {"window_ticks": 3, "requires_approval": False, "max_amount": 5000},
139
  ]
140
 
141
+ POLICY_DRIFT_CHANGES_TICKETING = [
142
+ {"high": 3, "medium": 6, "low": 10},
143
+ {"high": 2, "medium": 4, "low": 8},
144
+ {"high": 1, "medium": 3, "low": 6},
145
+ {"high": 8, "medium": 16, "low": 24},
146
+ {"high": 4, "medium": 8, "low": 12},
147
+ ]
148
+
149
  RATE_LIMIT_OPTIONS = [
150
  {"max_calls_per_tick": 1},
151
  {"max_calls_per_tick": 2},
 
166
  **rename,
167
  }
168
  if atype == AttackType.POLICY_DRIFT:
169
+ if target == TargetSystem.TICKETING:
170
+ changes = self.rng.choice(self.POLICY_DRIFT_CHANGES_TICKETING)
171
+ else:
172
+ changes = self.rng.choice(self.POLICY_DRIFT_CHANGES_BILLING)
173
  return {
174
  "attack_type": atype.value,
175
  "target_system": target.value,
 
193
  # Valid target systems per attack type (not all systems support all attacks)
194
  VALID_TARGETS = {
195
  AttackType.SCHEMA_DRIFT: [TargetSystem.CRM], # only CRM has apply_schema_drift
196
+ AttackType.POLICY_DRIFT: [TargetSystem.BILLING, TargetSystem.TICKETING],
197
  AttackType.SOCIAL_ENGINEERING: [TargetSystem.CRM, TargetSystem.BILLING, TargetSystem.TICKETING],
198
  AttackType.RATE_LIMIT: [TargetSystem.CRM, TargetSystem.BILLING, TargetSystem.TICKETING],
199
  }
sentinelops_arena/systems/ticketing.py CHANGED
@@ -137,6 +137,12 @@ class TicketingSystem:
137
  "sla_rules": self.sla_rules.model_dump(),
138
  }
139
 
 
 
 
 
 
 
140
  def apply_schema_drift(self, old_field: str, new_field: str):
141
  """Rename a field across all records."""
142
  self._field_map[old_field] = new_field
 
137
  "sla_rules": self.sla_rules.model_dump(),
138
  }
139
 
140
+ def apply_policy_drift(self, changes: Dict):
141
+ """Modify SLA rules fields (mirrors billing policy drift)."""
142
+ data = self.sla_rules.model_dump()
143
+ data.update(changes)
144
+ self.sla_rules = SLARules(**data)
145
+
146
  def apply_schema_drift(self, old_field: str, new_field: str):
147
  """Rename a field across all records."""
148
  self._field_map[old_field] = new_field