From a93339ce88c0efe3b3f3eed5652ca0b596b155e0 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 11 Jul 2025 11:09:21 +0200 Subject: [PATCH 1/3] fix(contexts): Overwrite non-string context types Follow-up to #4925. This replaces any context `"type"` except nonempty strings with the context's key. --- .../src/protocol/contexts/mod.rs | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/relay-event-schema/src/protocol/contexts/mod.rs b/relay-event-schema/src/protocol/contexts/mod.rs index 04ad6d28c66..f0c4a8e9255 100644 --- a/relay-event-schema/src/protocol/contexts/mod.rs +++ b/relay-event-schema/src/protocol/contexts/mod.rs @@ -274,7 +274,7 @@ impl FromValue for Contexts { for (key, value) in items.iter_mut() { if let Annotated(Some(Value::Object(items)), _) = value { // Set the `"type"` if it's empty and overwrite it if it's an empty object or array. - if value_is_empty(items.get("type")) { + if !is_valid_context_type(items.get("type")) { items.insert( "type".to_owned(), Annotated::new(Value::String(key.to_string())), @@ -287,17 +287,10 @@ impl FromValue for Contexts { } } -/// Returns `true` if `value` is `None`, empty object, or an empty array. -fn value_is_empty(value: Option<&Annotated>) -> bool { - let Some(value) = value.and_then(|v| v.value()) else { - return true; - }; - - match value { - Value::Array(values) => values.is_empty(), - Value::Object(values) => values.is_empty(), - _ => false, - } +/// Returns `true` if `value` is a nonempty string, which is the only valid form +/// for the `"type"` field of a context. +fn is_valid_context_type(value: Option<&Annotated>) -> bool { + matches!(value.and_then(|v| v.value()), Some(Value::String(s)) if !s.is_empty()) } /// A well-known context in the [`Contexts`] interface. @@ -371,14 +364,28 @@ mod tests { } #[test] - fn test_context_empty_type_deserialize() { - let json = r#"{"os":{"name":"Linux","type":{}},"runtime":{"name":"rustc","type":[]}}"#; + fn test_context_invalid_type_deserialize() { + let json = r#"{ + "monitor":{"name":"Foobar","type":17}, + "os":{"name":"Linux","type":{}}, + "profile":{"profile_id":"52df9022835246eeb317dbd739ccd059","type":""}, + "runtime":{"name":"rustc","type":["invalid"]} + }"#; let mut map = Contexts::new(); + map.add(MonitorContext( + [("name".to_owned(), Value::String("Foobar".to_owned()).into())] + .into_iter() + .collect(), + )); map.add(OsContext { name: Annotated::new("Linux".to_owned()), ..Default::default() }); + map.add(ProfileContext { + profile_id: Annotated::new("52df9022835246eeb317dbd739ccd059".parse().unwrap()), + ..Default::default() + }); map.add(RuntimeContext { name: Annotated::new("rustc".to_owned()), ..Default::default() From 16f24c8c0eca3346cd89ea7313029cbec45c2fca Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 11 Jul 2025 11:22:15 +0200 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e25b738c834..c3128f20e84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - Preserve user specified event values in Unreal crash reports. ([#4882](https://github.com/getsentry/relay/pull/4882)) - OS name parsing of Unreal crash reports. ([#4854](https://github.com/getsentry/relay/pull/4854)) - Do not overwrite geo information if already set. ([#4888](https://github.com/getsentry/relay/pull/4888)) +- The `type` fields of contexts are now enforced to be strings. Non-string values are replaced with the + context's key. ([#4932](https://github.com/getsentry/relay/pull/4932)) **Internal**: From bd991e94007d93a4efdc02bfc77804291ae446af Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Sat, 12 Jul 2025 12:21:15 +0200 Subject: [PATCH 3/3] Update relay-event-schema/src/protocol/contexts/mod.rs Co-authored-by: David Herberth --- relay-event-schema/src/protocol/contexts/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relay-event-schema/src/protocol/contexts/mod.rs b/relay-event-schema/src/protocol/contexts/mod.rs index f0c4a8e9255..8478429801d 100644 --- a/relay-event-schema/src/protocol/contexts/mod.rs +++ b/relay-event-schema/src/protocol/contexts/mod.rs @@ -287,7 +287,7 @@ impl FromValue for Contexts { } } -/// Returns `true` if `value` is a nonempty string, which is the only valid form +/// Returns `true` if `value` is a non-empty string, which is the only valid value /// for the `"type"` field of a context. fn is_valid_context_type(value: Option<&Annotated>) -> bool { matches!(value.and_then(|v| v.value()), Some(Value::String(s)) if !s.is_empty())