Skip to content

Commit

Permalink
fix(prompt): better output with control chars (#18108)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartlomieju authored Mar 10, 2023
1 parent 0da1938 commit 78d4301
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 5 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ base64 = "=0.13.1"
bencher = "0.1"
bytes = "=1.2.1"
cache_control = "=0.2.0"
console_static_text = "=0.7.1"
data-url = "=0.2.0"
dlopen = "0.1.8"
encoding_rs = "=0.8.31"
Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ chrono = { version = "=0.4.22", default-features = false, features = ["clock"] }
clap = "=3.1.12"
clap_complete = "=3.1.2"
clap_complete_fig = "=3.1.5"
console_static_text = "=0.3.4"
console_static_text.workspace = true
data-url.workspace = true
dissimilar = "=1.0.4"
dprint-plugin-json = "=0.17.0"
Expand Down
45 changes: 45 additions & 0 deletions cli/tests/integration/run_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4027,6 +4027,51 @@ fn stdio_streams_are_locked_in_permission_prompt() {
});
}

#[test]
fn permission_prompt_strips_ansi_codes_and_control_chars() {
let _guard = util::http_server();
util::with_pty(&["repl"], |mut console| {
console.write_line(
r#"Deno.permissions.request({ name: "env", variable: "\rDo you like ice cream? y/n" });"#
);
console.write_line("close();");
let output = console.read_all_output();

assert!(output.contains(
"┌ ⚠️ Deno requests env access to \"Do you like ice cream? y/n\"."
));
});

util::with_pty(&["repl"], |mut console| {
console.write_line(
r#"
const boldANSI = "\u001b[1m" // bold
const unboldANSI = "\u001b[22m" // unbold
const prompt = `┌ ⚠️ ${boldANSI}Deno requests run access to "echo"${unboldANSI}
├ Requested by \`Deno.Command().output()`
const moveANSIUp = "\u001b[1A" // moves to the start of the line
const clearANSI = "\u001b[2K" // clears the line
const moveANSIStart = "\u001b[1000D" // moves to the start of the line
Deno[Object.getOwnPropertySymbols(Deno)[0]].core.ops.op_spawn_child({
cmd: "cat",
args: ["/etc/passwd"],
clearEnv: false,
env: [],
stdin: "null",
stdout: "inherit",
stderr: "piped"
}, moveANSIUp + clearANSI + moveANSIStart + prompt)"#,
);
console.write_line("close();");
let output = console.read_all_output();

assert!(output.contains(r#"┌ ⚠️ Deno requests run access to "cat""#));
});
}

itest!(node_builtin_modules_ts {
args: "run --quiet --allow-read run/node_builtin_modules/mod.ts hello there",
output: "run/node_builtin_modules/mod.ts.out",
Expand Down
1 change: 1 addition & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ deno_websocket.workspace = true
deno_webstorage.workspace = true

atty.workspace = true
console_static_text.workspace = true
dlopen.workspace = true
encoding_rs.workspace = true
filetime = "0.2.16"
Expand Down
16 changes: 14 additions & 2 deletions runtime/permissions/prompter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use once_cell::sync::Lazy;

/// Helper function to strip ansi codes and ASCII control characters.
fn strip_ansi_codes_and_ascii_control(s: &str) -> std::borrow::Cow<str> {
console_static_text::strip_ansi_codes(s)
.chars()
.filter(|c| !c.is_ascii_control())
.collect()
}

pub const PERMISSION_EMOJI: &str = "⚠️";

#[derive(Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -203,6 +211,10 @@ impl PermissionPrompter for TtyPrompter {
let _stdout_guard = std::io::stdout().lock();
let _stderr_guard = std::io::stderr().lock();

let message = strip_ansi_codes_and_ascii_control(message);
let name = strip_ansi_codes_and_ascii_control(name);
let api_name = api_name.map(strip_ansi_codes_and_ascii_control);

// print to stderr so that if stdout is piped this is still displayed.
let opts: String = if is_unary {
format!("[y/n/A] (y = yes, allow; n = no, deny; A = allow all {name} permissions)")
Expand All @@ -211,9 +223,9 @@ impl PermissionPrompter for TtyPrompter {
};
eprint!("┌ {PERMISSION_EMOJI} ");
eprint!("{}", colors::bold("Deno requests "));
eprint!("{}", colors::bold(message));
eprint!("{}", colors::bold(message.clone()));
eprintln!("{}", colors::bold("."));
if let Some(api_name) = api_name {
if let Some(api_name) = api_name.clone() {
eprintln!("├ Requested by `{api_name}` API");
}
let msg = format!("Run again with --allow-{name} to bypass this prompt.");
Expand Down

0 comments on commit 78d4301

Please sign in to comment.