relay_event_normalization/
remove_other.rs

1use relay_event_schema::processor::{ProcessValue, ProcessingResult, ProcessingState, Processor};
2use relay_event_schema::protocol::{Breadcrumb, Event};
3use relay_protocol::{Annotated, ErrorKind, Meta, Object, Value};
4
5/// Replace remaining values and all existing meta with an errors.
6fn create_errors(other: &mut Object<Value>) {
7    for value in other.values_mut() {
8        *value = Annotated::from_error(ErrorKind::InvalidAttribute, None);
9    }
10}
11
12/// Removes unknown, internal and deprecated fields from a payload.
13pub struct RemoveOtherProcessor;
14
15impl Processor for RemoveOtherProcessor {
16    fn process_other(
17        &mut self,
18        other: &mut Object<Value>,
19        state: &ProcessingState<'_>,
20    ) -> ProcessingResult {
21        // Drop unknown attributes at all levels without error messages, unless `retain = "true"`
22        // was specified explicitly on the field.
23        if !state.attrs().retain {
24            other.clear();
25        }
26
27        Ok(())
28    }
29
30    fn process_breadcrumb(
31        &mut self,
32        breadcrumb: &mut Breadcrumb,
33        _meta: &mut Meta,
34        state: &ProcessingState<'_>,
35    ) -> ProcessingResult {
36        // Move the current map out so we don't clear it in `process_other`
37        let mut other = std::mem::take(&mut breadcrumb.other);
38        create_errors(&mut other);
39
40        // Recursively clean all `other`s now. Note that this won't touch the event's other
41        breadcrumb.process_child_values(self, state)?;
42        breadcrumb.other = other;
43        Ok(())
44    }
45
46    fn process_event(
47        &mut self,
48        event: &mut Event,
49        _meta: &mut Meta,
50        state: &ProcessingState<'_>,
51    ) -> ProcessingResult {
52        // Move the current map out so we don't clear it in `process_other`
53        let mut other = std::mem::take(&mut event.other);
54
55        // Drop Sentry internal attributes
56        other.remove("metadata");
57        other.remove("hashes");
58
59        // Drop known legacy attributes at top-level without errors
60        other.remove("applecrashreport");
61        other.remove("device");
62        other.remove("repos");
63        other.remove("query");
64
65        // Replace remaining values and all existing meta with an errors
66        create_errors(&mut other);
67
68        // Recursively clean all `other`s now. Note that this won't touch the event's other
69        event.process_child_values(self, state)?;
70
71        event.other = other;
72        Ok(())
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use relay_event_schema::processor::process_value;
79    use relay_event_schema::protocol::{Context, Contexts, OsContext, User, Values};
80    use relay_protocol::{FromValue, get_value};
81    use similar_asserts::assert_eq;
82
83    use super::*;
84
85    #[test]
86    fn test_remove_legacy_attributes() {
87        let mut event = Annotated::new(Event {
88            other: {
89                let mut other = Object::new();
90                other.insert("applecrashreport".to_string(), Value::U64(42).into());
91                other.insert("device".to_string(), Value::U64(42).into());
92                other.insert("repos".to_string(), Value::U64(42).into());
93                other.insert("query".to_string(), Value::U64(42).into());
94                other
95            },
96            ..Default::default()
97        });
98
99        process_value(
100            &mut event,
101            &mut RemoveOtherProcessor,
102            ProcessingState::root(),
103        )
104        .unwrap();
105
106        assert!(event.value().unwrap().other.is_empty());
107    }
108
109    #[test]
110    fn test_remove_unknown_attributes() {
111        let mut event = Annotated::new(Event {
112            other: {
113                let mut other = Object::new();
114                other.insert("foo".to_string(), Value::U64(42).into());
115                other.insert("bar".to_string(), Value::U64(42).into());
116                other
117            },
118            ..Default::default()
119        });
120
121        process_value(
122            &mut event,
123            &mut RemoveOtherProcessor,
124            ProcessingState::root(),
125        )
126        .unwrap();
127
128        let other = &event.value().unwrap().other;
129        assert_eq!(
130            *other.get("foo").unwrap(),
131            Annotated::from_error(ErrorKind::InvalidAttribute, None)
132        );
133        assert_eq!(
134            *other.get("bar").unwrap(),
135            Annotated::from_error(ErrorKind::InvalidAttribute, None)
136        );
137    }
138
139    #[test]
140    fn test_remove_nested_other() {
141        let mut event = Annotated::new(Event {
142            user: Annotated::from(User {
143                other: {
144                    let mut other = Object::new();
145                    other.insert("foo".to_string(), Value::U64(42).into());
146                    other.insert("bar".to_string(), Value::U64(42).into());
147                    other
148                },
149                ..Default::default()
150            }),
151            ..Default::default()
152        });
153
154        process_value(
155            &mut event,
156            &mut RemoveOtherProcessor,
157            ProcessingState::root(),
158        )
159        .unwrap();
160
161        assert!(get_value!(event.user!).other.is_empty());
162    }
163
164    #[test]
165    fn test_retain_context_other() {
166        let mut os = OsContext::default();
167        os.other
168            .insert("foo".to_string(), Annotated::from(Value::U64(42)));
169
170        let mut contexts = Contexts::new();
171        contexts.insert("renamed".to_string(), Context::Os(Box::new(os)));
172
173        let mut event = Annotated::new(Event {
174            contexts: Annotated::new(contexts.clone()),
175            ..Default::default()
176        });
177
178        process_value(
179            &mut event,
180            &mut RemoveOtherProcessor,
181            ProcessingState::root(),
182        )
183        .unwrap();
184
185        assert_eq!(get_value!(event.contexts!).0, contexts.0);
186    }
187
188    #[test]
189    fn test_breadcrumb_errors() {
190        let mut event = Annotated::new(Event {
191            breadcrumbs: Annotated::new(Values::new(vec![Annotated::new(Breadcrumb {
192                other: {
193                    let mut other = Object::new();
194                    other.insert("foo".to_string(), Value::U64(42).into());
195                    other.insert("bar".to_string(), Value::U64(42).into());
196                    other
197                },
198                ..Breadcrumb::default()
199            })])),
200            ..Default::default()
201        });
202
203        process_value(
204            &mut event,
205            &mut RemoveOtherProcessor,
206            ProcessingState::root(),
207        )
208        .unwrap();
209
210        let other = &event
211            .value()
212            .unwrap()
213            .breadcrumbs
214            .value()
215            .unwrap()
216            .values
217            .value()
218            .unwrap()[0]
219            .value()
220            .unwrap()
221            .other;
222
223        assert_eq!(
224            *other.get("foo").unwrap(),
225            Annotated::from_error(ErrorKind::InvalidAttribute, None)
226        );
227        assert_eq!(
228            *other.get("bar").unwrap(),
229            Annotated::from_error(ErrorKind::InvalidAttribute, None)
230        );
231    }
232
233    #[test]
234    fn test_scrape_attempts() {
235        let json = serde_json::json!({
236            "scraping_attempts": [
237                {"status": "not_attempted", "url": "http://example.com/embedded.js"},
238                {"status": "not_attempted", "url": "http://example.com/embedded.js.map"},
239            ]
240        });
241
242        let mut event = Event::from_value(json.into());
243        process_value(
244            &mut event,
245            &mut RemoveOtherProcessor,
246            ProcessingState::root(),
247        )
248        .unwrap();
249        assert!(event.value().unwrap().other.is_empty());
250    }
251}