relay_event_normalization/
stacktrace.rs1use std::mem;
2
3use relay_event_schema::processor;
4use relay_event_schema::protocol::{Frame, RawStacktrace};
5use relay_protocol::{Annotated, Empty, Meta};
6use url::Url;
7
8fn is_url(filename: &str) -> bool {
9 filename.starts_with("file:")
10 || filename.starts_with("http:")
11 || filename.starts_with("https:")
12 || filename.starts_with("applewebdata:")
13}
14
15pub fn normalize_stacktrace(stacktrace: &mut RawStacktrace, _meta: &mut Meta) {
16 if let Some(frames) = stacktrace.frames.value_mut() {
18 for frame in frames.iter_mut() {
19 normalize_non_raw_frame(frame);
20 }
21 }
22}
23
24pub fn normalize_non_raw_frame(frame: &mut Annotated<Frame>) {
25 let _ = processor::apply(frame, |frame, _meta| {
26 if frame.abs_path.value().is_empty() {
27 frame.abs_path = mem::replace(&mut frame.filename, Annotated::empty());
28 }
29
30 if frame.filename.value().is_empty() {
31 if let Some(abs_path) = frame.abs_path.value_mut() {
32 frame.filename = Annotated::new(abs_path.clone());
33
34 if is_url(abs_path.as_str()) {
35 if let Ok(url) = Url::parse(abs_path.as_str()) {
36 let path = url.path();
37
38 if !path.is_empty() && path != "/" {
39 frame.filename = Annotated::new(path.into());
40 }
41 }
42 }
43 }
44 }
45
46 if frame.function.as_str() == Some("?") {
47 frame.function.set_value(None);
48 }
49
50 if frame.symbol.as_str() == Some("?") {
51 frame.symbol.set_value(None);
52 }
53
54 if let Some(lines) = frame.pre_context.value_mut() {
55 for line in lines.iter_mut() {
56 line.get_or_insert_with(String::new);
57 }
58 }
59
60 if let Some(lines) = frame.post_context.value_mut() {
61 for line in lines.iter_mut() {
62 line.get_or_insert_with(String::new);
63 }
64 }
65
66 if frame.context_line.value().is_none()
67 && (!frame.pre_context.is_empty() || !frame.post_context.is_empty())
68 {
69 frame.context_line.set_value(Some(String::new()));
70 }
71
72 Ok(())
73 });
74}
75
76#[cfg(test)]
77mod tests {
78 use similar_asserts::assert_eq;
79
80 use super::*;
81
82 #[test]
83 fn test_coerces_url_filenames() {
84 let mut frame = Annotated::new(Frame {
85 lineno: Annotated::new(1),
86 filename: Annotated::new("http://foo.com/foo.js".into()),
87 ..Default::default()
88 });
89
90 normalize_non_raw_frame(&mut frame);
91 let frame = frame.value().unwrap();
92
93 assert_eq!(frame.filename.value().unwrap().as_str(), "/foo.js");
94 assert_eq!(
95 frame.abs_path.value().unwrap().as_str(),
96 "http://foo.com/foo.js"
97 );
98 }
99
100 #[test]
101 fn test_does_not_overwrite_filename() {
102 let mut frame = Annotated::new(Frame {
103 lineno: Annotated::new(1),
104 filename: Annotated::new("foo.js".into()),
105 abs_path: Annotated::new("http://foo.com/foo.js".into()),
106 ..Default::default()
107 });
108
109 normalize_non_raw_frame(&mut frame);
110 let frame = frame.value().unwrap();
111
112 assert_eq!(frame.filename.value().unwrap().as_str(), "foo.js");
113 assert_eq!(
114 frame.abs_path.value().unwrap().as_str(),
115 "http://foo.com/foo.js"
116 );
117 }
118
119 #[test]
120 fn test_ignores_results_with_empty_path() {
121 let mut frame = Annotated::new(Frame {
122 lineno: Annotated::new(1),
123 abs_path: Annotated::new("http://foo.com".into()),
124 ..Default::default()
125 });
126
127 normalize_non_raw_frame(&mut frame);
128 let frame = frame.value().unwrap();
129
130 assert_eq!(frame.filename.value().unwrap().as_str(), "http://foo.com");
131 assert_eq!(
132 frame.abs_path.value().unwrap().as_str(),
133 frame.filename.value().unwrap().as_str()
134 );
135 }
136
137 #[test]
138 fn test_ignores_results_with_slash_path() {
139 let mut frame = Annotated::new(Frame {
140 lineno: Annotated::new(1),
141 abs_path: Annotated::new("http://foo.com/".into()),
142 ..Default::default()
143 });
144
145 normalize_non_raw_frame(&mut frame);
146 let frame = frame.value().unwrap();
147
148 assert_eq!(frame.filename.value().unwrap().as_str(), "http://foo.com/");
149 assert_eq!(
150 frame.abs_path.value().unwrap().as_str(),
151 frame.filename.value().unwrap().as_str()
152 );
153 }
154
155 #[test]
156 fn test_coerce_empty_filename() {
157 let mut frame = Annotated::new(Frame {
158 lineno: Annotated::new(1),
159 filename: Annotated::new("".into()),
160 abs_path: Annotated::new("http://foo.com/foo.js".into()),
161 ..Default::default()
162 });
163
164 normalize_non_raw_frame(&mut frame);
165 let frame = frame.value().unwrap();
166
167 assert_eq!(frame.filename.value().unwrap().as_str(), "/foo.js");
168 assert_eq!(
169 frame.abs_path.value().unwrap().as_str(),
170 "http://foo.com/foo.js"
171 );
172 }
173
174 #[test]
175 fn test_is_url() {
176 assert!(is_url("http://example.org/"));
177 assert!(is_url("https://example.org/"));
178 assert!(is_url("file:///tmp/filename"));
179 assert!(is_url(
180 "applewebdata://00000000-0000-1000-8080-808080808080"
181 ));
182 assert!(!is_url("app:///index.bundle")); assert!(!is_url("webpack:///./app/index.jsx")); assert!(!is_url("data:,"));
185 assert!(!is_url("blob:\x00"));
186 }
187}