Skip to content

Commit 920be48

Browse files
sanitise and remove reserved field from other_fields
1 parent dc796e5 commit 920be48

File tree

1 file changed

+69
-4
lines changed

1 file changed

+69
-4
lines changed

src/alerts/alert_structs.rs

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,35 @@ use crate::{
3939
storage::object_storage::{alert_json_path, alert_state_json_path},
4040
};
4141

42+
const RESERVED_FIELDS: &[&str] = &[
43+
"version",
44+
"id",
45+
"severity",
46+
"title",
47+
"query",
48+
"datasets",
49+
"alertType",
50+
"alert_type",
51+
"anomalyConfig",
52+
"anomaly_config",
53+
"forecastConfig",
54+
"forecast_config",
55+
"thresholdConfig",
56+
"threshold_config",
57+
"evalConfig",
58+
"eval_config",
59+
"targets",
60+
"state",
61+
"notificationState",
62+
"notification_state",
63+
"notificationConfig",
64+
"notification_config",
65+
"created",
66+
"tags",
67+
"lastTriggeredAt",
68+
"last_triggered_at",
69+
];
70+
4271
/// Helper struct for basic alert fields during migration
4372
pub struct BasicAlertFields {
4473
pub id: Ulid,
@@ -261,15 +290,29 @@ pub struct AlertRequest {
261290
impl AlertRequest {
262291
pub async fn into(self) -> Result<AlertConfig, AlertError> {
263292
// Validate that other_fields doesn't contain reserved field names
264-
if let Some(ref other_fields) = self.other_fields {
293+
let other_fields = if let Some(mut other_fields) = self.other_fields {
265294
// Limit other_fields to maximum 10 fields
266295
if other_fields.len() > 10 {
267296
return Err(AlertError::ValidationFailure(format!(
268297
"other_fields can contain at most 10 fields, found {}",
269298
other_fields.len()
270299
)));
271300
}
272-
}
301+
302+
for reserved in RESERVED_FIELDS {
303+
if other_fields.remove(*reserved).is_some() {
304+
tracing::warn!("Removed reserved field '{}' from other_fields", reserved);
305+
}
306+
}
307+
308+
if other_fields.is_empty() {
309+
None
310+
} else {
311+
Some(other_fields)
312+
}
313+
} else {
314+
None
315+
};
273316

274317
// Validate that all target IDs exist
275318
for id in &self.targets {
@@ -283,6 +326,8 @@ impl AlertRequest {
283326
)));
284327
}
285328

329+
let created_timestamp = Utc::now();
330+
286331
let config = AlertConfig {
287332
version: AlertVersion::from(CURRENT_ALERTS_VERSION),
288333
id: Ulid::new(),
@@ -320,11 +365,12 @@ impl AlertRequest {
320365
state: AlertState::default(),
321366
notification_state: NotificationState::Notify,
322367
notification_config: self.notification_config,
323-
created: Utc::now(),
368+
created: created_timestamp,
324369
tags: self.tags,
325370
last_triggered_at: None,
326-
other_fields: self.other_fields,
371+
other_fields,
327372
};
373+
328374
Ok(config)
329375
}
330376
}
@@ -384,6 +430,25 @@ pub struct AlertConfigResponse {
384430
}
385431

386432
impl AlertConfig {
433+
/// Filters out reserved field names from other_fields
434+
/// This prevents conflicts when flattening other_fields during serialization
435+
pub fn sanitize_other_fields(&mut self) {
436+
if let Some(ref mut other_fields) = self.other_fields {
437+
for reserved in RESERVED_FIELDS {
438+
if other_fields.remove(*reserved).is_some() {
439+
tracing::warn!(
440+
"Removed reserved field '{}' from other_fields during sanitization",
441+
reserved
442+
);
443+
}
444+
}
445+
446+
if other_fields.is_empty() {
447+
self.other_fields = None;
448+
}
449+
}
450+
}
451+
387452
pub fn to_response(self) -> AlertConfigResponse {
388453
AlertConfigResponse {
389454
version: self.version,

0 commit comments

Comments
 (0)