fix(interface): refuse container-runtime dirs and docker.sock mounts

check_mountable_dir already blocks system trees and .docker credential
dirs, but still admitted /var/run and directories that hold docker.sock.
Those bind-mount writable into the sandbox and expose the host
container runtime API. Expand the denylist and refuse runtime socket
children. Fixes #1058.
This commit is contained in:
Sasha Mitchell 2026-08-11 12:36:46 +07:00
parent 7cc9fa9faa
commit ea9a34120b
No known key found for this signature in database
2 changed files with 48 additions and 0 deletions

View file

@ -1328,6 +1328,12 @@ _FORBIDDEN_MOUNT_TREES = frozenset(
"/lib",
"/lib64",
"/nix/store",
"/run",
"/var/run",
"/private/var/run",
"/var/lib/docker",
"/var/lib/containers",
"/var/lib/containerd",
"/run/current-system/sw",
"/Applications",
"/Library",
@ -1339,6 +1345,16 @@ _FORBIDDEN_MOUNT_TREES = frozenset(
}
)
# Direct children that indicate a container-runtime API socket directory.
_FORBIDDEN_RUNTIME_SOCKET_NAMES = frozenset(
{
"docker.sock",
"podman.sock",
"containerd.sock",
"crio.sock",
}
)
# Refused themselves, but they hold projects too, so their contents are fine.
_FORBIDDEN_MOUNT_ROOTS = frozenset(
{
@ -1418,6 +1434,23 @@ def check_mountable_dir(path: Path) -> None:
"holds credentials, not code."
)
try:
socket_child = next(
(
entry.name
for entry in resolved.iterdir()
if entry.name.casefold() in _FORBIDDEN_RUNTIME_SOCKET_NAMES
),
None,
)
except OSError:
socket_child = None
if socket_child is not None:
raise ValueError(
f"Refusing to mount '{resolved}' into the sandbox: '{socket_child}' "
"exposes a container runtime API, not a codebase."
)
def dedupe_local_targets(targets_info: list[dict[str, Any]]) -> list[dict[str, Any]]:
result: list[dict[str, Any]] = []

View file

@ -166,6 +166,21 @@ def test_check_mountable_dir_rejects_system_subdirs() -> None:
check_mountable_dir(system_subdir)
def test_check_mountable_dir_rejects_var_run() -> None:
var_run = next((p for p in (Path("/var/run"), Path("/private/var/run")) if p.is_dir()), None)
if var_run is None:
pytest.skip("no /var/run on this platform")
with pytest.raises(ValueError, match="Refusing to mount"):
check_mountable_dir(var_run)
def test_check_mountable_dir_rejects_runtime_socket_dir(tmp_path: Path) -> None:
sock = tmp_path / "docker.sock"
sock.write_text("")
with pytest.raises(ValueError, match="container runtime API"):
check_mountable_dir(tmp_path)
def test_check_mountable_dir_accepts_a_project_under_the_home_root(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None: