Merge the native Linux device sandbox fix into Hermes qualification

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/hermes-native-runner:
  fix(hermes): mount usable Linux sandbox devices
This commit is contained in:
DottaandPaperclip committed 2026-10-08 23:06:31 -05:00
commit a9e033c4e7
4 files changed
+62 -2

No files matched your search

@@ -12,3 +12,17 @@ it("probes the macOS process policy before admitting a provider", async () => {
await verifyHermesCommandSandbox("darwin", execute);
expect(execute).toHaveBeenCalledWith("/usr/bin/sandbox-exec", expect.arrayContaining(["/usr/bin/true"]));
});
it("checks usable Linux character devices before admitting credentials", async () => {
const execute = vi.fn(async () => {});
await verifyHermesCommandSandbox("linux", execute);
expect(execute).toHaveBeenCalledWith("/usr/bin/bwrap", [
"--die-with-parent", "--unshare-pid", "--bind", "/", "/", "--proc", "/proc", "--dev", "/dev",
"--", "/bin/sh", "-c", "test -c /dev/null && : < /dev/null && : > /dev/null",
]);
});
it("rejects a Linux host when the device probe fails", async () => {
const execute = vi.fn(async () => { throw new Error("/dev/null: Permission denied"); });
await expect(verifyHermesCommandSandbox("linux", execute)).rejects.toMatchObject({ code: "HERMES_HOST_SANDBOX_UNAVAILABLE" });
});
@@ -41,7 +41,7 @@ export async function verifyHermesCommandSandbox(
if (platform === "darwin") {
await execute("/usr/bin/sandbox-exec", ["-p", "(version 1) (allow default) (deny process-info*) (allow process-info* (target self))", "/usr/bin/true"]);
} else if (platform === "linux") {
await execute("/usr/bin/bwrap", ["--die-with-parent", "--unshare-pid", "--bind", "/", "/", "--proc", "/proc", "--", "/bin/true"]);
await execute("/usr/bin/bwrap", ["--die-with-parent", "--unshare-pid", "--bind", "/", "/", "--proc", "/proc", "--dev", "/dev", "--", "/bin/sh", "-c", "test -c /dev/null && : < /dev/null && : > /dev/null"]);
} else {
throw new Error("Unsupported platform");
}
@@ -620,6 +620,49 @@ class Controls(unittest.IsolatedAsyncioTestCase):
self.assertEqual(second.message_id, text.message_id)
class ToolProcess(unittest.TestCase):
def test_linux_devices_keep_protected_and_assigned_overlays(self):
import tool_process
with patch.object(tool_process.sys, "platform", "linux"), patch.object(tool_process.shutil, "which", return_value="/usr/bin/bwrap"):
command = tool_process.sandbox_command(["/bin/sh", "-c", "true"], cwd="/workspace",
policy={"protectedPaths": ["/runtime"]}, assigned=["/assigned"])
self.assertEqual(command[:11], ["/usr/bin/bwrap", "--die-with-parent", "--unshare-pid", "--bind", "/", "/", "--proc", "/proc", "--dev", "/dev", "--tmpfs"])
self.assertEqual(command[11:], ["/runtime", "--ro-bind", "/assigned", "/assigned", "--chdir", "/workspace", "--", "/bin/sh", "-c", "true"])
def test_linux_device_redirection_supports_concurrent_atomic_writes(self):
import subprocess
import sys
import tempfile
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from tool_process import sandbox_command
if sys.platform != "linux":
self.skipTest("Requires the real Linux bubblewrap device mount")
with tempfile.TemporaryDirectory() as root:
root = Path(root)
workspace, protected, assigned = [root / name for name in ("workspace", "protected", "assigned")]
for directory in (workspace, protected, assigned):
directory.mkdir()
(protected / "secret").write_text("MUST_NOT_READ")
(assigned / "SKILL.md").write_text("Assigned instructions")
script = ('set -eu; test -c /dev/null; : < /dev/null; : > /dev/null; '
'test ! -e "$3/secret"; test -r "$4/SKILL.md"; '
'if printf forbidden > "$4/changed" 2>/dev/null; then exit 23; fi; '
'tmp="$(mktemp "$1/.device-write.XXXXXX" 2>/dev/null)"; '
'printf "%s" "$2" > "$tmp"; mv "$tmp" "$1/$2"')
def write(index):
name = f"write-{index}"
command = sandbox_command(["/bin/sh", "-c", script, "fixture", str(workspace), name, str(protected), str(assigned)],
cwd=str(workspace), policy={"protectedPaths": [str(protected)]}, assigned=[str(assigned)])
subprocess.run(command, check=True, capture_output=True, timeout=10)
self.assertEqual((workspace / name).read_text(), name)
with ThreadPoolExecutor(max_workers=4) as pool:
list(pool.map(write, range(16)))
self.assertEqual(len(list(workspace.iterdir())), 16)
self.assertFalse((assigned / "changed").exists())
self.assertEqual((protected / "secret").read_text(), "MUST_NOT_READ")
class NoAuth(unittest.TestCase):
def test_no_auth_omits_sdk_headers_and_metadata_probe_credentials(self):
import openai
@@ -33,7 +33,10 @@ def sandbox_command(command, *, cwd, policy, assigned):
bwrap = shutil.which("bwrap")
if not bwrap:
raise PermissionError("Hermes command execution requires bubblewrap in the runner image")
args = [bwrap, "--die-with-parent", "--unshare-pid", "--bind", "/", "/", "--proc", "/proc"]
# Ordinary bind mounts disable device access. Provide bubblewrap's
# minimal /dev so native shell redirects and mktemp work; apply the
# protected and assigned overlays afterwards so they remain binding.
args = [bwrap, "--die-with-parent", "--unshare-pid", "--bind", "/", "/", "--proc", "/proc", "--dev", "/dev"]
for path in protected:
args += ["--tmpfs", path]
for path in readonly: