Skip to content

Commit 7e6b942

Browse files
authored
feat(core): highlight unprintable chars in permission prompts (#22468)
If we strip out unprintable chars, we don't see the full filename being requested by permission prompts. Instead, we highlight and escape them to make them visible.
1 parent 9a43a2b commit 7e6b942

File tree

2 files changed

+29
-21
lines changed

2 files changed

+29
-21
lines changed

runtime/permissions/prompter.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,24 @@ use std::io::StderrLock;
1111
use std::io::StdinLock;
1212
use std::io::Write as IoWrite;
1313

14-
/// Helper function to strip ansi codes and ASCII control characters.
15-
fn strip_ansi_codes_and_ascii_control(s: &str) -> std::borrow::Cow<str> {
16-
console_static_text::ansi::strip_ansi_codes(s)
17-
.chars()
18-
.filter(|c| !c.is_ascii_control())
19-
.collect()
14+
/// Helper function to make control characters visible so users can see the underlying filename.
15+
fn escape_control_characters(s: &str) -> std::borrow::Cow<str> {
16+
if !s.contains(|c: char| c.is_ascii_control() || c.is_control()) {
17+
return std::borrow::Cow::Borrowed(s);
18+
}
19+
let mut output = String::with_capacity(s.len() * 2);
20+
for c in s.chars() {
21+
match c {
22+
c if c.is_ascii_control() => output.push_str(
23+
&colors::white_bold_on_red(c.escape_debug().to_string()).to_string(),
24+
),
25+
c if c.is_control() => output.push_str(
26+
&colors::white_bold_on_red(c.escape_debug().to_string()).to_string(),
27+
),
28+
c => output.push(c),
29+
}
30+
}
31+
output.into()
2032
}
2133

2234
pub const PERMISSION_EMOJI: &str = "⚠️";
@@ -249,9 +261,9 @@ impl PermissionPrompter for TtyPrompter {
249261
return PromptResponse::Deny; // don't grant permission if this fails
250262
}
251263

252-
let message = strip_ansi_codes_and_ascii_control(message);
253-
let name = strip_ansi_codes_and_ascii_control(name);
254-
let api_name = api_name.map(strip_ansi_codes_and_ascii_control);
264+
let message = escape_control_characters(message);
265+
let name = escape_control_characters(name);
266+
let api_name = api_name.map(escape_control_characters);
255267

256268
// print to stderr so that if stdout is piped this is still displayed.
257269
let opts: String = if is_unary {

tests/integration/run_tests.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4725,19 +4725,19 @@ fn stdio_streams_are_locked_in_permission_prompt() {
47254725
}
47264726

47274727
#[test]
4728-
fn permission_prompt_strips_ansi_codes_and_control_chars() {
4728+
fn permission_prompt_escapes_ansi_codes_and_control_chars() {
47294729
util::with_pty(&["repl"], |mut console| {
47304730
console.write_line(
47314731
r#"Deno.permissions.request({ name: "env", variable: "\rDo you like ice cream? y/n" });"#
47324732
);
47334733
// will be uppercase on windows
47344734
let env_name = if cfg!(windows) {
4735-
"DO YOU LIKE ICE CREAM? Y/N"
4735+
"\\rDO YOU LIKE ICE CREAM? Y/N"
47364736
} else {
4737-
"Do you like ice cream? y/n"
4737+
"\\rDo you like ice cream? y/n"
47384738
};
47394739
console.expect(format!(
4740-
"┌ ⚠️ Deno requests env access to \"{}\".",
4740+
"\u{250c} \u{26a0}\u{fe0f} Deno requests env access to \"{}\".",
47414741
env_name
47424742
))
47434743
});
@@ -4747,14 +4747,10 @@ fn permission_prompt_strips_ansi_codes_and_control_chars() {
47474747
console.expect("undefined");
47484748
console.write_line_raw(r#"const unboldANSI = "\u001b[22m";"#);
47494749
console.expect("undefined");
4750-
console.write_line_raw(r#"const prompt = `┌ ⚠️ ${boldANSI}Deno requests run access to "echo"${unboldANSI}\n ├ Requested by \`Deno.Command().output()`"#);
4751-
console.expect("undefined");
4752-
console.write_line_raw(r#"const moveANSIUp = "\u001b[1A";"#);
4753-
console.expect("undefined");
4754-
console.write_line_raw(r#"const clearANSI = "\u001b[2K";"#);
4755-
console.expect("undefined");
4756-
console.write_line_raw(r#"const moveANSIStart = "\u001b[1000D";"#);
4757-
console.expect("undefined");
4750+
console.write_line_raw(
4751+
r#"new Deno.Command(`${boldANSI}cat${unboldANSI}`).spawn();"#,
4752+
);
4753+
console.expect("\u{250c} \u{26a0}\u{fe0f} Deno requests run access to \"\\u{1b}[1mcat\\u{1b}[22m\".");
47584754
});
47594755
}
47604756

0 commit comments

Comments
 (0)