mirror of
https://github.com/Llewellynvdm/starship.git
synced 2024-12-04 02:48:25 +00:00
Merge commit from fork
addresses GHSA-vx24-x4mv-vwr5
This commit is contained in:
parent
4fa3914ba7
commit
cfc58161e0
4
.github/config-schema.json
vendored
4
.github/config-schema.json
vendored
@ -6444,6 +6444,10 @@
|
||||
"ignore_timeout": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"unsafe_no_escape": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
@ -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. |
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user