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 fixup! TB 44806: Implement the tor integration in Rust. TB 44930: Implement the commands on the Rust control port Implement more tests. - - - - - 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: ===================================== toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rs ===================================== @@ -58,9 +58,56 @@ mod tests { } #[test] - fn escaped() { + fn escaped_special_chars() { + assert_eq!(tor_escape("'"), "\"\\'\""); + assert_eq!(tor_escape("\""), "\"\\\"\""); + assert_eq!(tor_escape("\\"), "\"\\\\\""); assert_eq!(tor_escape("'\"\\\r\n\t"), "\"\\'\\\"\\\\\\r\\n\\t\""); + } + + #[test] + fn escaped_control_chars() { + assert_eq!(tor_escape("\n"), "\"\\n\""); + assert_eq!(tor_escape("\t"), "\"\\t\""); + assert_eq!(tor_escape("\r"), "\"\\r\""); + } + + #[test] + fn ascii_printable_boundaries() { + // 0x1F is just below the printable range and must be hex-escaped. + assert_eq!(tor_escape(b"\x1F"), "\"\\x1F\""); + // 0x20 (space) is the first byte of the printable range. + assert_eq!(tor_escape(b"\x20"), "\" \""); + // 0x7E ('~') is the last byte of the printable range. + assert_eq!(tor_escape(b"\x7E"), "\"~\""); + // 0x7F (DEL) is just above the printable range and must be hex-escaped. + assert_eq!(tor_escape(b"\x7F"), "\"\\x7F\""); + } + + #[test] + fn hex_zero_padding() { assert_eq!(tor_escape("\0"), "\"\\x00\""); + assert_eq!(tor_escape(b"\x0B"), "\"\\x0B\""); + assert_eq!(tor_escape(b"\x01"), "\"\\x01\""); + } + + #[test] + fn hex_uppercase() { + assert_eq!(tor_escape(b"\xAB"), "\"\\xAB\""); + assert_eq!(tor_escape(b"\xFF"), "\"\\xFF\""); + } + + #[test] + fn non_utf8_bytes() { + // Raw invalid UTF-8 bytes are escaped byte-by-byte, same as any + // other non-printable byte. + assert_eq!(tor_escape(b"\xF5"), "\"\\xF5\""); + assert_eq!(tor_escape(b"\xFF\xFE"), "\"\\xFF\\xFE\""); + assert_eq!(tor_escape(b"te\xF5st"), "\"te\\xF5st\""); + } + + #[test] + fn unicode() { assert_eq!(tor_escape("\u{1F9C5}"), "\"\\xF0\\x9F\\xA7\\x85\""); } } ===================================== toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs ===================================== @@ -8,3 +8,42 @@ mod unescape; use escape::*; use unescape::*; + +#[cfg(test)] +mod tests { + use super::*; + + fn round_trip(buf: &[u8]) { + let mut escaped = String::new(); + tor_escape_into(buf, &mut escaped); + assert_eq!(&*tor_unescape(escaped.as_bytes()).unwrap(), buf); + } + + #[test] + fn round_trip_simple() { + round_trip(b"test"); + } + + #[test] + fn round_trip_empty() { + round_trip(b""); + } + + #[test] + fn round_trip_special_chars() { + round_trip(b"'\"\\\r\n\t"); + } + + #[test] + fn round_trip_non_utf8() { + round_trip(b"\xF5\xFF\xFE"); + } + + #[test] + fn round_trip_all_bytes() { + // Every possible byte value, escaped then unescaped, + // must come back unchanged. + let all_bytes: Vec<u8> = (0..=255).collect(); + round_trip(&all_bytes); + } +} ===================================== toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rs ===================================== @@ -140,6 +140,26 @@ mod tests { assert_eq!(&*tor_unescape(b"\"\"").unwrap(), &[]); } + #[test] + fn lone_quote() { + // A single quote character is not a valid empty quoted string: there + // is no closing quote. + assert_eq!( + tor_unescape(b"\"").unwrap_err(), + UnescapeError::Unterminated + ); + } + + #[test] + fn quoted_spaces() { + // Spaces are only rejected in unquoted strings, so they must be + // preserved as-is once inside quotes. + assert_eq!(&*tor_unescape(b"\"test test\"").unwrap(), b"test test"); + assert_eq!(&*tor_unescape(b"\" test\"").unwrap(), b" test"); + assert_eq!(&*tor_unescape(b"\"test \"").unwrap(), b"test "); + assert_eq!(&*tor_unescape(b"\"t e s t\"").unwrap(), b"t e s t"); + } + #[test] fn unescape_simple() { assert_eq!(&*tor_unescape(b"\"\\n\"").unwrap(), b"\n"); @@ -190,6 +210,23 @@ mod tests { assert_eq!(&*tor_unescape(b"\"\\40test\\0\"").unwrap(), b" test\0"); } + #[test] + fn unescape_octal_max_digits() { + // Only up to 3 octal digits are consumed per escape, even if a 4th + // digit-looking byte follows. + assert_eq!(&*tor_unescape(b"\"\\0004\"").unwrap(), b"\x004"); + assert_eq!(&*tor_unescape(b"\"\\0007\"").unwrap(), b"\x007"); + } + + #[test] + fn unescape_octal_ambiguous_digit() { + // '8' and '9' are not valid octal digits, so they end the escape + // early and are then treated as literal characters, even though at + // a glance "\048" looks like it could mean octal 048. + assert_eq!(&*tor_unescape(b"\"\\048\"").unwrap(), b"\x048"); + assert_eq!(&*tor_unescape(b"\"\\09\"").unwrap(), b"\x009"); + } + #[test] fn invalid_unicode() { // Raw invalid sequence, without quotes @@ -287,4 +324,13 @@ mod tests { UnescapeError::OctalOverflow, ); } + + #[test] + fn stress() { + // A mix of literals, spaces, and every kind of escape back to back. + assert_eq!( + &*tor_unescape(b"\"a \\n b\\tc\\r\\\\d\\'e\\\"f\\x20g\\040h\\048\"").unwrap(), + b"a \n b\tc\r\\d'e\"f g h\x048" + ); + } } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f111c7eb... -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f111c7eb... You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
participants (1)
-
brizental (@brizental)