Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 56 additions & 4 deletions src/shims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ fn remove_shims_individually(shims_dir: &Path) -> Result<()> {
let entry = entry?;
let name = entry.file_name();
// skip dotfiles (e.g. .mode) — these are metadata, not shims
if name.to_string_lossy().starts_with('.') {
if is_hidden_shim_name(&name) {
continue;
}
let path = entry.path();
Expand Down Expand Up @@ -440,11 +440,15 @@ fn list_executables_in_dir(dir: &Path) -> Result<HashSet<String>> {
.read_dir()?
.map(|bin| {
let bin = bin?;
let name = bin.file_name();
if is_hidden_shim_name(&name) {
return Ok(None);
}
// files and symlinks which are executable
if file::is_executable(&bin.path())
&& (bin.file_type()?.is_file() || bin.file_type()?.is_symlink())
{
Ok(Some(bin.file_name().into_string().unwrap()))
Ok(name.into_string().ok())
} else {
Ok(None)
Comment on lines 448 to 453

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using .unwrap() on into_string() can cause mise to panic and crash if any file in the tool's directory has an invalid UTF-8 filename. Since list_executables_in_dir scans external tool directories, it is safer to use .ok() to gracefully skip files with non-UTF-8 names.

Suggested change
if file::is_executable(&bin.path())
&& (bin.file_type()?.is_file() || bin.file_type()?.is_symlink())
{
Ok(Some(bin.file_name().into_string().unwrap()))
Ok(Some(name.into_string().unwrap()))
} else {
Ok(None)
if file::is_executable(&bin.path())
&& (bin.file_type()?.is_file() || bin.file_type()?.is_symlink())
{
Ok(name.into_string().ok())
} else {
Ok(None)
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 311019e. Both shim scanners now use name.into_string().ok() so non-UTF-8 filenames are skipped instead of panicking, and list_shims reuses the bound name.

This comment was generated by an AI coding assistant.

}
Expand All @@ -462,14 +466,14 @@ fn list_shims() -> Result<HashSet<String>> {
let bin = bin?;
let name = bin.file_name();
// skip dotfiles (e.g. .mode) — these are metadata, not shims
if name.to_string_lossy().starts_with('.') {
if is_hidden_shim_name(&name) {
return Ok(None);
}
// files and symlinks which are executable or extensionless files (Git Bash/Cygwin)
if (file::is_executable(&bin.path()) || bin.path().extension().is_none())
&& (bin.file_type()?.is_file() || bin.file_type()?.is_symlink())
{
Ok(Some(bin.file_name().into_string().unwrap()))
Ok(name.into_string().ok())
} else {
Ok(None)
}
Expand All @@ -480,6 +484,10 @@ fn list_shims() -> Result<HashSet<String>> {
.collect())
}

fn is_hidden_shim_name(name: &std::ffi::OsStr) -> bool {
name.to_string_lossy().starts_with('.')
}
Comment thread
cursor[bot] marked this conversation as resolved.

async fn get_desired_shims(
config: &Arc<Config>,
mise_bin: &Path,
Expand Down Expand Up @@ -610,3 +618,47 @@ async fn err_no_version_set(
Err(eyre!(msg.trim().to_string()))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn list_executables_in_dir_skips_dotfiles() {
let dir = tempfile::tempdir().unwrap();
let visible_name = if cfg!(windows) {
"ffmpeg.exe"
} else {
"ffmpeg"
};
let visible = dir.path().join(visible_name);
let hidden = dir.path().join(".librsvg-post-link.exe");

fs::write(&visible, "").unwrap();
fs::write(&hidden, "").unwrap();
file::make_executable(&visible).unwrap();
file::make_executable(&hidden).unwrap();

let bins = list_executables_in_dir(dir.path()).unwrap();

assert!(bins.contains(visible_name));
assert!(!bins.contains(".librsvg-post-link.exe"));
}

#[cfg(target_os = "linux")]
#[test]
fn list_executables_in_dir_skips_non_utf8_names() {
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;

let dir = tempfile::tempdir().unwrap();
let non_utf8 = dir.path().join(OsString::from_vec(vec![0xff]));

fs::write(&non_utf8, "").unwrap();
file::make_executable(&non_utf8).unwrap();

let bins = list_executables_in_dir(dir.path()).unwrap();

assert!(bins.is_empty());
}
}
Loading