Skip to content

Commit 820fe74

Browse files
karthiknadigCopilot
andcommitted
fix: parse Unicode Conda command paths safely (PR #493)
Use ASCII-insensitive byte-stable matching and checked slicing for history command extraction. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8c6055c commit 820fe74

1 file changed

Lines changed: 26 additions & 6 deletions

File tree

crates/pet-conda/src/environments.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,17 +207,28 @@ fn is_conda_env_name_in_cmd(cmd_line: &str, name: &str) -> bool {
207207
cmd_line.contains(format!("-n {name}").as_str())
208208
|| cmd_line.contains(format!("--name {name}").as_str())
209209
}
210+
fn find_ascii_case_insensitive(haystack: &str, needle: &str) -> Option<usize> {
211+
haystack
212+
.as_bytes()
213+
.windows(needle.len())
214+
.position(|window| window.eq_ignore_ascii_case(needle.as_bytes()))
215+
}
216+
217+
fn get_conda_executable_from_cmd(cmd_line: &str) -> Option<PathBuf> {
218+
let start_index = find_ascii_case_insensitive(cmd_line, "# cmd:")? + "# cmd:".len();
219+
let end_index = find_ascii_case_insensitive(cmd_line, " create -")?;
220+
let executable = cmd_line.get(start_index..end_index)?.trim();
221+
(!executable.is_empty()).then(|| PathBuf::from(executable))
222+
}
223+
210224
fn get_conda_dir_from_cmd(cmd_line: &str) -> Option<PathBuf> {
211225
// Sample lines
212-
// # cmd: <conda install directory>\Scripts\conda-script.py create -n samlpe1
226+
// # cmd: <conda install directory>\Scripts\conda-script.py create -n sample
213227
// # cmd: <conda install directory>\Scripts\conda-script.py create -p <full path>
214228
// # cmd: /Users/donjayamanne/miniconda3/bin/conda create -n conda1
215-
// # cmd_line: "# cmd: /usr/bin/conda create -p ./prefix-envs/.conda1 python=3.12 -y"
216-
let start_index = cmd_line.to_lowercase().find("# cmd:")? + "# cmd:".len();
217-
let end_index = cmd_line.to_lowercase().find(" create -")?;
218-
let conda_exe = PathBuf::from(cmd_line[start_index..end_index].trim().to_string());
219-
// Sometimes the path can be as follows, where `/usr/bin/conda` could be a symlink.
220229
// cmd_line: "# cmd: /usr/bin/conda create -p ./prefix-envs/.conda1 python=3.12 -y"
230+
let conda_exe = get_conda_executable_from_cmd(cmd_line)?; // Sometimes the path can be as follows, where `/usr/bin/conda` could be a symlink.
231+
// cmd_line: "# cmd: /usr/bin/conda create -p ./prefix-envs/.conda1 python=3.12 -y"
221232
let conda_exe = resolve_symlink(&conda_exe).unwrap_or(conda_exe);
222233
if let Some(cmd_line) = conda_exe.parent() {
223234
if let Some(conda_dir) = cmd_line.file_name() {
@@ -298,6 +309,15 @@ pub fn get_activation_command(
298309
mod tests {
299310
use super::*;
300311

312+
#[test]
313+
fn parses_unicode_conda_executable_without_invalid_byte_indices() {
314+
let line = "# CMD: /Users/İpek/miniconda3/bin/conda CREATE -n sample";
315+
316+
assert_eq!(
317+
get_conda_executable_from_cmd(line),
318+
Some(PathBuf::from("/Users/İpek/miniconda3/bin/conda"))
319+
);
320+
}
301321
#[test]
302322
#[cfg(windows)]
303323
fn parse_cmd_line() {

0 commit comments

Comments
 (0)