Skip to repository content124 lines · 4.0 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:45:28.388Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
sandbox_tests.rs
1#![allow(clippy::disallowed_methods, reason = "tooling is exempt")]
2
3use std::process::Command;
4
5use anyhow::{Context as _, Result, bail};
6use clap::Parser;
7
8/// Runs the Linux Bubblewrap sandboxing NixOS VM tests (see `nix/tests/sandboxing`).
9///
10/// Each test boots a real kernel under QEMU to exercise bwrap behavior across
11/// host configurations: a working host (the sandbox must be enforced and the
12/// full fs x network policy matrix must hold), and degraded hosts (no bwrap,
13/// setuid-only bwrap, user namespaces disabled) where `Sandbox::can_create`
14/// must report the specific failure and the consumer must fail closed. They
15/// therefore only run on Linux with `/dev/kvm` available, and require `nix`
16/// with flakes enabled.
17#[derive(Parser)]
18pub struct SandboxTestsArgs {
19 /// Names of specific tests to run (e.g. `sandbox-working`). Defaults
20 /// to all.
21 tests: Vec<String>,
22
23 /// Nix system to build for. Defaults to the host system.
24 #[arg(long)]
25 system: Option<String>,
26
27 /// Force already-built tests to re-run instead of returning the cached
28 /// result (passes `--rebuild` to `nix build`).
29 ///
30 /// This only works once a test has been built at least once; after changing
31 /// the test code the derivation changes, so run without `--rebuild` first.
32 #[arg(long)]
33 rebuild: bool,
34
35 /// List the available tests and exit without running anything.
36 #[arg(long)]
37 list: bool,
38}
39
40/// Tests in `nix/tests/sandboxing` are exposed as flake checks with this prefix.
41const TEST_PREFIX: &str = "sandbox-";
42
43pub fn run_sandbox_tests(args: SandboxTestsArgs) -> Result<()> {
44 let system = args.system.unwrap_or_else(host_system);
45 let available = available_tests(&system)?;
46
47 if args.list {
48 for name in &available {
49 println!("{name}");
50 }
51 return Ok(());
52 }
53
54 let selected = if args.tests.is_empty() {
55 available
56 } else {
57 for name in &args.tests {
58 if !available.contains(name) {
59 bail!(
60 "unknown sandbox test {name:?}; available: {}",
61 available.join(", ")
62 );
63 }
64 }
65 args.tests
66 };
67
68 if selected.is_empty() {
69 bail!("no sandbox tests found for system `{system}`");
70 }
71
72 let mut command = Command::new("nix");
73 command.args(["build", "-L", "--keep-going"]);
74 if args.rebuild {
75 command.arg("--rebuild");
76 }
77 for name in &selected {
78 command.arg(format!(".#checks.{system}.{name}"));
79 }
80
81 eprintln!("Running sandbox tests: {}", selected.join(", "));
82 let status = command.status().context("failed to run `nix build`")?;
83 if !status.success() {
84 bail!("sandbox tests failed");
85 }
86 Ok(())
87}
88
89/// The Nix system double for the host. These tests are Linux-only, so we always
90/// target a `*-linux` system (overridable via `--system`).
91fn host_system() -> String {
92 format!("{}-linux", std::env::consts::ARCH)
93}
94
95/// Query the flake for the names of the sandbox-test checks, so the task stays
96/// in sync with `nix/tests/sandboxing` without a hardcoded list.
97fn available_tests(system: &str) -> Result<Vec<String>> {
98 let output = Command::new("nix")
99 .args([
100 "eval",
101 &format!(".#checks.{system}"),
102 "--apply",
103 "checks: builtins.concatStringsSep \"\\n\" (builtins.attrNames checks)",
104 "--raw",
105 ])
106 .output()
107 .context("failed to run `nix eval` to list checks (is `nix` installed?)")?;
108 if !output.status.success() {
109 bail!(
110 "`nix eval` failed: {}",
111 String::from_utf8_lossy(&output.stderr).trim()
112 );
113 }
114
115 let names = String::from_utf8(output.stdout).context("`nix eval` output was not UTF-8")?;
116 let mut tests: Vec<String> = names
117 .lines()
118 .filter(|name| name.starts_with(TEST_PREFIX))
119 .map(str::to_string)
120 .collect();
121 tests.sort();
122 Ok(tests)
123}
124