brizental pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
f111c7eb
by Beatriz Rizental at 2026-09-01T17:39:44+02:00
3 changed files:
- toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rs
- toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs
- toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rs
Changes:
| ... | ... | @@ -58,9 +58,56 @@ mod tests { |
| 58 | 58 | }
|
| 59 | 59 | |
| 60 | 60 | #[test]
|
| 61 | - fn escaped() {
|
|
| 61 | + fn escaped_special_chars() {
|
|
| 62 | + assert_eq!(tor_escape("'"), "\"\\'\"");
|
|
| 63 | + assert_eq!(tor_escape("\""), "\"\\\"\"");
|
|
| 64 | + assert_eq!(tor_escape("\\"), "\"\\\\\"");
|
|
| 62 | 65 | assert_eq!(tor_escape("'\"\\\r\n\t"), "\"\\'\\\"\\\\\\r\\n\\t\"");
|
| 66 | + }
|
|
| 67 | + |
|
| 68 | + #[test]
|
|
| 69 | + fn escaped_control_chars() {
|
|
| 70 | + assert_eq!(tor_escape("\n"), "\"\\n\"");
|
|
| 71 | + assert_eq!(tor_escape("\t"), "\"\\t\"");
|
|
| 72 | + assert_eq!(tor_escape("\r"), "\"\\r\"");
|
|
| 73 | + }
|
|
| 74 | + |
|
| 75 | + #[test]
|
|
| 76 | + fn ascii_printable_boundaries() {
|
|
| 77 | + // 0x1F is just below the printable range and must be hex-escaped.
|
|
| 78 | + assert_eq!(tor_escape(b"\x1F"), "\"\\x1F\"");
|
|
| 79 | + // 0x20 (space) is the first byte of the printable range.
|
|
| 80 | + assert_eq!(tor_escape(b"\x20"), "\" \"");
|
|
| 81 | + // 0x7E ('~') is the last byte of the printable range.
|
|
| 82 | + assert_eq!(tor_escape(b"\x7E"), "\"~\"");
|
|
| 83 | + // 0x7F (DEL) is just above the printable range and must be hex-escaped.
|
|
| 84 | + assert_eq!(tor_escape(b"\x7F"), "\"\\x7F\"");
|
|
| 85 | + }
|
|
| 86 | + |
|
| 87 | + #[test]
|
|
| 88 | + fn hex_zero_padding() {
|
|
| 63 | 89 | assert_eq!(tor_escape("\0"), "\"\\x00\"");
|
| 90 | + assert_eq!(tor_escape(b"\x0B"), "\"\\x0B\"");
|
|
| 91 | + assert_eq!(tor_escape(b"\x01"), "\"\\x01\"");
|
|
| 92 | + }
|
|
| 93 | + |
|
| 94 | + #[test]
|
|
| 95 | + fn hex_uppercase() {
|
|
| 96 | + assert_eq!(tor_escape(b"\xAB"), "\"\\xAB\"");
|
|
| 97 | + assert_eq!(tor_escape(b"\xFF"), "\"\\xFF\"");
|
|
| 98 | + }
|
|
| 99 | + |
|
| 100 | + #[test]
|
|
| 101 | + fn non_utf8_bytes() {
|
|
| 102 | + // Raw invalid UTF-8 bytes are escaped byte-by-byte, same as any
|
|
| 103 | + // other non-printable byte.
|
|
| 104 | + assert_eq!(tor_escape(b"\xF5"), "\"\\xF5\"");
|
|
| 105 | + assert_eq!(tor_escape(b"\xFF\xFE"), "\"\\xFF\\xFE\"");
|
|
| 106 | + assert_eq!(tor_escape(b"te\xF5st"), "\"te\\xF5st\"");
|
|
| 107 | + }
|
|
| 108 | + |
|
| 109 | + #[test]
|
|
| 110 | + fn unicode() {
|
|
| 64 | 111 | assert_eq!(tor_escape("\u{1F9C5}"), "\"\\xF0\\x9F\\xA7\\x85\"");
|
| 65 | 112 | }
|
| 66 | 113 | } |
| ... | ... | @@ -8,3 +8,42 @@ mod unescape; |
| 8 | 8 | |
| 9 | 9 | use escape::*;
|
| 10 | 10 | use unescape::*;
|
| 11 | + |
|
| 12 | +#[cfg(test)]
|
|
| 13 | +mod tests {
|
|
| 14 | + use super::*;
|
|
| 15 | + |
|
| 16 | + fn round_trip(buf: &[u8]) {
|
|
| 17 | + let mut escaped = String::new();
|
|
| 18 | + tor_escape_into(buf, &mut escaped);
|
|
| 19 | + assert_eq!(&*tor_unescape(escaped.as_bytes()).unwrap(), buf);
|
|
| 20 | + }
|
|
| 21 | + |
|
| 22 | + #[test]
|
|
| 23 | + fn round_trip_simple() {
|
|
| 24 | + round_trip(b"test");
|
|
| 25 | + }
|
|
| 26 | + |
|
| 27 | + #[test]
|
|
| 28 | + fn round_trip_empty() {
|
|
| 29 | + round_trip(b"");
|
|
| 30 | + }
|
|
| 31 | + |
|
| 32 | + #[test]
|
|
| 33 | + fn round_trip_special_chars() {
|
|
| 34 | + round_trip(b"'\"\\\r\n\t");
|
|
| 35 | + }
|
|
| 36 | + |
|
| 37 | + #[test]
|
|
| 38 | + fn round_trip_non_utf8() {
|
|
| 39 | + round_trip(b"\xF5\xFF\xFE");
|
|
| 40 | + }
|
|
| 41 | + |
|
| 42 | + #[test]
|
|
| 43 | + fn round_trip_all_bytes() {
|
|
| 44 | + // Every possible byte value, escaped then unescaped,
|
|
| 45 | + // must come back unchanged.
|
|
| 46 | + let all_bytes: Vec<u8> = (0..=255).collect();
|
|
| 47 | + round_trip(&all_bytes);
|
|
| 48 | + }
|
|
| 49 | +} |
| ... | ... | @@ -140,6 +140,26 @@ mod tests { |
| 140 | 140 | assert_eq!(&*tor_unescape(b"\"\"").unwrap(), &[]);
|
| 141 | 141 | }
|
| 142 | 142 | |
| 143 | + #[test]
|
|
| 144 | + fn lone_quote() {
|
|
| 145 | + // A single quote character is not a valid empty quoted string: there
|
|
| 146 | + // is no closing quote.
|
|
| 147 | + assert_eq!(
|
|
| 148 | + tor_unescape(b"\"").unwrap_err(),
|
|
| 149 | + UnescapeError::Unterminated
|
|
| 150 | + );
|
|
| 151 | + }
|
|
| 152 | + |
|
| 153 | + #[test]
|
|
| 154 | + fn quoted_spaces() {
|
|
| 155 | + // Spaces are only rejected in unquoted strings, so they must be
|
|
| 156 | + // preserved as-is once inside quotes.
|
|
| 157 | + assert_eq!(&*tor_unescape(b"\"test test\"").unwrap(), b"test test");
|
|
| 158 | + assert_eq!(&*tor_unescape(b"\" test\"").unwrap(), b" test");
|
|
| 159 | + assert_eq!(&*tor_unescape(b"\"test \"").unwrap(), b"test ");
|
|
| 160 | + assert_eq!(&*tor_unescape(b"\"t e s t\"").unwrap(), b"t e s t");
|
|
| 161 | + }
|
|
| 162 | + |
|
| 143 | 163 | #[test]
|
| 144 | 164 | fn unescape_simple() {
|
| 145 | 165 | assert_eq!(&*tor_unescape(b"\"\\n\"").unwrap(), b"\n");
|
| ... | ... | @@ -190,6 +210,23 @@ mod tests { |
| 190 | 210 | assert_eq!(&*tor_unescape(b"\"\\40test\\0\"").unwrap(), b" test\0");
|
| 191 | 211 | }
|
| 192 | 212 | |
| 213 | + #[test]
|
|
| 214 | + fn unescape_octal_max_digits() {
|
|
| 215 | + // Only up to 3 octal digits are consumed per escape, even if a 4th
|
|
| 216 | + // digit-looking byte follows.
|
|
| 217 | + assert_eq!(&*tor_unescape(b"\"\\0004\"").unwrap(), b"\x004");
|
|
| 218 | + assert_eq!(&*tor_unescape(b"\"\\0007\"").unwrap(), b"\x007");
|
|
| 219 | + }
|
|
| 220 | + |
|
| 221 | + #[test]
|
|
| 222 | + fn unescape_octal_ambiguous_digit() {
|
|
| 223 | + // '8' and '9' are not valid octal digits, so they end the escape
|
|
| 224 | + // early and are then treated as literal characters, even though at
|
|
| 225 | + // a glance "\048" looks like it could mean octal 048.
|
|
| 226 | + assert_eq!(&*tor_unescape(b"\"\\048\"").unwrap(), b"\x048");
|
|
| 227 | + assert_eq!(&*tor_unescape(b"\"\\09\"").unwrap(), b"\x009");
|
|
| 228 | + }
|
|
| 229 | + |
|
| 193 | 230 | #[test]
|
| 194 | 231 | fn invalid_unicode() {
|
| 195 | 232 | // Raw invalid sequence, without quotes
|
| ... | ... | @@ -287,4 +324,13 @@ mod tests { |
| 287 | 324 | UnescapeError::OctalOverflow,
|
| 288 | 325 | );
|
| 289 | 326 | }
|
| 327 | + |
|
| 328 | + #[test]
|
|
| 329 | + fn stress() {
|
|
| 330 | + // A mix of literals, spaces, and every kind of escape back to back.
|
|
| 331 | + assert_eq!(
|
|
| 332 | + &*tor_unescape(b"\"a \\n b\\tc\\r\\\\d\\'e\\\"f\\x20g\\040h\\048\"").unwrap(),
|
|
| 333 | + b"a \n b\tc\r\\d'e\"f g h\x048"
|
|
| 334 | + );
|
|
| 335 | + }
|
|
| 290 | 336 | } |