Skip to main content

tartarus_api/
config.rs

1#[cfg(feature = "config_file")]
2pub mod file;
3pub mod overrides;
4
5use crate::{
6    error::Result,
7    sandbox::{Sandbox, SandboxType},
8};
9use std::{
10    fmt::Debug,
11    fs::File,
12    io::{BufReader, BufWriter, Read, Write},
13    path::{Path, PathBuf},
14};
15
16/// The main configuration struct for the sandbox.
17///
18/// If at least one override or passthrough home directory is specified, the sandbox will use a
19/// temporary directory for the home directory instead of the user's actual one. The original home
20/// directory can still be accessed via absolute paths (and will be read-only by default like any
21/// other directory not explicitly specified as writable).
22#[derive(Debug)]
23pub struct SandboxConfig {
24    /// Whether network access is allowed for the sandbox.
25    pub allow_network_access: bool,
26
27    /// The directories to mounted as writable for the sandbox.
28    pub writable_dirs: Option<Vec<PathBuf>>,
29
30    /// Directories to recursively copy into the sandbox under a temporary directory rather than
31    /// mapping the real ones.
32    ///
33    /// Overriden home directories are mounted as writable for the sandbox.
34    pub override_home_dirs: Option<Vec<OverrideHomeDir>>,
35
36    /// Directories to directly mount from the user's home directory under the override home
37    /// directory.
38    ///
39    /// Passthrough directories are mounted as writable for the sandbox.
40    pub passthrough_home_dirs: Option<Vec<PathBuf>>,
41}
42
43impl Default for SandboxConfig {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl SandboxConfig {
50    /// Creates a new configuration with no network access, no writable directories, and no fake
51    /// home directory
52    pub const fn new() -> Self {
53        Self {
54            allow_network_access: false,
55            writable_dirs: None,
56            override_home_dirs: None,
57            passthrough_home_dirs: None,
58        }
59    }
60
61    /// Enables network access for the sandbox.
62    pub const fn with_network_access(&mut self) -> &mut Self {
63        self.allow_network_access = true;
64        self
65    }
66
67    /// Marks a directory as writable for the sandbox.
68    pub fn with_writable_dir(&mut self, dir: impl Into<PathBuf>) -> &mut Self {
69        self.writable_dirs.get_or_insert_default().push(dir.into());
70        self
71    }
72
73    /// Marks a subdirectory of the user's home as an override.
74    pub fn with_override_home_dir(&mut self, override_home_dir: OverrideHomeDir) -> &mut Self {
75        self.override_home_dirs
76            .get_or_insert_default()
77            .push(override_home_dir);
78        self
79    }
80
81    /// Marks a subdirectory of the user's home as passthrough.
82    pub fn with_passthrough_home_dir(
83        &mut self,
84        segments: impl IntoIterator<Item = impl AsRef<Path>>,
85    ) -> &mut Self {
86        self.passthrough_home_dirs
87            .get_or_insert_default()
88            .push(PathBuf::from_iter(segments));
89        self
90    }
91
92    /// Builds a sandbox with the given type.
93    pub const fn build_sandbox(&mut self, sandbox_type: SandboxType) -> Sandbox {
94        Sandbox {
95            sandbox_type,
96            config: Self {
97                allow_network_access: self.allow_network_access,
98                writable_dirs: self.writable_dirs.take(),
99                override_home_dirs: self.override_home_dirs.take(),
100                passthrough_home_dirs: self.passthrough_home_dirs.take(),
101            },
102        }
103    }
104
105    /// Returns whether the sandbox should configure a fake home directory.
106    pub(crate) fn needs_fake_home(&self) -> bool {
107        self.override_home_dirs
108            .as_ref()
109            .is_some_and(|v| !v.is_empty()) ||
110            self.passthrough_home_dirs
111                .as_ref()
112                .is_some_and(|v| !v.is_empty())
113    }
114}
115
116/// A directory under the home directory to recursively copy into the sandbox under a temporary
117/// directory rather than mapping the real one.
118#[derive(Debug)]
119pub struct OverrideHomeDir {
120    /// The path of the directory to copy from the host filesystem. These paths are interpreted as
121    /// relative paths below the user's home directory.
122    pub subpath: PathBuf,
123
124    /// Arbitrary extra settings to apply to files mapped to the sandbox
125    pub overrides: Option<Vec<OverrideFile<Box<dyn Override>>>>,
126}
127
128impl OverrideHomeDir {
129    /// Creates a new override home directory for the given path.
130    pub fn new(path_segments: impl IntoIterator<Item = impl AsRef<Path>>) -> Self {
131        Self {
132            subpath: PathBuf::from_iter(path_segments),
133            overrides: None,
134        }
135    }
136
137    /// Adds an override for the given path with the given behavior.
138    pub fn with_override(
139        &mut self,
140        path: impl Into<PathBuf>,
141        behavior: impl Override + 'static,
142    ) -> &mut Self {
143        self.overrides.get_or_insert_default().push(OverrideFile {
144            path: path.into(),
145            behavior: Box::new(behavior),
146        });
147        self
148    }
149
150    /// Helper function to get an owned [`OverrideHomeDir`] instance from a mutable reference to
151    /// facilitate builder-style chaining.
152    #[must_use]
153    pub fn take(&mut self) -> Self {
154        Self {
155            subpath: std::mem::take(&mut self.subpath),
156            overrides: self.overrides.take(),
157        }
158    }
159}
160
161/// Specifies how to override a file from the user's home directory in the sandbox.
162#[derive(Debug)]
163pub struct OverrideFile<T> {
164    pub path: PathBuf,
165    pub behavior: T,
166}
167
168/// Provides lazy access to the file being overridden in the sandbox and a sink for the contents of
169/// the file to override with in the sandbox.
170#[derive(Debug)]
171pub struct FileContents {
172    pub(crate) original: BufReader<File>,
173    pub(crate) output: BufWriter<File>,
174}
175
176impl FileContents {
177    /// Returns a [`Read`] instance for reading the original file contents.
178    pub fn read(&mut self) -> impl Read {
179        &mut self.original
180    }
181
182    /// Returns a [`Write`] instance for writing the sandboxed file contents.
183    pub fn write(&mut self) -> impl Write {
184        &mut self.output
185    }
186}
187
188/// A trait providing a way to define how a file should be overridden in the sandbox.
189///
190/// This can be used to inject extra configurations to the sandbox that you don't want to live in
191/// your standard configurations (bypassing permission checks, allowing arbitrary tool usage, etc.)
192pub trait Override: Debug {
193    /// Defines how the original file should be processed and mapped to the sandbox.
194    ///
195    /// # Arguments
196    ///
197    /// - `contents`: Provides access to the original file contents for reading and the sandboxed
198    ///   output file for writing.
199    ///
200    /// # Errors
201    ///
202    /// Returns an error if the setting could not be applied.
203    fn apply(&self, contents: FileContents) -> Result<()>;
204}