Skip to main content

ready_set_sdk/
sandbox.rs

1//! Sandbox abstraction for plugins.
2//!
3//! In v0.1.0 the per-platform implementations are no-op stubs that record
4//! declared capabilities only. The trait surface is `stable`; implementations
5//! are `experimental` and will gain real enforcement post-v0.1.0.
6
7use crate::error::Result;
8
9/// Capability a plugin may declare to its sandbox.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[non_exhaustive]
12pub enum Capability {
13    /// Read files within the project root.
14    ReadProject,
15    /// Write files within the project root.
16    WriteProject,
17    /// Read files within the user's home directory.
18    ReadHome,
19    /// Write files within the user's home directory.
20    WriteHome,
21    /// Network access.
22    Network,
23    /// Spawn subprocesses.
24    Subprocess,
25}
26
27/// A platform sandbox. Plugins declare the capabilities they need, then
28/// `enter` to commit to the sandbox.
29pub trait Sandbox {
30    /// Declare the capabilities this plugin needs.
31    fn declare(&mut self, capabilities: &[Capability]);
32    /// Enter the sandbox. After this returns, attempts to use undeclared
33    /// capabilities should fail.
34    ///
35    /// # Errors
36    ///
37    /// Implementation-specific. The v0.1.0 stub never errors.
38    fn enter(&self) -> Result<()>;
39}
40
41/// A no-op sandbox that records declarations and does not enforce anything.
42#[derive(Debug, Default, Clone)]
43pub struct NoopSandbox {
44    declared: Vec<Capability>,
45}
46
47impl NoopSandbox {
48    /// Capabilities recorded so far.
49    #[must_use]
50    pub fn declared(&self) -> &[Capability] {
51        &self.declared
52    }
53}
54
55impl Sandbox for NoopSandbox {
56    fn declare(&mut self, capabilities: &[Capability]) {
57        self.declared.extend_from_slice(capabilities);
58    }
59
60    fn enter(&self) -> Result<()> {
61        Ok(())
62    }
63}
64
65/// Return the sandbox for the current platform.
66///
67/// In v0.1.0 every platform returns a [`NoopSandbox`].
68#[must_use]
69pub fn for_current_platform() -> Box<dyn Sandbox> {
70    Box::new(NoopSandbox::default())
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn noop_sandbox_records_declarations() {
79        let mut sb = NoopSandbox::default();
80        sb.declare(&[Capability::ReadProject, Capability::Subprocess]);
81        sb.enter().unwrap();
82        assert_eq!(
83            sb.declared(),
84            &[Capability::ReadProject, Capability::Subprocess]
85        );
86    }
87}