Skip to main content

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, DebugImage, OsContext, User, Values};
80    use relay_protocol::{Empty, 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_owned(), Value::U64(42).into());
91                other.insert("device".to_owned(), Value::U64(42).into());
92                other.insert("repos".to_owned(), Value::U64(42).into());
93                other.insert("query".to_owned(), 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_owned(), Value::U64(42).into());
115                other.insert("bar".to_owned(), 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_owned(), Value::U64(42).into());
146                    other.insert("bar".to_owned(), 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_remove_enum_fallback_variant() {
166        let mut debug_image = Annotated::new(DebugImage::Other({
167            let mut other = Object::new();
168            other.insert("foo".to_owned(), Value::U64(42).into());
169            other.insert("bar".to_owned(), Value::U64(42).into());
170            other
171        }));
172
173        process_value(
174            &mut debug_image,
175            &mut RemoveOtherProcessor,
176            ProcessingState::root(),
177        )
178        .unwrap();
179
180        assert!(debug_image.is_empty());
181    }
182
183    #[test]
184    fn test_retain_context_other() {
185        let mut os = OsContext::default();
186        os.other
187            .insert("foo".to_owned(), Annotated::from(Value::U64(42)));
188
189        let mut contexts = Contexts::new();
190        contexts.insert("renamed".to_owned(), Context::Os(Box::new(os)));
191
192        let mut event = Annotated::new(Event {
193            contexts: Annotated::new(contexts.clone()),
194            ..Default::default()
195        });
196
197        process_value(
198            &mut event,
199            &mut RemoveOtherProcessor,
200            ProcessingState::root(),
201        )
202        .unwrap();
203
204        assert_eq!(get_value!(event.contexts!).0, contexts.0);
205    }
206
207    #[test]
208    fn test_breadcrumb_errors() {
209        let mut event = Annotated::new(Event {
210            breadcrumbs: Annotated::new(Values::new(vec![Annotated::new(Breadcrumb {
211                other: {
212                    let mut other = Object::new();
213                    other.insert("foo".to_owned(), Value::U64(42).into());
214                    other.insert("bar".to_owned(), Value::U64(42).into());
215                    other
216                },
217                ..Breadcrumb::default()
218            })])),
219            ..Default::default()
220        });
221
222        process_value(
223            &mut event,
224            &mut RemoveOtherProcessor,
225            ProcessingState::root(),
226        )
227        .unwrap();
228
229        let other = &event
230            .value()
231            .unwrap()
232            .breadcrumbs
233            .value()
234            .unwrap()
235            .values
236            .value()
237            .unwrap()[0]
238            .value()
239            .unwrap()
240            .other;
241
242        assert_eq!(
243            *other.get("foo").unwrap(),
244            Annotated::from_error(ErrorKind::InvalidAttribute, None)
245        );
246        assert_eq!(
247            *other.get("bar").unwrap(),
248            Annotated::from_error(ErrorKind::InvalidAttribute, None)
249        );
250    }
251
252    #[test]
253    fn test_scrape_attempts() {
254        let json = serde_json::json!({
255            "scraping_attempts": [
256                {"status": "not_attempted", "url": "http://example.com/embedded.js"},
257                {"status": "not_attempted", "url": "http://example.com/embedded.js.map"},
258            ]
259        });
260
261        let mut event = Event::from_value(json.into());
262        process_value(
263            &mut event,
264            &mut RemoveOtherProcessor,
265            ProcessingState::root(),
266        )
267        .unwrap();
268        assert!(event.value().unwrap().other.is_empty());
269    }
270}