Unified · Split
| @@ -2300,7 +2300,7 @@ dependencies = [ · 2299 unmodified lines | |||
| 2300 | "cap-primitives", | 2300 | "cap-primitives", |
| 2301 | "cap-std", | 2301 | "cap-std", |
| 2302 | "io-lifetimes", | 2302 | "io-lifetimes", |
| 2303 | "windows-sys 0.52.0", | 2303 | "windows-sys 0.59.0", |
| 2304 | ] | 2304 | ] |
| 2305 | 2305 | ||
| 2306 | [[package]] | 2306 | [[package]] |
| @@ -2328,7 +2328,7 @@ dependencies = [ · 21 unmodified lines | |||
| 2328 | "ipnet", | 2328 | "ipnet", |
| 2329 | "maybe-owned", | 2329 | "maybe-owned", |
| 2330 | "rustix", | 2330 | "rustix", |
| 2331 | "windows-sys 0.52.0", | 2331 | "windows-sys 0.59.0", |
| 2332 | "winx", | 2332 | "winx", |
| 2333 | ] | 2333 | ] |
| 2334 | 2334 | ||
| @@ -4402,7 +4402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" · 2067 unmodified lines | |||
| 4402 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" | 4402 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" |
| 4403 | dependencies = [ | 4403 | dependencies = [ |
| 4404 | "libc", | 4404 | "libc", |
| 4405 | "windows-sys 0.52.0", | 4405 | "windows-sys 0.59.0", |
| 4406 | ] | 4406 | ] |
| 4407 | 4407 | ||
| 4408 | [[package]] | 4408 | [[package]] |
| @@ -5064,7 +5064,7 @@ checksum = "5e2e6123af26f0f2c51cc66869137080199406754903cc926a7690401ce09cb4" · 655 unmodified lines | |||
| 5064 | dependencies = [ | 5064 | dependencies = [ |
| 5065 | "io-lifetimes", | 5065 | "io-lifetimes", |
| 5066 | "rustix", | 5066 | "rustix", |
| 5067 | "windows-sys 0.52.0", | 5067 | "windows-sys 0.59.0", |
| 5068 | ] | 5068 | ] |
| 5069 | 5069 | ||
| 5070 | [[package]] | 5070 | [[package]] |
| @@ -6709,7 +6709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" · 1638 unmodified lines | |||
| 6709 | checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" | 6709 | checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" |
| 6710 | dependencies = [ | 6710 | dependencies = [ |
| 6711 | "io-lifetimes", | 6711 | "io-lifetimes", |
| 6712 | "windows-sys 0.52.0", | 6712 | "windows-sys 0.59.0", |
| 6713 | ] | 6713 | ] |
| 6714 | 6714 | ||
| 6715 | [[package]] | 6715 | [[package]] |
| @@ -10734,7 +10734,7 @@ dependencies = [ · 4018 unmodified lines | |||
| 10734 | "once_cell", | 10734 | "once_cell", |
| 10735 | "socket2", | 10735 | "socket2", |
| 10736 | "tracing", | 10736 | "tracing", |
| 10737 | "windows-sys 0.52.0", | 10737 | "windows-sys 0.59.0", |
| 10738 | ] | 10738 | ] |
| 10739 | 10739 | ||
| 10740 | [[package]] | 10740 | [[package]] |
| @@ -11656,7 +11656,7 @@ dependencies = [ · 915 unmodified lines | |||
| 11656 | "libc", | 11656 | "libc", |
| 11657 | "linux-raw-sys", | 11657 | "linux-raw-sys", |
| 11658 | "once_cell", | 11658 | "once_cell", |
| 11659 | "windows-sys 0.52.0", | 11659 | "windows-sys 0.59.0", |
| 11660 | ] | 11660 | ] |
| 11661 | 11661 | ||
| 11662 | [[package]] | 11662 | [[package]] |
| @@ -11927,6 +11927,7 @@ dependencies = [ · 264 unmodified lines | |||
| 11927 | "serde", | 11927 | "serde", |
| 11928 | "serde_json", | 11928 | "serde_json", |
| 11929 | "settings", | 11929 | "settings", |
| 11930 | "shlex", | ||
| 11930 | "util", | 11931 | "util", |
| 11931 | ] | 11932 | ] |
| 11932 | 11933 | ||
| @@ -13426,7 +13427,7 @@ dependencies = [ · 1493 unmodified lines | |||
| 13426 | "fd-lock", | 13427 | "fd-lock", |
| 13427 | "io-lifetimes", | 13428 | "io-lifetimes", |
| 13428 | "rustix", | 13429 | "rustix", |
| 13429 | "windows-sys 0.52.0", | 13430 | "windows-sys 0.59.0", |
| 13430 | "winx", | 13431 | "winx", |
| 13431 | ] | 13432 | ] |
| 13432 | 13433 | ||
| @@ -13566,7 +13567,7 @@ dependencies = [ · 133 unmodified lines | |||
| 13566 | "getrandom 0.3.1", | 13567 | "getrandom 0.3.1", |
| 13567 | "once_cell", | 13568 | "once_cell", |
| 13568 | "rustix", | 13569 | "rustix", |
| 13569 | "windows-sys 0.52.0", | 13570 | "windows-sys 0.59.0", |
| 13570 | ] | 13571 | ] |
| 13571 | 13572 | ||
| 13572 | [[package]] | 13573 | [[package]] |
| @@ -15912,7 +15913,7 @@ version = "0.1.9" · 2339 unmodified lines | |||
| 15912 | source = "registry+https://github.com/rust-lang/crates.io-index" | 15913 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 15913 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" | 15914 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" |
| 15914 | dependencies = [ | 15915 | dependencies = [ |
| 15915 | "windows-sys 0.52.0", | 15916 | "windows-sys 0.59.0", |
| 15916 | ] | 15917 | ] |
| 15917 | 15918 | ||
| 15918 | [[package]] | 15919 | [[package]] |
| @@ -16377,7 +16378,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" · 458 unmodified lines | |||
| 16377 | checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" | 16378 | checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" |
| 16378 | dependencies = [ | 16379 | dependencies = [ |
| 16379 | "bitflags 2.8.0", | 16380 | "bitflags 2.8.0", |
| 16380 | "windows-sys 0.52.0", | 16381 | "windows-sys 0.59.0", |
| 16381 | ] | 16382 | ] |
| 16382 | 16383 | ||
| 16383 | [[package]] | 16384 | [[package]] |
| @@ -16,6 +16,7 @@ doctest = false · 15 unmodified lines | |||
| 16 | anyhow.workspace = true | 16 | anyhow.workspace = true |
| 17 | assistant_tool.workspace = true | 17 | assistant_tool.workspace = true |
| 18 | collections.workspace = true | 18 | collections.workspace = true |
| 19 | shlex.workspace = true | ||
| 19 | futures.workspace = true | 20 | futures.workspace = true |
| 20 | gpui.workspace = true | 21 | gpui.workspace = true |
| 21 | mlua.workspace = true | 22 | mlua.workspace = true |
| @@ -0,0 +1,104 @@ | |||
| 1 | /// Models will commonly generate POSIX shell one-liner commands which | ||
| 2 | /// they run via io.popen() in Lua. Instead of giving those shell command | ||
| 3 | /// strings to the operating system - which is a security risk, and | ||
| 4 | /// which can eaisly fail on Windows, since Windows doesn't do POSIX - we | ||
| 5 | /// parse the shell command ourselves and translate it into a sequence of | ||
| 6 | /// commands in our normal sandbox. Essentially, this is an extremely | ||
| 7 | /// minimalstic shell which Lua popen() commands can execute in. | ||
| 8 | use mlua::{Error, Result}; | ||
| 9 | |||
| 10 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
| 11 | struct ShellCmd { | ||
| 12 | command: String, | ||
| 13 | args: Vec<String>, | ||
| 14 | stdout_redirect: Option<String>, | ||
| 15 | stderr_redirect: Option<String>, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl ShellCmd { | ||
| 19 | /// Parse a shell string, which we assume the model will generate in POSIX format. | ||
| 20 | /// Note that since we are turning this into our own representation, this should | ||
| 21 | /// work seamlessly on Windows too, even though Windows has a different shell syntax. | ||
| 22 | /// | ||
| 23 | /// If there are multiple commands piped into one another, this returns them all. | ||
| 24 | pub fn parse_shell_str(string: &str) -> Result<Vec<Self>> { | ||
| 25 | // For now, we don't support any of these shell features. We can add support them | ||
| 26 | // in the future, though. | ||
| 27 | if string.contains('$') | ||
| 28 | || string.contains('`') | ||
| 29 | || string.contains('(') | ||
| 30 | || string.contains(')') | ||
| 31 | || string.contains(';') | ||
| 32 | || string.contains('&') | ||
| 33 | || string.contains('{') | ||
| 34 | || string.contains('}') | ||
| 35 | { | ||
| 36 | return Err(Error::RuntimeError( | ||
| 37 | "Complex shell features (pipes, subshells, variables, etc.) are not available in this shell." | ||
| 38 | .to_string(), | ||
| 39 | )); | ||
| 40 | } | ||
| 41 | |||
| 42 | // Use shlex to split the command line into tokens | ||
| 43 | let tokens = shlex::split(string) | ||
| 44 | .ok_or_else(|| Error::RuntimeError("Failed to parse shell command".to_string()))?; | ||
| 45 | |||
| 46 | // The first token is the command | ||
| 47 | let mut tokens_iter = tokens.into_iter(); | ||
| 48 | let Some(command) = tokens_iter.next() else { | ||
| 49 | return Err(Error::RuntimeError("Missing popen command".to_string())); | ||
| 50 | }; | ||
| 51 | |||
| 52 | let mut args = Vec::new(); | ||
| 53 | let mut stdout_redirect = None; | ||
| 54 | let mut stderr_redirect = None; | ||
| 55 | |||
| 56 | // Process the remaining tokens | ||
| 57 | while let Some(token) = tokens_iter.next() { | ||
| 58 | match token.as_str() { | ||
| 59 | ">" | "1>" => { | ||
| 60 | // stdout redirection | ||
| 61 | let target = tokens_iter.next().ok_or_else(|| { | ||
| 62 | Error::RuntimeError("Missing redirection target".to_string()) | ||
| 63 | })?; | ||
| 64 | stdout_redirect = Some(target); | ||
| 65 | } | ||
| 66 | "2>" => { | ||
| 67 | // stderr redirection | ||
| 68 | let target = tokens_iter.next().ok_or_else(|| { | ||
| 69 | Error::RuntimeError("Missing redirection target".to_string()) | ||
| 70 | })?; | ||
| 71 | stderr_redirect = Some(target); | ||
| 72 | } | ||
| 73 | "&>" | ">&" => { | ||
| 74 | // both stdout and stderr redirection | ||
| 75 | let target = tokens_iter.next().ok_or_else(|| { | ||
| 76 | Error::RuntimeError("Missing redirection target".to_string()) | ||
| 77 | })?; | ||
| 78 | stdout_redirect = Some(target.clone()); | ||
| 79 | stderr_redirect = Some(target); | ||
| 80 | } | ||
| 81 | _ if token.starts_with(">") | ||
| 82 | || token.starts_with("2>") | ||
| 83 | || token.starts_with("&>") => | ||
| 84 | { | ||
| 85 | // Handle cases like ">file" without a space | ||
| 86 | return Err(Error::RuntimeError( | ||
| 87 | "Redirections must have a space between operator and target".to_string(), | ||
| 88 | )); | ||
| 89 | } | ||
| 90 | _ => { | ||
| 91 | // Regular argument | ||
| 92 | args.push(token); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | Ok(ShellCmd { | ||
| 98 | command, | ||
| 99 | args, | ||
| 100 | stdout_redirect, | ||
| 101 | stderr_redirect, | ||
| 102 | }) | ||
| 103 | } | ||
| 104 | } | ||
| @@ -1,3 +1,4 @@ | |||
| 1 | mod sandboxed_shell; | ||
| 1 | mod session; | 2 | mod session; |
| 2 | 3 | ||
| 3 | use project::Project; | 4 | use project::Project; |