From 075514764d3c8b4967cd770a6d3b9454d7286c73 Mon Sep 17 00:00:00 2001 From: Matt Wozniski Date: Wed, 21 Oct 2026 15:27:03 -0700 Subject: [PATCH] Install busybox in snekbox Make all of busybox's applets available in snekbox, mounted at both /bin (for `/bin/sh`) and at /usr/bin (for `/usr/bin/env`). --- Dockerfile | 4 +++- config/snekbox.cfg | 14 ++++++++++++++ tests/test_nsjail.py | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0b6c976d..4291f387 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,17 +17,19 @@ RUN git clone -b 3.6 --single-branch --depth 1 https://github.com/google/nsjail. FROM python:3.14-slim-bookworm AS base -RUN mkdir -p /snekbin/python/ +RUN mkdir -p /snekbin/python/ /busybox/ ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \ PIP_NO_CACHE_DIR=false RUN apt-get -y update \ && apt-get install -y --no-install-recommends \ + busybox \ gcc \ git \ libnl-route-3-200 \ libprotobuf32 \ + && busybox --install /busybox/ \ && rm -rf /var/lib/apt/lists/* COPY --link --from=builder-nsjail /nsjail/nsjail /usr/sbin/ diff --git a/config/snekbox.cfg b/config/snekbox.cfg index c4187781..43037c65 100644 --- a/config/snekbox.cfg +++ b/config/snekbox.cfg @@ -89,6 +89,20 @@ mount { rw: false } +mount { + src: "/busybox" + dst: "/bin" + is_bind: true + rw: false +} + +mount { + src: "/busybox" + dst: "/usr/bin" + is_bind: true + rw: false +} + mount { src: "/usr/local/bin/python" dst: "/usr/local/bin/python" diff --git a/tests/test_nsjail.py b/tests/test_nsjail.py index 857e42c8..3594aa7b 100644 --- a/tests/test_nsjail.py +++ b/tests/test_nsjail.py @@ -72,6 +72,27 @@ def test_multi_files(self): self.assertEqual(result.stdout, "hello\n") self.assertEqual(result.stderr, None) + def test_busybox_shell_pipeline(self): + code = dedent(r""" + import subprocess + + print( + subprocess.run( + r'''printf 'Hello world!\nThis is a test.\n' | /usr/bin/env wc''', + shell=True, + check=True, + capture_output=True, + text=True, + ).stdout + ) + """).strip() + + result = self.eval_file(code) + self.assertEqual(result.returncode, 0) + # Count of lines, words, and characters, separated by whitespace. + self.assertEqual(result.stdout.split(), ["2", "6", "29"]) + self.assertEqual(result.stderr, None) + def test_subprocess_resource_unavailable(self): max_pids = self.nsjail.config.cgroup_pids_max code = dedent(f"""