Allow runner to execute without a sandbox - #720
Conversation
🤖 Claude Code ReviewPR: #720 Now I have a thorough understanding of the PR. Here's my review: PR Review: Add OS and Sandbox fields to spec, enable non-sandboxed runner executionOverviewThis PR adds StrengthsSecurity design is solid:
Code quality is high and follows project conventions:
Types and schema well-implemented:
Issues1. Polling loop in The timeout/cancel polling loop sleeps 100ms per iteration. This is functional but means up to 100ms latency on process completion. Consider using 2. hint: format!("{e}"),Should be 3. let msg = panic
.downcast_ref::<&str>()
.copied()
.or_else(|| panic.downcast_ref::<String>().map(String::as_str))
.unwrap_or("unknown panic");CLAUDE.md says "Do NOT use 4. Migration hardcodes All existing specs get 5. Warnings are printed to stderr but callers have no way to distinguish "no output files requested" from "all output files failed to read." This is a minor observability gap — the current 6. The Minor Nits
VerdictApprove with minor fixes. The security model is well-thought-out, the code follows project conventions closely, and the type system is used effectively to prevent misuse. The issues identified are minor and don't block merging. The most important ones to address are #2 (clippy lint) and adding comments for #3 and #4. Model: claude-opus-4-6 |
|
| Branch | u/ep/local-runner |
| Testbed | ubuntu-22.04 |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result microseconds (µs) (Result Δ%) | Upper Boundary microseconds (µs) (Limit %) |
|---|---|---|---|
| Adapter::Json | 📈 view plot 🚷 view threshold | 3.92 µs(+10.49%)Baseline: 3.55 µs | 4.68 µs (83.73%) |
| Adapter::Magic (JSON) | 📈 view plot 🚷 view threshold | 3.90 µs(+10.73%)Baseline: 3.52 µs | 4.60 µs (84.73%) |
| Adapter::Magic (Rust) | 📈 view plot 🚷 view threshold | 24.96 µs(-2.15%)Baseline: 25.50 µs | 31.53 µs (79.15%) |
| Adapter::Rust | 📈 view plot 🚷 view threshold | 2.89 µs(+2.76%)Baseline: 2.81 µs | 3.26 µs (88.49%) |
| Adapter::RustBench | 📈 view plot 🚷 view threshold | 2.87 µs(+2.15%)Baseline: 2.81 µs | 3.26 µs (87.93%) |
| head_version_insert/batch/10 | 📈 view plot 🚷 view threshold | 104.83 µs(+4.11%)Baseline: 100.69 µs | 118.74 µs (88.29%) |
| head_version_insert/batch/100 | 📈 view plot 🚷 view threshold | 246.78 µs(+3.58%)Baseline: 238.25 µs | 265.55 µs (92.93%) |
| head_version_insert/batch/255 | 📈 view plot 🚷 view threshold | 469.83 µs(+1.41%)Baseline: 463.32 µs | 499.30 µs (94.10%) |
| head_version_insert/batch/50 | 📈 view plot 🚷 view threshold | 164.95 µs(+2.15%)Baseline: 161.47 µs | 183.04 µs (90.12%) |
| threshold_query/join/10 | 📈 view plot 🚷 view threshold | 149.98 µs(+3.18%)Baseline: 145.35 µs | 169.62 µs (88.42%) |
| threshold_query/join/20 | 📈 view plot 🚷 view threshold | 167.65 µs(+4.84%)Baseline: 159.92 µs | 186.54 µs (89.87%) |
| threshold_query/join/5 | 📈 view plot 🚷 view threshold | 144.48 µs(+5.10%)Baseline: 137.47 µs | 160.38 µs (90.09%) |
| threshold_query/join/50 | 📈 view plot 🚷 view threshold | 206.85 µs(+2.80%)Baseline: 201.22 µs | 231.48 µs (89.36%) |
…nner Add OperatingSystem and Sandbox validated types to bencher_valid, add both fields to the spec JSON types and database model, create a table recreation migration that backfills existing specs with os=linux/sandbox=firecracker, and replace compile-time three-way cfg dispatch in the runner with runtime dispatch based on config.sandbox. Non-sandboxed execution is now available on any platform in release builds. https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
Reject jobs with sandbox: None at runtime unless the operator explicitly passes --danger-allow-no-sandbox when starting the runner. This prevents accidentally running untrusted benchmark code directly on the host. https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
Address review feedback for the non-sandboxed runner: - Clear host environment before spawning benchmark (env_clear()) - Set working directory to temp dir for process isolation - Validate output file paths to prevent host filesystem traversal - Use dedicated Runtime error variant instead of OutOfRange for timeout and cancellation errors https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
… config CI fixes: - Regenerate OpenAPI spec with os/sandbox fields - Add os/sandbox to CLI spec create/update types - Pass --sandbox firecracker to existing runner test scenarios Non-sandboxed execution now unpacks OCI image layers to a temp directory and runs the command from the unpacked rootfs. No isolation is applied — this mode is for trusted workloads only. Extract duplicated OCI config resolution (entrypoint/cmd/env/working_dir) into shared resolve_oci_config() used by both local.rs and vm.rs. Add non-sandboxed test scenarios (nosandbox_basic, nosandbox_env, nosandbox_exit_code) and document sandbox modes in DESIGN.md. https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
Rename MacOs variant to Macos so snake_case rename produces "macos" matching the runtime TryFrom/Into impls. Previously the OpenAPI schema had "mac_os" while serde used "macos". https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
- Add env_clear() before spawning benchmark in non-sandboxed mode so host secrets don't leak into benchmark processes - Match explicitly on Sandbox::Firecracker in job sandbox check so future sandbox types are handled correctly https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
Update sandbox modes documentation to reflect actual behavior: host environment is cleared and only OCI-derived variables are set. https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
- Regenerate TypeScript types with OperatingSystem and Sandbox enums (fixes "Check Typescript Types for changes" CI failure) - Add nosandbox_scratch_image test scenario: FROM scratch with a statically-linked binary, matching the common production use case of shipping only the benchmark binary in the OCI image https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
The action bundles the OpenAPI spec; rebuild to include the new OperatingSystem and Sandbox types. https://claude.ai/code/session_01Dse1aG6RxsmUg6s2sGdYQy
- Fix OCI storage test failures: update hardcoded "oci" directory references to "registry" in blobs, manifests, uploads, and jobs tests - Fix timeout kill race condition: use PID-based libc::kill instead of trying to kill a child already consumed by wait_with_output - Fix program path traversal: canonicalize and validate program path stays within unpacked rootfs - Fix output file path traversal: resolve OCI file paths relative to unpack_dir and validate they don't escape the rootfs - Expose --os and --sandbox CLI flags in spec create and update commands - Add From<OperatingSystem> and From<Sandbox> impls for bencher_client types https://claude.ai/code/session_011ZiX8t1cpGMGQH93zrrVWf
…dir validation - Close PID reuse window: add `exited` AtomicBool flag set by wait thread before returning, checked before signaling to prevent killing recycled PIDs - Add PATH-based program resolution: bare command names (e.g. `echo`) are searched in PATH directories within the unpacked rootfs, matching container runtime behavior - Canonicalize unpack_dir after unpacking for stable containment checks - Validate and canonicalize working directory stays within rootfs - Gate `libc::kill` with `#[cfg(unix)]`, add Windows `taskkill` fallback - Fix nosandbox_exit_code test to match runner process exit behavior https://claude.ai/code/session_011ZiX8t1cpGMGQH93zrrVWf
runner to execute without a sandbox
The hard-coded MAX_SYMLINKS constant (40) in canonicalize_within_rootfs is now a configurable field on Config, threaded through all call sites in the local runner and exposed as --max-symlinks on both `runner run` and `runner up` CLI commands. The default (40) matches the Linux kernel's MAXSYMLINKS (since 3.18). The CLI arg conflicts with --sandbox since it only applies to non-sandboxed mode. https://claude.ai/code/session_01Y2aGgg46htY4AjeXpqGao9
This changeset adds the concept of an operating system (
os) andsandboxto thespec.This allows the bare metal
runnerto be used without Firecracker and non-Linux operating systems.