Skip to main content

deck_sandbox/
linux.rs

1//! Linux backend: seccompiler BPF + landlock ruleset.
2//!
3//! In Phase 1 we wire up profile -> ruleset translation and expose the
4//! [`Sandbox`] trait shape. Actual `exec(2)`-time application of the filter
5//! is staged in Phase 2 (it requires forking a helper process and applying
6//! the filter post-fork, pre-exec).
7
8use deck_core::Sandbox;
9
10use crate::profile::SandboxProfile;
11
12#[derive(Debug, Default)]
13pub struct LinuxSandbox {
14    _placeholder: (),
15}
16
17impl LinuxSandbox {
18    /// Translate a profile into a landlock ruleset (Phase 1: returns the
19    /// counts so we can unit-test the translation; actual ruleset object
20    /// is wired in Phase 2 when the child-spawn glue lands).
21    #[must_use]
22    pub fn plan(&self, profile: &SandboxProfile) -> SandboxPlan {
23        SandboxPlan {
24            read_paths: profile.allow_read.len(),
25            write_paths: profile.allow_write.len(),
26            allow_network: profile.allow_network,
27        }
28    }
29}
30
31impl Sandbox for LinuxSandbox {
32    fn availability(&self) -> &'static str {
33        "scaffolded (not enforcing in 0.1)"
34    }
35
36    fn enforces(&self) -> bool {
37        // 0.1 ships the policy types and the dependency wiring but does
38        // NOT yet apply the seccomp BPF filter or the landlock ruleset
39        // around `exec(2)`. Reporting `false` keeps `doctor` honest and
40        // gives `--sandbox-strict` the correct refusal behaviour. The
41        // fork+exec helper + `landlock_create_ruleset(NULL, 0, ...)`
42        // probe land in 0.2.
43        false
44    }
45}
46
47#[derive(Debug, Clone, Copy)]
48pub struct SandboxPlan {
49    pub read_paths: usize,
50    pub write_paths: usize,
51    pub allow_network: bool,
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use std::path::PathBuf;
58
59    #[test]
60    fn plan_counts_paths() {
61        let sb = LinuxSandbox::default();
62        let p = SandboxProfile {
63            allow_read: vec![PathBuf::from("/etc")],
64            allow_write: vec![PathBuf::from("/tmp")],
65            allow_network: false,
66        };
67        let plan = sb.plan(&p);
68        assert_eq!(plan.read_paths, 1);
69        assert_eq!(plan.write_paths, 1);
70        assert!(!plan.allow_network);
71    }
72}