|
1 | 1 | use node_types::{EntryKind, Field, NodeTypeMap, Storage, TypeName}; |
| 2 | +use std::borrow::Cow; |
2 | 3 | use std::collections::BTreeMap as Map; |
3 | 4 | use std::collections::BTreeSet as Set; |
4 | 5 | use std::fmt; |
@@ -190,6 +191,41 @@ pub fn extract( |
190 | 191 | Ok(Program(visitor.trap_writer.trap_output)) |
191 | 192 | } |
192 | 193 |
|
| 194 | +/// Escapes a string for use in a TRAP key, by replacing special characters with |
| 195 | +/// HTML entities. |
| 196 | +fn escape_key<'a, S: Into<Cow<'a, str>>>(key: S) -> Cow<'a, str> { |
| 197 | + fn needs_escaping(c: char) -> bool { |
| 198 | + match c { |
| 199 | + '&' => true, |
| 200 | + '{' => true, |
| 201 | + '}' => true, |
| 202 | + '"' => true, |
| 203 | + '@' => true, |
| 204 | + '#' => true, |
| 205 | + _ => false, |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + let key = key.into(); |
| 210 | + if key.contains(needs_escaping) { |
| 211 | + let mut escaped = String::with_capacity(2 * key.len()); |
| 212 | + for c in key.chars() { |
| 213 | + match c { |
| 214 | + '&' => escaped.push_str("&"), |
| 215 | + '{' => escaped.push_str("{"), |
| 216 | + '}' => escaped.push_str("}"), |
| 217 | + '"' => escaped.push_str("""), |
| 218 | + '@' => escaped.push_str("@"), |
| 219 | + '#' => escaped.push_str("#"), |
| 220 | + _ => escaped.push(c), |
| 221 | + } |
| 222 | + } |
| 223 | + Cow::Owned(escaped) |
| 224 | + } else { |
| 225 | + key |
| 226 | + } |
| 227 | +} |
| 228 | + |
193 | 229 | /// Normalizes the path according the common CodeQL specification. Assumes that |
194 | 230 | /// `path` has already been canonicalized using `std::fs::canonicalize`. |
195 | 231 | fn normalize_path(path: &Path) -> String { |
@@ -230,11 +266,11 @@ fn normalize_path(path: &Path) -> String { |
230 | 266 | } |
231 | 267 |
|
232 | 268 | fn full_id_for_file(path: &Path) -> String { |
233 | | - format!("{};sourcefile", normalize_path(path)) |
| 269 | + format!("{};sourcefile", escape_key(&normalize_path(path))) |
234 | 270 | } |
235 | 271 |
|
236 | 272 | fn full_id_for_folder(path: &Path) -> String { |
237 | | - format!("{};folder", normalize_path(path)) |
| 273 | + format!("{};folder", escape_key(&normalize_path(path))) |
238 | 274 | } |
239 | 275 |
|
240 | 276 | struct ChildNode { |
@@ -731,3 +767,16 @@ fn limit_string_test() { |
731 | 767 | assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); |
732 | 768 | assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); |
733 | 769 | } |
| 770 | + |
| 771 | +#[test] |
| 772 | +fn escape_key_test() { |
| 773 | + assert_eq!("foo!", escape_key("foo!")); |
| 774 | + assert_eq!("foo{}", escape_key("foo{}")); |
| 775 | + assert_eq!("{}", escape_key("{}")); |
| 776 | + assert_eq!("", escape_key("")); |
| 777 | + assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); |
| 778 | + assert_eq!( |
| 779 | + "/path/to/foo&{}"@#.rb", |
| 780 | + escape_key("/path/to/foo&{}\"@#.rb") |
| 781 | + ); |
| 782 | +} |
0 commit comments