Skip to content

Commit

Permalink
make compatible with all architectures, and fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfv committed Nov 30, 2024
1 parent c0e5009 commit 379febd
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 82 deletions.
14 changes: 12 additions & 2 deletions crates/rattler_sandbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,17 @@ edition.workspace = true
readme.workspace = true

[dependencies]
birdcage = "0.8.1"
clap = { workspace = true, features = ["derive"] }
fs-err = "3.0.0"
fs-err = { workspace = true }
tokio = { workspace = true, optional = true, features = ["process"]}

[target.'cfg(any(target_os = "macos", all(target_os = "linux", target_arch = "x86_64"), all(target_os = "linux", target_arch = "aarch64")))'.dependencies]
birdcage = "0.8.1"

[dev-dependencies]
libtest-mimic = "0.8.1"

[[test]]
name = "integration_test"
path = "tests/integration_test.rs"
harness = false
110 changes: 30 additions & 80 deletions crates/rattler_sandbox/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,31 @@
use birdcage::process::Command;
use birdcage::{Birdcage, Sandbox};
use clap::Parser;

// A shim for the sandbox that is used on non-supported platforms
#[cfg(not(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "aarch64"),
)))]
mod sandbox_shim;
#[cfg(not(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "aarch64"),
)))]
pub use sandbox_shim::*;

/// The actual implementation of the sandbox that is used on supported platforms
#[cfg(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "aarch64"),
))]
pub mod sandbox;
#[cfg(feature = "tokio")]
pub mod tokio;

pub use sandbox::{sandboxed_command, Exception};

#[derive(clap::Parser)]
struct Opts {
#[clap(long)]
fs_exec_and_read: Option<Vec<String>>,

#[clap(long)]
fs_write_and_read: Option<Vec<String>>,

#[clap(long)]
fs_read: Option<Vec<String>>,

#[clap(long)]
network: bool,

#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
}

// This function checks if the current executable should execute as a sandboxed process
pub fn init() {
let mut args = std::env::args().collect::<Vec<String>>();
// Remove the first `__sandbox_trampoline__` argument
args.remove(1);
let opts = Opts::parse_from(args.iter());
// Allow access to our test executable.
let mut sandbox = Birdcage::new();

if let Some(fs_exec_and_read) = opts.fs_exec_and_read {
for path in fs_exec_and_read {
let _ = sandbox.add_exception(birdcage::Exception::ExecuteAndRead(path.into()));
}
}

if let Some(fs_read) = opts.fs_read {
for path in fs_read {
let _ = sandbox.add_exception(birdcage::Exception::Read(path.into()));
}
}

if let Some(fs_write_and_read) = opts.fs_write_and_read {
for path in fs_write_and_read {
let _ = sandbox.add_exception(birdcage::Exception::WriteAndRead(path.into()));
}
}
if let Some((exe, args)) = opts.args.split_first() {
// Initialize the sandbox; by default everything is prohibited.
let mut command = Command::new(exe);
command.args(args);

let mut child = sandbox.spawn(command).unwrap();

let status = child.wait().unwrap();
std::process::exit(status.code().unwrap());
} else {
panic!("No executable provided");
}
}

pub fn init_sandbox() {
// TODO ideally we check that we are single threaded, but birdcage will also check it later on

if std::env::args().any(|arg| arg == "__sandbox_trampoline__") {
// This is a sandboxed process
println!("Running in sandbox mode");
// Initialize the sandbox
init();
} else {
// This is the main process
println!("Running in main process mode");
}
}
#[cfg(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "aarch64"),
))]
pub use sandbox::*;
75 changes: 75 additions & 0 deletions crates/rattler_sandbox/src/sandbox/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use birdcage::process::Command;
use birdcage::{Birdcage, Sandbox};
use clap::Parser;

pub mod sandbox_impl;
#[cfg(feature = "tokio")]
pub mod tokio;

pub use sandbox_impl::{sandboxed_command, Exception};

#[derive(clap::Parser)]
struct Opts {
#[clap(long)]
fs_exec_and_read: Option<Vec<String>>,

#[clap(long)]
fs_write_and_read: Option<Vec<String>>,

#[clap(long)]
fs_read: Option<Vec<String>>,

#[clap(long)]
network: bool,

#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
}

// This function checks if the current executable should execute as a sandboxed process
fn init() {
let mut args = std::env::args().collect::<Vec<String>>();
// Remove the first `__sandbox_trampoline__` argument
args.remove(1);
let opts = Opts::parse_from(args.iter());
// Allow access to our test executable.
let mut sandbox = Birdcage::new();

if let Some(fs_exec_and_read) = opts.fs_exec_and_read {
for path in fs_exec_and_read {
let _ = sandbox.add_exception(birdcage::Exception::ExecuteAndRead(path.into()));
}
}

if let Some(fs_read) = opts.fs_read {
for path in fs_read {
let _ = sandbox.add_exception(birdcage::Exception::Read(path.into()));
}
}

if let Some(fs_write_and_read) = opts.fs_write_and_read {
for path in fs_write_and_read {
let _ = sandbox.add_exception(birdcage::Exception::WriteAndRead(path.into()));
}
}
if let Some((exe, args)) = opts.args.split_first() {
// Initialize the sandbox; by default everything is prohibited.
let mut command = Command::new(exe);
command.args(args);

let mut child = sandbox.spawn(command).unwrap();

let status = child.wait().unwrap();
std::process::exit(status.code().unwrap());
} else {
panic!("No executable provided");
}
}

pub fn init_sandbox() {
// TODO ideally we check that we are single threaded, but birdcage will also check it later on
if std::env::args().any(|arg| arg == "__sandbox_trampoline__") {
// This is a sandboxed process, so we initialize the sandbox
init();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,43 @@ pub fn sandboxed_command(exe: &str, exceptions: &[Exception]) -> Command {

cmd
}

#[cfg(test)]
mod tests {
use std::ffi::OsStr;

use super::*;

#[test]
fn test_sandboxed_command() {
let cmd = sandboxed_command(
"test",
&[
Exception::ExecuteAndRead("/bin".into()),
Exception::Read("/etc".into()),
Exception::ReadAndWrite("/tmp".into()),
Exception::Networking,
],
);

let args = cmd.get_args();

// args to string to compare
let args: Vec<&OsStr> = args.collect();

assert_eq!(
args,
vec![
"__sandbox_trampoline__",
"--fs-exec-and-read",
"/bin",
"--fs-read",
"/etc",
"--fs-write-and-read",
"/tmp",
"--network",
"test",
]
);
}
}
File renamed without changes.
3 changes: 3 additions & 0 deletions crates/rattler_sandbox/src/sandbox_shim.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn init_sandbox() {
panic!("Sandbox is not supported on this platform");
}
28 changes: 28 additions & 0 deletions crates/rattler_sandbox/tests/integration_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#[cfg(any(
target_os = "macos",
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
))]
mod tests_impl;

#[cfg(any(
target_os = "macos",
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
))]
fn main() {
rattler_sandbox::init_sandbox();
let args = libtest_mimic::Arguments::from_args();
let tests = tests_impl::tests();
libtest_mimic::run(&args, tests).exit();
}

#[cfg(not(any(
target_os = "macos",
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
)))]
fn main() {
eprintln!("This platform is not supported by the sandbox");
std::process::exit(0);
}
14 changes: 14 additions & 0 deletions crates/rattler_sandbox/tests/tests_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use libtest_mimic::{Failed, Trial};
use rattler_sandbox::sandboxed_command;

Check failure on line 2 in crates/rattler_sandbox/tests/tests_impl.rs

View workflow job for this annotation

GitHub Actions / Linux-arm

unresolved import `rattler_sandbox::sandboxed_command`

Check failure on line 2 in crates/rattler_sandbox/tests/tests_impl.rs

View workflow job for this annotation

GitHub Actions / Linux-powerpc64

unresolved import `rattler_sandbox::sandboxed_command`

Check failure on line 2 in crates/rattler_sandbox/tests/tests_impl.rs

View workflow job for this annotation

GitHub Actions / Linux-powerpc64le

unresolved import `rattler_sandbox::sandboxed_command`

Check failure on line 2 in crates/rattler_sandbox/tests/tests_impl.rs

View workflow job for this annotation

GitHub Actions / Linux-s390x

unresolved import `rattler_sandbox::sandboxed_command`

Check failure on line 2 in crates/rattler_sandbox/tests/tests_impl.rs

View workflow job for this annotation

GitHub Actions / Windows-x86_64

unresolved import `rattler_sandbox::sandboxed_command`

Check failure on line 2 in crates/rattler_sandbox/tests/tests_impl.rs

View workflow job for this annotation

GitHub Actions / Windows-aarch64

unresolved import `rattler_sandbox::sandboxed_command`

fn test_cannot_ls() -> Result<(), Failed> {
let mut cmd = sandboxed_command("ls", &[]);
cmd.arg("/");
let output = cmd.output().unwrap();
assert!(!output.status.success());
Ok(())
}

pub fn tests() -> Vec<Trial> {
vec![Trial::test("test_cannot_ls", test_cannot_ls)]
}

0 comments on commit 379febd

Please sign in to comment.