-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: introduce ShellQuoted to avoid shell quoting issues (#56)
There was a shell quoting issue here: https://github.com/pnpm/pn/blob/9c9654ac4e6501386e6f0782f6ee24d7aa8dad95/src/main.rs#L112 Where `pn echo ';ls'` would end up running `ls`. This PR introduces `ShellQuoted` that takes care of quoting, and changes functions that end up calling `sh -c ...` to use that as parameter, to reduce the chance of reintroducing this kind of issue. Another option is to avoid calling `sh -c ...` and instead use https://github.com/denoland/deno_task_shell which will also have the benefit of being more consistent across operating systems. --------- Co-authored-by: khai96_ <[email protected]>
- Loading branch information
Showing
4 changed files
with
103 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use derive_more::{Display, Into}; | ||
use os_display::Quoted; | ||
use std::ffi::OsStr; | ||
|
||
#[derive(Debug, Display, Into)] | ||
pub struct ShellQuoted(String); | ||
|
||
impl AsRef<OsStr> for ShellQuoted { | ||
fn as_ref(&self) -> &OsStr { | ||
self.0.as_ref() | ||
} | ||
} | ||
|
||
impl ShellQuoted { | ||
/// `command` will not be quoted | ||
pub fn from_command(command: String) -> Self { | ||
Self(command) | ||
} | ||
|
||
pub fn push_arg<S: AsRef<str>>(&mut self, arg: S) { | ||
use std::fmt::Write; | ||
if !self.0.is_empty() { | ||
self.0.push(' '); | ||
} | ||
let quoted = Quoted::unix(arg.as_ref()); // because pn uses `sh -c` even on Windows | ||
write!(self.0, "{quoted}").expect("string write doesn't panic"); | ||
} | ||
|
||
pub fn from_command_and_args<Args>(command: String, args: Args) -> Self | ||
where | ||
Args: IntoIterator, | ||
Args::Item: AsRef<str>, | ||
{ | ||
let mut cmd = Self::from_command(command); | ||
for arg in args { | ||
cmd.push_arg(arg); | ||
} | ||
cmd | ||
} | ||
|
||
pub fn from_args<Args>(args: Args) -> Self | ||
where | ||
Args: IntoIterator, | ||
Args::Item: AsRef<str>, | ||
{ | ||
Self::from_command_and_args(String::default(), args) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use pretty_assertions::assert_eq; | ||
|
||
#[test] | ||
fn test_from_command_and_args() { | ||
let command = ShellQuoted::from_command_and_args( | ||
"echo hello world".into(), | ||
["abc", ";ls /etc", "ghi jkl", "\"", "'"], | ||
); | ||
assert_eq!( | ||
command.to_string(), | ||
r#"echo hello world 'abc' ';ls /etc' 'ghi jkl' '"' "'""# | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,6 +58,15 @@ fn run_script() { | |
"\n> [email protected] {}\n> echo hello world '\"me\"'\n\n", | ||
temp_dir.path().pipe(dunce::canonicalize).unwrap().display(), | ||
)); | ||
|
||
Command::cargo_bin("pn") | ||
.unwrap() | ||
.current_dir(&temp_dir) | ||
.args(["echo", ";ls"]) | ||
.assert() | ||
.success() | ||
.stdout(";ls\n") | ||
.stderr(""); | ||
} | ||
|
||
#[test] | ||
|