History

Add sandboxed_shell

Richard Feldman committed 1 year ago

Commit 38136cb1 parent 6ed6e8b4 files · +118 −11Browse files

Unified · Split

4 files
  1. Cargo.lock +12 −11
  2. crates/scripting_tool/Cargo.toml +1 −0
  3. crates/scripting_tool/src/sandboxed_shell.rs +104 −0
  4. crates/scripting_tool/src/scripting_tool.rs +1 −0
Cargo.lock+12−11
@@ -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
4402checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"4402checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
4403dependencies = [4403dependencies = [
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
5064dependencies = [5064dependencies = [
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
6709checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65"6709checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65"
6710dependencies = [6710dependencies = [
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
15912source = "registry+https://github.com/rust-lang/crates.io-index"15913source = "registry+https://github.com/rust-lang/crates.io-index"
15913checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"15914checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
15914dependencies = [15915dependencies = [
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
16377checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d"16378checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d"
16378dependencies = [16379dependencies = [
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]]
crates/scripting_tool/Cargo.toml+1−0
@@ -16,6 +16,7 @@ doctest = false · 15 unmodified lines
16anyhow.workspace = true16anyhow.workspace = true
17assistant_tool.workspace = true17assistant_tool.workspace = true
18collections.workspace = true18collections.workspace = true
19shlex.workspace = true
19futures.workspace = true20futures.workspace = true
20gpui.workspace = true21gpui.workspace = true
21mlua.workspace = true22mlua.workspace = true
crates/scripting_tool/src/sandboxed_shell.rs+104−0
@@ -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.
8use mlua::{Error, Result};
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11struct ShellCmd {
12 command: String,
13 args: Vec<String>,
14 stdout_redirect: Option<String>,
15 stderr_redirect: Option<String>,
16}
17
18impl 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}
crates/scripting_tool/src/scripting_tool.rs+1−0
@@ -1,3 +1,4 @@
1mod sandboxed_shell;
1mod session;2mod session;
2 3
3use project::Project;4use project::Project;