Skip to content

Commit

Permalink
Add support for shebang containing spaces like asdf's node does (#2182)
Browse files Browse the repository at this point in the history
* Add support for shebang containing spaces like asdf's node does

* Add support for shebang containing spaces like asdf's node does

* fix

* Return original shebang including whitespace.

- Handle whitespace between magic and path.
- Don't skip BOM at the start because scripts with a BOM before the shebang don't work anyways.
- Don't handle `#![` specially because the os also doesn't.
- Remove now unused whitespace module.
- Shebang whitespace tests.

---------

Co-authored-by: t4lz <[email protected]>
  • Loading branch information
aviramha and t4lz authored Jan 19, 2024
1 parent 3df3392 commit 8c692c8
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 116 deletions.
1 change: 1 addition & 0 deletions changelog.d/2181.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for shebang containing spaces like asdf's node does
73 changes: 52 additions & 21 deletions mirrord/sip/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
mod codesign;
mod error;
mod rpath;
mod whitespace;

mod main {
use std::{
Expand Down Expand Up @@ -334,21 +333,20 @@ mod main {
/// Extract shebang from file contents.
/// "#!/usr/bin/env bash\n..." -> Some("#!/usr/bin/env")
fn get_shebang_from_string(file_contents: &str) -> Option<String> {
const BOM: &str = "\u{feff}";
let content = file_contents.strip_prefix(BOM).unwrap_or(file_contents);
let rest = content.strip_prefix("#!")?;
let rest = file_contents.strip_prefix("#!")?;

if whitespace::skip(rest).starts_with('[') {
None
let mut char_iter = rest
.char_indices()
.skip_while(|(_, c)| c.is_whitespace()) // any whitespace directly after #!
.skip_while(|(_, c)| !c.is_whitespace()); // Any non-whitespace characters after that (path)

let shebang = if let Some((path_len, _next_char)) = char_iter.next() {
file_contents.get(..path_len + 2)? // +2 for #! because the index is in `rest`
} else {
content
.split_once('\n')
.map(|(line, _)| line)
.unwrap_or(content)
.split_whitespace()
.next()
.map(ToString::to_string)
}
// There is no next character after the shebang, so the whole file is just shebang.
file_contents
};
Some(shebang.to_string())
}

/// Including '#!', just until whitespace, no arguments.
Expand Down Expand Up @@ -689,19 +687,38 @@ mod main {
assert_eq!(cpu_type, macho::CPU_TYPE_X86_64);
}

#[test]
fn patch_script_with_shebang() {
fn test_patch_script_with_shebang(
file_contents: &str,
patched_binary_path: &str,
new_file_contents: &str,
) {
let mut original_file = tempfile::NamedTempFile::new().unwrap();
let patched_path =
env::temp_dir().join(original_file.path().strip_prefix("/").unwrap());
original_file
.write_all("#!/usr/bin/env bash\n".as_ref())
.unwrap();
original_file.write_all(file_contents.as_ref()).unwrap();
original_file.flush().unwrap();
std::fs::create_dir_all(patched_path.parent().unwrap()).unwrap();
patch_script(original_file.path(), &patched_path, "/test/shebang").unwrap();
patch_script(original_file.path(), &patched_path, patched_binary_path).unwrap();
let new_contents = std::fs::read(&patched_path).unwrap();
assert_eq!(new_contents, "#!/test/shebang bash\n".as_bytes())
assert_eq!(new_contents, new_file_contents.as_bytes())
}

#[test]
fn patch_script_with_shebang() {
test_patch_script_with_shebang(
"#!/usr/bin/env bash\n",
"/test/shebang",
"#!/test/shebang bash\n",
);
}

#[test]
fn patch_script_with_shebang_with_space() {
test_patch_script_with_shebang(
"#! /usr/bin/env bash\n",
"/test/shebang",
"#!/test/shebang bash\n",
);
}

#[test]
Expand All @@ -713,6 +730,20 @@ mod main {
)
}

#[test]
fn shebang_from_string_with_space() {
let contents = "#! /usr/bin/env bash\n".to_string();
assert_eq!(
get_shebang_from_string(&contents).unwrap(),
"#! /usr/bin/env"
);
let contents = "#! /usr/bin/env bash\n".to_string();
assert_eq!(
get_shebang_from_string(&contents).unwrap(),
"#! /usr/bin/env"
)
}

/// Run `sip_patch` on a script with a shebang that points to `env`, verify that a path to
/// a new script is returned, in which the shebang points to a patched version of `env`
/// that is not SIPed.
Expand Down
95 changes: 0 additions & 95 deletions mirrord/sip/src/whitespace.rs

This file was deleted.

0 comments on commit 8c692c8

Please sign in to comment.