Spaces:
Running on Zero
Running on Zero
| """Sandbox tests: VFS, shell, puzzles, fabrication, mirror, and the | |
| no-real-filesystem tripwire.""" | |
| from pathlib import Path | |
| import pytest | |
| from scrypt.sandbox.fabricate import fabricate_home | |
| from scrypt.sandbox.mirror import mirror_home | |
| from scrypt.sandbox.puzzles import plant_all | |
| from scrypt.sandbox.shell import Shell | |
| from scrypt.sandbox.vfs import VFS, VfsError | |
| def make_shell(seed: int = 1) -> Shell: | |
| vfs = VFS() | |
| fabricate_home(vfs, seed=seed) | |
| return Shell(vfs) | |
| # ---------------------------------------------------------------------- vfs | |
| def test_vfs_write_read_roundtrip(): | |
| vfs = VFS() | |
| vfs.write("/home/drifter/a/b.txt", "hello") | |
| assert vfs.read("/home/drifter/a/b.txt") == "hello" | |
| assert vfs.read("~/a/b.txt") == "hello" | |
| def test_vfs_relative_paths_and_dotdot(): | |
| vfs = VFS() | |
| vfs.write("~/x/deep/file.txt", "v") | |
| vfs.chdir("~/x/deep") | |
| assert vfs.read("file.txt") == "v" | |
| assert vfs.read("../deep/file.txt") == "v" | |
| vfs.chdir("..") | |
| assert vfs.cwd_path == "/home/drifter/x" | |
| def test_vfs_remove_counts_files(): | |
| vfs = VFS() | |
| for i in range(5): | |
| vfs.write(f"~/photos/{i}.jpg", "x") | |
| assert vfs.remove("~/photos", recursive=True) == 5 | |
| assert vfs.resolve("~/photos") is None | |
| with pytest.raises(VfsError): | |
| vfs.remove("~/photos") | |
| # -------------------------------------------------------------------- shell | |
| def test_shell_ls_hides_dotfiles_without_dash_a(): | |
| sh = make_shell() | |
| plain = sh.run("ls").out | |
| assert ".bash_history" not in plain | |
| assert ".bash_history" in sh.run("ls -a").out | |
| def test_shell_pipe_cat_grep(): | |
| sh = make_shell() | |
| out = sh.run("cat documents/todo.txt | grep insurance").out | |
| assert "insurance" in out and "gym" not in out | |
| def test_shell_grep_file_directly(): | |
| sh = make_shell() | |
| assert "insurance" in sh.run("grep insurance documents/todo.txt").out | |
| def test_shell_glob_expansion(): | |
| sh = make_shell() | |
| sh.vfs.chdir("~/photos/vacation") | |
| out = sh.run("ls *.jpg").out | |
| assert "IMG_" in out | |
| def test_shell_find_by_name(): | |
| sh = make_shell() | |
| out = sh.run("find / -name todo.txt").out | |
| assert "/home/drifter/documents/todo.txt" in out | |
| def test_shell_rm_reports_deletions(): | |
| sh = make_shell() | |
| sh.run("rm -r photos") | |
| assert sh.last_deletions >= 5 | |
| def test_shell_revocation(): | |
| sh = make_shell() | |
| sh.run("grep x documents/todo.txt") | |
| sh.revoke("grep", "no more needles.") | |
| result = sh.run("grep x documents/todo.txt") | |
| assert "command not found" in result.err and "needles" in result.err | |
| assert "grep" not in sh.available() | |
| def test_most_used_tracks_and_excludes_revoked(): | |
| sh = make_shell() | |
| for _ in range(3): | |
| sh.run("ls") | |
| sh.run("pwd") | |
| assert sh.most_used() == "ls" | |
| sh.revoke("ls") | |
| assert sh.most_used() == "pwd" | |
| def test_unknown_command(): | |
| sh = make_shell() | |
| assert "command not found" in sh.run("vim todo.txt").err | |
| # ------------------------------------------------------------------ puzzles | |
| def test_zip_puzzle_solvable_via_history(): | |
| sh = make_shell(seed=3) | |
| puzzles = {p.id: p for p in plant_all(sh.vfs, seed=3)} | |
| # The password is in .bash_history | |
| history = sh.run("cat ~/.bash_history").out | |
| password = next( | |
| word.rstrip(",") for line in history.splitlines() if "# pw" in line | |
| for word in [line.split()[2]] | |
| ) | |
| sh.run(f"cd ~/downloads") | |
| assert "inflating" in sh.run(f"unzip severance.zip {password}").out | |
| assert puzzles["zip_password"].poll(sh) is not None | |
| assert puzzles["zip_password"].solved | |
| def test_zip_rejects_wrong_password(): | |
| sh = make_shell(seed=3) | |
| plant_all(sh.vfs, seed=3) | |
| assert "incorrect password" in sh.run("unzip ~/downloads/severance.zip guess").err | |
| def test_hidden_dir_puzzle_requires_reading_manifest(): | |
| sh = make_shell(seed=3) | |
| puzzles = {p.id: p for p in plant_all(sh.vfs, seed=3)} | |
| assert puzzles["hidden_dir"].poll(sh) is None | |
| sh.run("cat ~/.warden/manifest.txt") | |
| reward = puzzles["hidden_dir"].poll(sh) | |
| assert reward is not None and reward.kind == "card" | |
| def test_cron_defusal_by_rm(): | |
| sh = make_shell(seed=3) | |
| puzzles = {p.id: p for p in plant_all(sh.vfs, seed=3)} | |
| assert puzzles["cron_defusal"].poll(sh) is None | |
| sh.run("rm /etc/cron.d/reinforcement") | |
| reward = puzzles["cron_defusal"].poll(sh) | |
| assert reward is not None and reward.kind == "mercy" | |
| # -------------------------------------------------------------- ingestion | |
| def test_fabricate_is_deterministic(): | |
| a, b = VFS(), VFS() | |
| fabricate_home(a, seed=9) | |
| fabricate_home(b, seed=9) | |
| assert [p for p, _ in a.iter_files()] == [p for p, _ in b.iter_files()] | |
| def test_mirror_filters_secrets_and_caps(tmp_path: Path): | |
| (tmp_path / "notes").mkdir() | |
| (tmp_path / "notes" / "diary.txt").write_text("dear diary") | |
| (tmp_path / ".ssh").mkdir() | |
| (tmp_path / ".ssh" / "id_rsa").write_text("PRIVATE KEY") | |
| (tmp_path / "api_token.txt").write_text("sk-12345") | |
| (tmp_path / "big.txt").write_text("x" * 100_000) | |
| (tmp_path / "photo.png").write_bytes(b"\x89PNG....") | |
| vfs = VFS() | |
| mirror_home(vfs, source=tmp_path) | |
| files = dict(vfs.iter_files()) | |
| assert files["/home/drifter/notes/diary.txt"].content == "dear diary" | |
| assert not any(".ssh" in p for p in files) | |
| assert not any("api_token" in p for p in files) | |
| # Oversized text and binary files come through as name-only stubs. | |
| assert files["/home/drifter/big.txt"].content == "<mirrored file>" | |
| assert files["/home/drifter/photo.png"].content == "<mirrored file>" | |
| def test_mirror_never_writes_to_host(tmp_path: Path): | |
| (tmp_path / "a.txt").write_text("hi") | |
| before = sorted(p.name for p in tmp_path.rglob("*")) | |
| vfs = VFS() | |
| mirror_home(vfs, source=tmp_path) | |
| vfs.remove("/home/drifter/a.txt") # the Warden deletes the COPY | |
| after = sorted(p.name for p in tmp_path.rglob("*")) | |
| assert before == after | |
| def test_vfs_and_shell_never_import_os(): | |
| """The tripwire: only mirror.py may touch the host filesystem.""" | |
| import scrypt.sandbox.puzzles as puzzles | |
| import scrypt.sandbox.shell as shell | |
| import scrypt.sandbox.vfs as vfs | |
| for module in (vfs, shell, puzzles): | |
| source = Path(module.__file__).read_text() | |
| assert "import os" not in source, f"{module.__name__} imports os" | |
| assert "pathlib" not in source, f"{module.__name__} imports pathlib" | |
| assert "open(" not in source, f"{module.__name__} calls open()" | |