fix: render actual newline in terminal-sanitized error messages
sanitize_for_terminal() escaped every control char it saw, including plain LF, so any multi-line diagnostic built from a hardcoded template (e.g. the path-separator error in main.rs) came out on a TTY as the literal 4-character sequence "\x0A" instead of a line break. LF cannot draw, move the cursor, or exfiltrate data the way ESC- and CSI/OSC-initiated sequences can, so it does not need the same defense as those. CR is still escaped since it can overwrite the current line and forge fake output. Closes #2104
This commit is contained in:
1 parent
b6fe117b17
commit
8157be1243
2 files changed
+29
-6
No files matched your search
+2
-1
@@ -4,7 +4,8 @@
|
||||
-
|
||||
|
||||
## Bugfixes
|
||||
-
|
||||
- Render actual newlines instead of the literal `\x0A` escape in multi-line error
|
||||
messages printed to a terminal, see #2104
|
||||
|
||||
# 10.5.0
|
||||
|
||||
|
||||
+27
-5
@@ -3,11 +3,19 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
|
||||
/// True for any char that is neither printable nor permitted whitespace (only HT).
|
||||
/// Covers C0/C1/DEL, bidi overrides, zero-width and format chars, and tag chars.
|
||||
/// True for any char that is neither printable nor permitted whitespace (only HT
|
||||
/// and LF). Covers C0/C1/DEL, bidi overrides, zero-width and format chars, and tag
|
||||
/// chars.
|
||||
///
|
||||
/// LF is deliberately let through even though it is a C0 control: it only ever
|
||||
/// starts a new line and cannot draw, move the cursor or exfiltrate data the way
|
||||
/// ESC- and CSI/OSC-initiated sequences can, so escaping it just mangles the
|
||||
/// developer-authored multi-line diagnostics fd prints (see sharkdp/fd#2104).
|
||||
/// CR is still escaped: unlike LF it can overwrite the current line and forge
|
||||
/// fake output.
|
||||
#[inline]
|
||||
fn needs_escape(c: char) -> bool {
|
||||
if c == '\t' {
|
||||
if c == '\t' || c == '\n' {
|
||||
return false;
|
||||
}
|
||||
c.is_control()
|
||||
@@ -107,8 +115,22 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strips_newline() {
|
||||
assert_eq!(sanitize_for_terminal("a\nb"), "a\\x0Ab");
|
||||
fn preserves_newline() {
|
||||
assert!(matches!(sanitize_for_terminal("a\nb"), Cow::Borrowed(_)));
|
||||
assert_eq!(sanitize_for_terminal("a\nb"), "a\nb");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserves_real_newlines_in_diagnostics() {
|
||||
// Multi-line diagnostics such as the path-separator error in main.rs use
|
||||
// real newlines for readability; they carry no attacker-controlled bytes,
|
||||
// so a bare '\n' must reach the terminal as an actual line break, not as
|
||||
// the literal 4-character sequence "\x0A". See sharkdp/fd#2104.
|
||||
let message = "The search pattern 'a/b' contains a path-separation character \
|
||||
and will not lead to any search results.\n\n\
|
||||
If you want to search for all files inside the 'a/b' directory, use a match-all pattern:\n\n \
|
||||
fd . 'a/b'";
|
||||
assert_eq!(sanitize_for_terminal(message), message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user