tracing_subscriber/fmt/format/
escape.rs

1//! ANSI escape sequence sanitization to prevent terminal injection attacks.
2
3use std::fmt::{self, Write};
4
5/// A wrapper that implements `fmt::Debug` and `fmt::Display` and escapes ANSI sequences on-the-fly.
6/// This avoids creating intermediate strings while providing security against terminal injection.
7pub(super) struct Escape<T>(pub(super) T);
8
9/// Helper struct that escapes ANSI sequences as characters are written
10struct EscapingWriter<'a, 'b> {
11    inner: &'a mut fmt::Formatter<'b>,
12}
13
14impl<'a, 'b> fmt::Write for EscapingWriter<'a, 'b> {
15    fn write_str(&mut self, s: &str) -> fmt::Result {
16        // Stream the string character by character, escaping ANSI and C1 control sequences
17        for ch in s.chars() {
18            match ch {
19                // C0 control characters that can be used in terminal escape sequences
20                '\x1b' => self.inner.write_str("\\x1b")?,  // ESC
21                '\x07' => self.inner.write_str("\\x07")?,  // BEL
22                '\x08' => self.inner.write_str("\\x08")?,  // BS
23                '\x0c' => self.inner.write_str("\\x0c")?,  // FF
24                '\x7f' => self.inner.write_str("\\x7f")?,  // DEL
25                
26                // C1 control characters (\x80-\x9f) - 8-bit control codes
27                // These can be used as alternative escape sequences in some terminals
28                ch if ch as u32 >= 0x80 && ch as u32 <= 0x9f => {
29                    write!(self.inner, "\\u{{{:x}}}", ch as u32)?
30                },
31                
32                _ => self.inner.write_char(ch)?,
33            }
34        }
35        Ok(())
36    }
37}
38
39impl<T: fmt::Debug> fmt::Debug for Escape<T> {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        let mut escaping_writer = EscapingWriter { inner: f };
42        write!(escaping_writer, "{:?}", self.0)
43    }
44}
45
46impl<T: fmt::Display> fmt::Display for Escape<T> {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        let mut escaping_writer = EscapingWriter { inner: f };
49        write!(escaping_writer, "{}", self.0)
50    }
51}