Skip to content

Keep a resource jail's environment off every command line - #155

Open
M3gA-Mind wants to merge 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/sr024-native-jail-env-out-of-argv
Open

M3gA-Mind wants to merge 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/sr024-native-jail-env-out-of-argv

Conversation

@M3gA-Mind

Copy link
Copy Markdown
Collaborator

Summary

The 2.1 native resource jail (StartJailedExecWithResources) passed the jailed command's environment to bubblewrap as --setenv KEY VALUE arguments. /proc/<pid>/cmdline is readable by every process of the same user, so a token in that environment was readable by every such process for the life of the launcher, systemd-run and bwrap processes.

The --setenv triples now go, NUL-separated, into a private file: mode 0600, in a fresh 0700 directory under $XDG_RUNTIME_DIR (else the temp directory), never inside one of the jail's grants. The launcher opens that file as descriptor 9 and deletes it before anything else runs. bwrap --args 9 then reads the triples. No value appears on any command line or in any host-side process environment. The file is also removed when the launch handle drops, in case the launcher never ran.

Setting the variables on the launcher's own environment was rejected: that would expose them to the host-side programs on the way, and a value such as LD_PRELOAD would run code there.

Related issue

None filed. Found reading the 2.1 launcher. The legacy bwrap backend already avoids this with its own --args file (tinybox-jail/src/bwrap/launcher.rs).

API or behavior changes

  • tinybox_linux::native_jail_argv and native_jail_argv_with_programs return NativeJailLaunch { argv, .. } instead of Vec<String>. The handle owns the environment file and must outlive the spawn. Nothing outside this repo calls them.
  • The module's per-execution resource backend keeps that handle until the child is spawned.
  • No bus contract change.

Validation

On our Linux CI runner (bubblewrap 0.9.0, user systemd session running), at b4910d6:

  • cargo fmt --all -- --check: clean.
  • cargo clippy --all-targets --all-features -- -D warnings: 0 errors, same as main.
  • cargo build --all-targets --all-features and RUSTDOCFLAGS="-D warnings" cargo doc: ok.
  • cargo test -p tinybox-linux -p tinybox-module: no failures, the same as main. The live test a_resource_jail_keeps_the_target_environment_off_every_command_line ran, not skipped.
  • Red controls, each restored afterwards:
    • putting --setenv back in the argv: both the argv test and the live test fail ("the launcher's own argv carries the value");
    • a scan that never matches: the scan test fails.

Tests

  • env_file_tests.rs:
    • the file holds the NUL-separated triples;
    • the file is 0600 and its directory 0700, and both are removed on drop;
    • a NUL in a key or value is refused;
    • a directory inside a grant is never used.
  • The native_jail_command_… test asserts that no target value appears anywhere in the argv.
  • a_resource_jail_keeps_the_target_environment_off_every_command_line (live, Linux with a user systemd session and bubblewrap): it runs a jailed command with a marker value in its environment, and checks that the command sees the value and that no /proc/*/cmdline holds it.
  • the_command_line_scan_finds_a_running_process_argument (Linux): the /proc/*/cmdline scan the live test relies on does find a marker on a running process's command line, so an empty result means no leak, not a broken scan.
  • Red controls: putting --setenv back in the argv fails both tests, and a scan that never matches fails the scan test.

Documentation

Module docs in env_file.rs and rustdoc on NativeJailLaunch.

Checklist

  • The change is focused on one logical change
  • No new #[allow(...)], #[ignore], or relaxed lints
  • No secrets, tokens, or .env contents in the diff or the description

The 2.1 native jail (StartJailedExecWithResources) launched
bash -> systemd-run --scope -> bwrap with the target's environment as
`--setenv KEY VALUE` arguments. /proc/<pid>/cmdline is readable by every
process of the same user, so a token a host passed in the environment
was visible on the launcher's, systemd-run's and bwrap's command lines
for as long as the command ran. Setting the variables on the launcher's
environment instead would expose them to those host-side programs and
let a value such as LD_PRELOAD or BASH_ENV run code there.

The --setenv triples are now written NUL-separated to a private file
(0600 in a fresh 0700 directory under $XDG_RUNTIME_DIR or the temp dir,
never under a grant). The launcher shim opens it as descriptor 9 and
deletes it before closing every other inherited descriptor, and bwrap
reads it with `--args 9`. native_jail_argv(_with_programs) now returns a
NativeJailLaunch that owns the file and removes it on drop, in case the
launcher never runs; the module holds it for the execution.

Not in this change: the namespace box backend's exec also passes
--setenv on bwrap's command line, and its host may be remote (SSH),
which needs its own transport.
@coderabbitai

coderabbitai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6555e44a-55ee-44e7-9003-1b53f91a4bc8
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@tinysweeper

tinysweeper Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Reviewed head: b23e4ec813aab68fcb842428039058367c2e0e5b

Verdict: Ready for maintainer review · Priority: none
Coverage: Reviewed 8 of 8 files.
Pull request purpose (title): Keep a resource jail…

Diagnostics and supporting details

Full summary

Pull request purpose (title): Keep a resource jail's environment off every command line. Tiny Sweeper completed its review; deterministic results follow.

Review snapshot

Change surface Files Review signal Count
Production 5 Active findings 0
Tests 3 Noted findings 0
Documentation 0 Resolved findings 0
Configuration 0 Pending checks/questions 0

Completeness: Complete
Test assessment: No supported feature-to-test mapping was available; this does not mean tests are absent or passed.

What changed

Environment values for the resource jail are moved off the command line: `env_file::EnvArgsFile::create` serializes the map into `--setenv`/key/value byte runs separated by NULs into a 0600 file created with `create_new` inside a fresh 0700 directory, drawing the base directory from `$XDG_RUNTIME_DIR` else the temp directory, canonicalized and rejected if it falls under the jail's root or any read-only/read-write grant, with random hex naming from `/dev/urandom` and cleanup on `Drop`. `native_bwrap_argv` now emits `--args 9` instead of per-variable `--setenv` triples, and the launcher shim script takes the env-file path as its first argument, opens it on descriptor 9, unlinks it, removes the parent directory, and preserves descriptor 9 while closing every other non-stdio descriptor. `native_jail_argv` and `native_jail_argv_with_programs` return a new `NativeJailLaunch` pairing the argv with the owned file handle, re-exported from the crate root and sandbox module. In the module, `NativeResourceBackend` gains a `launch` slot and a `keep` method so the handle is held until the execution's backend drops, and `configure_resource_launcher` binds and clones the new argv rather than assigning a `Vec<String>`. Latest-push delta: unavailable. The review state carries the prior reviewed head `7f2b0f1f2d0bd9f1133f6161fd50ee3a0fb786d2` and the current head `b23e4ec813aab68fcb842428039058367c2e0e5b`, but no trusted base-to-head diff comparison, so the whole-pull-request diff is not presented as a push delta.

Features

  • Added — Private NUL-separated environment file for the jail: Target environment values no longer appear in any `--setenv` argv; they live in a 0600 file in a 0700 directory chosen to lie outside the jail's grants, so the jailed program cannot read the file it is fed. (crates/tinybox-linux/src/sandbox/env_file.rs, crates/tinybox-linux/src/sandbox/args.rs pub fn native_jail_argv_with_programs()
  • Modified — bubblewrap reads the environment from descriptor 9: `bwrap --args 9` replaces per-variable `--setenv` arguments, so a token in the target environment is no longer visible in `/proc/<pid>/cmdline` of the bwrap process. (crates/tinybox-linux/src/sandbox/args.rs fn native_bwrap_argv()
  • Modified — Launcher shim opens, unlinks and preserves descriptor 9: The environment file is opened and removed before the scope command runs, and the launcher keeps descriptor 9 open while closing every other non-stdio descriptor, so the value reaches only the intended target. (crates/tinybox-linux/src/sandbox/args.rs fn native_bwrap_argv(, crates/tinybox-linux/src/sandbox/env_file.rs)
  • Added — `NativeJailLaunch` launch handle: The public API returns a handle owning the environment file instead of a bare argv, so callers must keep it alive across the spawn; the handle removes the file on drop if the launcher never ran. (crates/tinybox-linux/src/sandbox/args.rs pub fn native_jail_argv_with_programs(, crates/tinybox-linux/src/lib.rs, crates/tinybox-linux/src/sandbox/mod.rs)
  • Modified — Resource backend retains the launch for the execution: The module backend stores the launch handle until the execution's backend is dropped, keeping the file alive long enough for the launcher to open it while still cleaning it up on a launch that never happens. (crates/tinybox-module/src/tinybus_module/native_jail.rs fn configure_resource_launcher(, crates/tinybox-module/src/tinybus_module/native_jail.rs fn unavailable() -> Error {)

Tests

No supported feature-to-test mapping was produced. Test execution is not inferred.

Findings

No active actionable findings.

Before merge

None.

How this fits together

flowchart LR
  n0["NativeJailCommand<br/>changed"]:::changed
  n1["native_bwrap_argv<br/>changed"]:::changed
  n2["native_jail_argv_with_programs<br/>changed"]:::changed
  n3["..._target_environment_after_namespace_setup<br/>changed"]:::changed
  n4["configure_resource_launcher<br/>changed"]:::changed
  n5["unavailable<br/>changed"]:::changed
  n6["prepare_with_resources"]:::impacted
  n7["tempdir"]:::impacted
  n8["...d_descendants_and_preserves_stream_events"]:::impacted
  n9["resource_backend"]:::impacted
  n10["...cpu_limit_throttles_the_live_native_scope"]:::impacted
  n1 -->|uses| n0
  n2 -->|uses| n0
  n2 -->|calls| n1
  n3 -->|uses| n0
  n3 -->|calls| n2
  n3 -->|tests| n2
  n3 -->|calls| n7
  n3 -->|tests| n7
  n4 -->|uses| n0
  n4 -->|calls| n2
  n4 -->|calls| n5
  n6 -->|uses| n0
  n6 -->|calls| n4
  n6 -->|calls| n5
  n8 -->|calls| n6
  n8 -->|tests| n6
  n8 -->|calls| n7
  n8 -->|tests| n7
  n8 -->|calls| n9
  n8 -->|tests| n9
  n10 -->|calls| n6
  n10 -->|tests| n6
  n10 -->|calls| n7
  n10 -->|tests| n7
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 2 files; 0 findings. _The code index is behind this pull request (indexed at `381104b39ae1`), so retrieved context may be out of date._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 2 files; 0 findings. _The code index is behind this pull request (indexed at `381104b39ae1`), so retrieved context may be out of date._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: Tests lane answered with no reportable defects, describing the moved environment as covered by the file-contents, permissions, cleanup and refusal tests, the argv assertions, and the live `/proc` scan with its self-check; the only gap it named is the untested `backend.keep` rejection branch.
  • Lane summary: The change moves the native jail's target environment out of argv into a private 0600 file read by the launcher on descriptor 9, and is covered by unit tests for the file's contents, permissions, cleanup and refusal paths, a launcher-argv test asserting no `--setenv`, `BASH_ENV` or value appears and that `--args 9` points at the file, and a live end-to-end test that scans every `/proc/<pid>/cmdline` while the jail runs (with a self-check that the scan does find a value). The paths I could verify are sound; I could not exercise the new module test `backend.keep` rejection branch, which has no test. _The code index is behind this pull request (indexed at `381104b39ae1`), so retrieved context may be out of date._

commits

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: Commits lane answered and reported no sensitive content in the committed files.
  • Lane summary: No sensitive content in committed files. _The code index is behind this pull request (indexed at `381104b39ae1`), so retrieved context may be out of date._

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: Description lane answered and found the body consistent with the diff across the file-based environment, the `NativeJailLaunch` return type, the backend handle retention, the new `env_file` module, and the listed tests, and found no defect in the code.
  • Positive: Description lane noted that the Validation and Tests sections cite live tests and red controls that are not part of this diff, so a reader cannot verify those claims from the request.
  • Lane summary: The change moves the jailed command's `--setenv` triples out of argv into a private 0600 file read via `bwrap --args 9`, with the launcher opening and unlinking the file and `NativeJailLaunch` owning it until the spawn; the description matches what the diff does, including the API change, the backend handle and the new tests. I found no defect in the code itself; the only gap is that the description's Validation and Tests sections cite live tests (`a_resource_jail_keeps_the_target_environment_off_every_command_line`, the red controls) that are not part of this diff, so a reader cannot verify those claims from the request. _The code index is behind this pull request (indexed at `381104b39ae1`), so retrieved context may be out of date._
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.004879
  • Tokens: 540016 input · 8578 output · 189952 cached · 767 embedding
  • Updated: 2026-11-12T07:30:20Z
Head State Pass summary
b4910d6993bb ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-11T11:51:30Z)
7f2b0f1f2d0b ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-11T14:19:09Z)
b23e4ec813aa ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-12T07:30:20Z)

tinysweeper 0.1.2

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

Diagnostics

Total spend: $0.0576

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Nov 11, 2026
M3gA-Mind and others added 2 commits November 11, 2026 19:43

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

Diagnostics

Total spend: $0.0049

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant