Merge commit from fork

addresses GHSA-vx24-x4mv-vwr5
This commit is contained in:
David Knaack 2024-07-26 19:31:41 +02:00 committed by GitHub
parent 4fa3914ba7
commit cfc58161e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 84 additions and 19 deletions

View File

@ -6444,6 +6444,10 @@
"ignore_timeout": {
"default": false,
"type": "boolean"
},
"unsafe_no_escape": {
"default": false,
"type": "boolean"
}
},
"additionalProperties": false

View File

@ -4756,11 +4756,12 @@ If you have an interesting example not covered there, feel free to share it ther
:::
::: warning Command output is printed unescaped to the prompt
::: warning If `unsafe_no_escape` is enabled or prior to starship v1.20 command output is printed unescaped to the prompt.
Whatever output the command generates is printed unmodified in the prompt. This means if the output
contains special sequences that are interpreted by your shell they will be expanded when displayed.
These special sequences are shell specific, e.g. you can write a command module that writes bash sequences,
contains shell-specific interpretable sequences, they could be interpreted on display.
Depending on the shell, this can mean that e.g. strings enclosed by backticks are executed by the shell.
Such sequences are usually shell specific, e.g. you can write a command module that writes bash sequences,
e.g. `\h`, but this module will not work in a fish or zsh shell.
Format strings can also contain shell specific prompt sequences, e.g.
@ -4778,6 +4779,7 @@ Format strings can also contain shell specific prompt sequences, e.g.
| `require_repo` | `false` | If `true`, the module will only be shown in paths containing a (git) repository. This option alone is not sufficient display condition in absence of other options. |
| `shell` | | [See below](#custom-command-shell) |
| `description` | `'<custom module>'` | The description of the module that is shown when running `starship explain`. |
| `unsafe_no_escape` | `false` | When set, command output is not escaped of characters that could be interpreted by the shell. |
| `detect_files` | `[]` | The files that will be searched in the working directory for a match. |
| `detect_folders` | `[]` | The directories that will be searched in the working directory for a match. |
| `detect_extensions` | `[]` | The extensions that will be searched in the working directory for a match. |

View File

@ -30,6 +30,7 @@ pub struct CustomConfig<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub use_stdin: Option<bool>,
pub ignore_timeout: bool,
pub unsafe_no_escape: bool,
}
impl<'a> Default for CustomConfig<'a> {
@ -50,6 +51,7 @@ impl<'a> Default for CustomConfig<'a> {
os: None,
use_stdin: None,
ignore_timeout: false,
unsafe_no_escape: false,
}
}
}

View File

@ -59,8 +59,22 @@ pub fn module<'a>(name: &str, context: &'a Context) -> Option<Module<'a>> {
}
}
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter
let variables_closure = |variable: &str| match variable {
"output" => {
let output = exec_command(config.command, context, &config)?;
let trimmed = output.trim();
if trimmed.is_empty() {
None
} else {
Some(Ok(trimmed.to_string()))
}
}
_ => None,
};
let parsed = StringFormatter::new(config.format).and_then(|mut formatter| {
formatter = formatter
.map_meta(|var, _| match var {
"symbol" => Some(config.symbol),
_ => None,
@ -68,21 +82,15 @@ pub fn module<'a>(name: &str, context: &'a Context) -> Option<Module<'a>> {
.map_style(|variable| match variable {
"style" => Some(Ok(config.style)),
_ => None,
})
.map_no_escaping(|variable| match variable {
"output" => {
let output = exec_command(config.command, context, &config)?;
let trimmed = output.trim();
});
if trimmed.is_empty() {
None
} else {
Some(Ok(trimmed.to_string()))
}
}
_ => None,
})
.parse(None, Some(context))
if config.unsafe_no_escape {
formatter = formatter.map_no_escaping(variables_closure)
} else {
formatter = formatter.map(variables_closure)
}
formatter.parse(None, Some(context))
});
match parsed {
@ -244,6 +252,11 @@ fn exec_when(cmd: &str, config: &CustomConfig, context: &Context) -> bool {
fn exec_command(cmd: &str, context: &Context, config: &CustomConfig) -> Option<String> {
log::trace!("Running '{cmd}'");
#[cfg(test)]
if cmd == "__starship_to_be_escaped" {
return Some("`to_be_escaped`".to_string());
}
if let Some(output) = shell_command(cmd, config, context) {
if !output.status.success() {
log::trace!("Non-zero exit code '{:?}'", output.status.code());
@ -298,6 +311,7 @@ fn handle_shell(command: &mut Command, shell: &str, shell_args: &[&str]) -> bool
mod tests {
use super::*;
use crate::context::Shell;
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
use nu_ansi_term::Color;
use std::fs::File;
@ -761,4 +775,47 @@ mod tests {
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn output_is_escaped() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("custom.test")
.path(dir.path())
.config(toml::toml! {
[custom.test]
format = "$output"
command = "__starship_to_be_escaped"
when = true
ignore_timeout = true
})
.shell(Shell::Bash)
.collect();
let expected = Some("\\`to_be_escaped\\`".to_string());
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn unsafe_no_escape() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("custom.test")
.path(dir.path())
.config(toml::toml! {
[custom.test]
format = "$output"
command = "__starship_to_be_escaped"
when = true
ignore_timeout = true
unsafe_no_escape = true
})
.shell(Shell::Bash)
.collect();
let expected = Some("`to_be_escaped`".to_string());
assert_eq!(expected, actual);
dir.close()
}
}