Files
handbook/docker/packages/core/tests/test_setup.py
T
2026-06-24 14:16:58 -07:00

191 lines
8.2 KiB
Python

"""Tests for core.setup — copies uploaded context files into the workdir."""
import os
import shutil
import tempfile
import unittest
from pathlib import Path
from unittest import mock
from core import setup as setup_mod # aliased to avoid clash with pytest's xunit setup_module fixture
from core.tools import sandbox
class SetupTests(unittest.TestCase):
def setUp(self):
self.temp_dir = Path(tempfile.mkdtemp())
self.world_root = self.temp_dir / "world"
self.workdir = self.temp_dir / "workdir"
self.world_root.mkdir()
self._original_workdir = sandbox.WORKDIR
sandbox.WORKDIR = str(self.workdir)
def tearDown(self):
sandbox.WORKDIR = self._original_workdir
shutil.rmtree(self.temp_dir, ignore_errors=True)
def _run(self, task_id: str | None = None) -> int:
"""Invoke setup.main() in-process and return its exit code (0 on normal return)."""
env = {"WORLDBENCH_ROOT": str(self.world_root)}
if task_id is not None:
env["WORLDBENCH_TASK_ID"] = task_id
with mock.patch.dict(os.environ, env, clear=False):
os.environ.pop("BUNDLEDIR", None)
if task_id is None:
os.environ.pop("WORLDBENCH_TASK_ID", None)
try:
setup_mod.main()
return 0
except SystemExit as e:
return int(e.code or 0)
def test_copies_task_specific_files_into_workdir(self):
"""Task-specific setup_data/{task_id}/files/ is copied into WORKDIR."""
task_id = "task-abc"
files_dir = self.world_root / "tasks" / "setup_data" / task_id / "files"
files_dir.mkdir(parents=True)
(files_dir / "james.txt").write_text("james is the CEO")
(files_dir / "nested").mkdir()
(files_dir / "nested" / "notes.md").write_text("# notes")
assert self._run(task_id=task_id) == 0
assert (self.workdir / "james.txt").read_text() == "james is the CEO"
assert (self.workdir / "nested" / "notes.md").read_text() == "# notes"
def test_falls_back_to_generic_setup_data_when_task_dir_missing(self):
"""If no task-specific dir exists, copies from {world_root}/setup_data/files/."""
generic_files = self.world_root / "setup_data" / "files"
generic_files.mkdir(parents=True)
(generic_files / "shared.txt").write_text("shared context")
assert self._run(task_id="unknown-task") == 0
assert (self.workdir / "shared.txt").read_text() == "shared context"
def test_uses_generic_when_no_task_id(self):
"""With no WORLDBENCH_TASK_ID set, generic setup_data/files/ is used."""
generic_files = self.world_root / "setup_data" / "files"
generic_files.mkdir(parents=True)
(generic_files / "default.txt").write_text("default")
assert self._run(task_id=None) == 0
assert (self.workdir / "default.txt").read_text() == "default"
def test_merges_into_existing_workdir(self):
"""Existing workdir contents are preserved; new files are added alongside."""
self.workdir.mkdir(parents=True)
(self.workdir / "preexisting.txt").write_text("keep me")
generic_files = self.world_root / "setup_data" / "files"
generic_files.mkdir(parents=True)
(generic_files / "new.txt").write_text("new file")
assert self._run(task_id=None) == 0
assert (self.workdir / "preexisting.txt").read_text() == "keep me"
assert (self.workdir / "new.txt").read_text() == "new file"
def test_no_setup_data_exits_cleanly(self):
"""If neither task-specific nor generic setup_data exists, exit 0 without error."""
assert self._run(task_id="whatever") == 0
def test_setup_data_without_files_subdir_is_noop(self):
"""setup_data/ with no files/ subdirectory is a no-op (does not error).
Workdir IS created (ensure_workdir runs unconditionally so subsequent
tools have something to chdir into), but no files are copied.
"""
(self.world_root / "setup_data").mkdir()
assert self._run(task_id=None) == 0
def test_rejects_symlink_in_setup_files(self):
"""Symlinks in the source tree are refused — they'd let a bundle
materialize an arbitrary host path (e.g. /app/tasks) into /workdir
and then chown it to the model user."""
files_dir = self.world_root / "setup_data" / "files"
files_dir.mkdir(parents=True)
(files_dir / "real.txt").write_text("ok")
# Target doesn't need to exist for the check to fire.
os.symlink("/app/tasks", str(files_dir / "rubrics"))
assert self._run(task_id=None) == 1
assert not (self.workdir / "rubrics").exists()
assert not (self.workdir / "real.txt").exists() # copytree never ran
def test_rejects_symlinked_files_dir_root(self):
"""A symlinked files/ root is rejected too."""
real_dir = self.world_root / "setup_data" / "real_files"
real_dir.mkdir(parents=True)
(real_dir / "x.txt").write_text("x")
os.symlink(str(real_dir), str(self.world_root / "setup_data" / "files"))
assert self._run(task_id=None) == 1
assert not (self.workdir / "x.txt").exists()
def test_rejects_hardlink_in_setup_files(self):
"""Hardlinked files in the source tree are refused — they could
point at a locked-down inode (e.g. /app/packages/grading/*) and
slip past the symlink guard."""
files_dir = self.world_root / "setup_data" / "files"
files_dir.mkdir(parents=True)
# Two paths sharing one inode: the "target" simulates a protected
# file outside the bundle; the entry inside files/ is its hardlink.
protected = self.temp_dir / "fake-protected.py"
protected.write_text("rubric body")
os.link(str(protected), str(files_dir / "rubric.py"))
assert self._run(task_id=None) == 1
assert not (self.workdir / "rubric.py").exists()
def test_rejects_preexisting_workdir_symlink(self):
"""A symlink planted in /workdir before setup runs is refused —
otherwise copytree(dirs_exist_ok=True) would write through it as
root, into paths the model can't normally write."""
files_dir = self.world_root / "setup_data" / "files"
files_dir.mkdir(parents=True)
(files_dir / "context").mkdir()
(files_dir / "context" / "doc.txt").write_text("payload")
# Plant a model-controlled symlink in /workdir before setup.
self.workdir.mkdir(parents=True)
protected_target = self.temp_dir / "fake-protected-dir"
protected_target.mkdir()
os.symlink(str(protected_target), str(self.workdir / "context"))
assert self._run(task_id=None) == 1
# The bundle file must not have been written through the symlink.
assert not (protected_target / "doc.txt").exists()
def test_chowns_workdir_to_target_uid_gid_when_root(self):
"""Under (mocked) root, copied files are chowned back to SETUID:SETGID
so the unprivileged model user can modify them."""
generic_files = self.world_root / "setup_data" / "files"
generic_files.mkdir(parents=True)
(generic_files / "a.txt").write_text("hello")
(generic_files / "sub").mkdir()
(generic_files / "sub" / "b.txt").write_text("world")
env = {"WORLDBENCH_ROOT": str(self.world_root), "SETUID": "1000", "SETGID": "1000"}
with (
mock.patch.dict(os.environ, env, clear=False),
mock.patch("core.privilege.os.geteuid", return_value=0),
mock.patch("core.privilege.os.chown") as chown,
):
os.environ.pop("BUNDLEDIR", None)
os.environ.pop("WORLDBENCH_TASK_ID", None)
setup_mod.main()
chowned = sorted(call.args[0] for call in chown.call_args_list)
# ensure_workdir chowns WORKDIR; chown_tree_to_target chowns it again plus
# every entry under it. We only assert the post-copy entries are present.
assert str(self.workdir / "a.txt") in chowned
assert str(self.workdir / "sub") in chowned
assert str(self.workdir / "sub" / "b.txt") in chowned
for call in chown.call_args_list:
assert call.args[1:] == (1000, 1000)
if __name__ == "__main__":
unittest.main()