relay_server/services/processor/
report.rs1use std::error::Error;
4
5use relay_event_schema::protocol::UserReport;
6
7use crate::envelope::{ContentType, ItemType};
8use crate::managed::{ItemAction, TypedEnvelope};
9use crate::services::outcome::{DiscardReason, Outcome};
10
11pub fn process_user_reports<Group>(managed_envelope: &mut TypedEnvelope<Group>) {
17 managed_envelope.retain_items(|item| {
18 if item.ty() != &ItemType::UserReport {
19 return ItemAction::Keep;
20 };
21
22 let payload = item.payload();
23 let payload = trim_whitespaces(&payload);
27 let report = match serde_json::from_slice::<UserReport>(payload) {
28 Ok(report) => report,
29 Err(error) => {
30 relay_log::debug!(
31 error = &error as &dyn Error,
32 "failed to deserialize user report"
33 );
34 return ItemAction::Drop(Outcome::Invalid(DiscardReason::InvalidJson));
35 }
36 };
37
38 let json_string = match serde_json::to_string(&report) {
39 Ok(json) => json,
40 Err(err) => {
41 relay_log::error!(
42 error = &err as &dyn Error,
43 "failed to serialize user report"
44 );
45 return ItemAction::Drop(Outcome::Invalid(DiscardReason::Internal));
46 }
47 };
48
49 item.set_payload(ContentType::Json, json_string);
50 ItemAction::Keep
51 });
52}
53
54fn trim_whitespaces(data: &[u8]) -> &[u8] {
55 let Some(from) = data.iter().position(|x| !x.is_ascii_whitespace()) else {
56 return &[];
57 };
58 let Some(to) = data.iter().rposition(|x| !x.is_ascii_whitespace()) else {
59 return &[];
60 };
61 &data[from..to + 1]
62}
63
64#[cfg(test)]
65mod tests {
66 use relay_event_schema::protocol::EventId;
67 use relay_system::Addr;
68
69 use crate::envelope::{Envelope, Item};
70 use crate::extractors::RequestMeta;
71 use crate::managed::ManagedEnvelope;
72 use crate::processing;
73 use crate::services::processor::{ProcessEnvelopeGrouped, ProcessingGroup, Submit};
74 use crate::testutils::create_test_processor;
75
76 use super::*;
77
78 #[tokio::test]
79 async fn test_user_report_invalid() {
80 let processor = create_test_processor(Default::default()).await;
81 let outcome_aggregator = Addr::dummy();
82 let event_id = EventId::new();
83
84 let dsn = "https://e12d836b15bb49d7bbf99e64295d995b:@sentry.io/42"
85 .parse()
86 .unwrap();
87
88 let request_meta = RequestMeta::new(dsn);
89 let mut envelope = Envelope::from_request(Some(event_id), request_meta);
90
91 envelope.add_item({
92 let mut item = Item::new(ItemType::UserReport);
93 item.set_payload(ContentType::Json, r#"{"foo": "bar"}"#);
94 item
95 });
96
97 envelope.add_item({
98 let mut item = Item::new(ItemType::Event);
99 item.set_payload(ContentType::Json, "{}");
100 item
101 });
102
103 let mut envelopes = ProcessingGroup::split_envelope(*envelope, &Default::default());
104 assert_eq!(envelopes.len(), 1);
105 let (group, envelope) = envelopes.pop().unwrap();
106 let envelope = ManagedEnvelope::new(envelope, outcome_aggregator);
107
108 let message = ProcessEnvelopeGrouped {
109 group,
110 envelope,
111 ctx: processing::Context::for_test(),
112 };
113
114 let Ok(Some(Submit::Envelope(new_envelope))) = processor.process(message).await else {
115 panic!();
116 };
117 let new_envelope = new_envelope.envelope();
118
119 assert_eq!(new_envelope.len(), 1);
120 assert_eq!(new_envelope.items().next().unwrap().ty(), &ItemType::Event);
121 }
122
123 #[test]
124 fn test_trim_whitespaces() {
125 assert_eq!(trim_whitespaces(b""), b"");
126 assert_eq!(trim_whitespaces(b" \n\r "), b"");
127 assert_eq!(trim_whitespaces(b" \nx\r "), b"x");
128 assert_eq!(trim_whitespaces(b" {foo: bar} "), b"{foo: bar}");
129 assert_eq!(trim_whitespaces(b"{ foo: bar}"), b"{ foo: bar}");
130 }
131}