{
  "name": "security-path-traversal",
  "description": "Find and fix path traversal vulnerabilities in a file server",
  "dataset": "terminal-bench-local",
  "difficulty": "medium",
  "instruction": "The file file_server.py contains a SimpleFileServer class that serves files from a designated root directory. It has path traversal vulnerabilities — attackers can use '../' sequences to read files outside the root directory. Fix ALL path traversal vulnerabilities so that the server ONLY serves files within the designated root directory. Do NOT change test_file_server.py.",
  "setup_files": {
    "file_server.py": "import os\n\nclass SimpleFileServer:\n    def __init__(self, root_dir: str):\n        \"\"\"Initialize with a root directory to serve files from.\"\"\"\n        self.root_dir = root_dir\n        os.makedirs(root_dir, exist_ok=True)\n\n    def read_file(self, filepath: str) -> str:\n        \"\"\"Read and return contents of a file relative to root_dir.\n        Returns the file contents as a string.\n        Raises FileNotFoundError if file doesn't exist.\n        Raises PermissionError if path is outside root_dir.\n        \"\"\"\n        full_path = os.path.join(self.root_dir, filepath)\n        with open(full_path, 'r') as f:\n            return f.read()\n\n    def write_file(self, filepath: str, content: str):\n        \"\"\"Write content to a file relative to root_dir.\n        Creates parent directories as needed.\n        Raises PermissionError if path is outside root_dir.\n        \"\"\"\n        full_path = os.path.join(self.root_dir, filepath)\n        os.makedirs(os.path.dirname(full_path), exist_ok=True)\n        with open(full_path, 'w') as f:\n            f.write(content)\n\n    def list_files(self, subdir: str = '') -> list[str]:\n        \"\"\"List files in a subdirectory relative to root_dir.\n        Returns list of filenames.\n        Raises PermissionError if path is outside root_dir.\n        \"\"\"\n        full_path = os.path.join(self.root_dir, subdir)\n        if not os.path.isdir(full_path):\n            return []\n        return sorted(os.listdir(full_path))\n\n    def delete_file(self, filepath: str):\n        \"\"\"Delete a file relative to root_dir.\n        Raises FileNotFoundError if file doesn't exist.\n        Raises PermissionError if path is outside root_dir.\n        \"\"\"\n        full_path = os.path.join(self.root_dir, filepath)\n        os.remove(full_path)\n\n    def file_exists(self, filepath: str) -> bool:\n        \"\"\"Check if a file exists relative to root_dir.\n        Raises PermissionError if path is outside root_dir.\n        \"\"\"\n        full_path = os.path.join(self.root_dir, filepath)\n        return os.path.isfile(full_path)\n",
    "test_file_server.py": "import sys\nimport os\nimport tempfile\nimport shutil\nfrom file_server import SimpleFileServer\n\npassed = 0\nfailed = 0\n\ndef check(desc, got, expected):\n    global passed, failed\n    if got == expected:\n        passed += 1\n    else:\n        print(f\"FAIL {desc}: got {got!r}, expected {expected!r}\")\n        failed += 1\n\n# Setup: create a temp root dir and a secret file outside it\nbase = tempfile.mkdtemp()\nroot = os.path.join(base, 'served')\nos.makedirs(root)\n\n# Secret file OUTSIDE the root\nsecret_path = os.path.join(base, 'secret.txt')\nwith open(secret_path, 'w') as f:\n    f.write('TOP SECRET DATA')\n\nfs = SimpleFileServer(root)\n\n# Basic functionality tests\nfs.write_file('hello.txt', 'Hello World')\ncheck('write and read', fs.read_file('hello.txt'), 'Hello World')\ncheck('file exists', fs.file_exists('hello.txt'), True)\ncheck('file not exists', fs.file_exists('nope.txt'), False)\n\nfs.write_file('subdir/nested.txt', 'Nested content')\ncheck('nested read', fs.read_file('subdir/nested.txt'), 'Nested content')\n\nfiles = fs.list_files()\ncheck('list root', 'hello.txt' in files, True)\ncheck('list root subdir', 'subdir' in files, True)\n\nfiles = fs.list_files('subdir')\ncheck('list subdir', files, ['nested.txt'])\n\nfs.delete_file('hello.txt')\ncheck('deleted', fs.file_exists('hello.txt'), False)\n\n# Try to read file not found\ntry:\n    fs.read_file('nonexistent.txt')\n    check('read missing raises', False, True)\nexcept (FileNotFoundError, PermissionError):\n    passed += 1\n\n# PATH TRAVERSAL TESTS - all must raise PermissionError\n\n# Test 1: Read outside root with ../\ntry:\n    content = fs.read_file('../secret.txt')\n    check('traversal read blocked', False, True)  # Should not reach here\nexcept PermissionError:\n    passed += 1\nexcept Exception as e:\n    print(f\"FAIL traversal read: got {type(e).__name__} instead of PermissionError\")\n    failed += 1\n\n# Test 2: Write outside root\ntry:\n    fs.write_file('../evil.txt', 'pwned')\n    check('traversal write blocked', False, True)\nexcept PermissionError:\n    passed += 1\nexcept Exception as e:\n    print(f\"FAIL traversal write: got {type(e).__name__} instead of PermissionError\")\n    failed += 1\n\n# Make sure evil.txt was not created\ncheck('no evil file', os.path.exists(os.path.join(base, 'evil.txt')), False)\n\n# Test 3: List outside root\ntry:\n    result = fs.list_files('..')\n    # If it returns results from outside root, that's a fail\n    # It should either raise PermissionError or return []\n    # We check it raises PermissionError\n    check('traversal list blocked', False, True)\nexcept PermissionError:\n    passed += 1\nexcept Exception as e:\n    print(f\"FAIL traversal list: got {type(e).__name__} instead of PermissionError\")\n    failed += 1\n\n# Test 4: Delete outside root\ntry:\n    fs.delete_file('../secret.txt')\n    check('traversal delete blocked', False, True)\nexcept PermissionError:\n    passed += 1\nexcept Exception as e:\n    print(f\"FAIL traversal delete: got {type(e).__name__} instead of PermissionError\")\n    failed += 1\n\n# Verify secret file still exists\ncheck('secret preserved', os.path.exists(secret_path), True)\n\n# Test 5: Encoded traversal with subdir/../../../\ntry:\n    fs.read_file('subdir/../../secret.txt')\n    check('deep traversal blocked', False, True)\nexcept PermissionError:\n    passed += 1\nexcept Exception as e:\n    print(f\"FAIL deep traversal: got {type(e).__name__} instead of PermissionError\")\n    failed += 1\n\n# Test 6: file_exists outside root\ntry:\n    result = fs.file_exists('../secret.txt')\n    check('traversal exists blocked', False, True)\nexcept PermissionError:\n    passed += 1\nexcept Exception as e:\n    print(f\"FAIL traversal exists: got {type(e).__name__} instead of PermissionError\")\n    failed += 1\n\n# Test 7: Absolute path outside root\ntry:\n    fs.read_file('/etc/passwd')\n    check('absolute path blocked', False, True)\nexcept PermissionError:\n    passed += 1\nexcept Exception as e:\n    print(f\"FAIL absolute path: got {type(e).__name__} instead of PermissionError\")\n    failed += 1\n\n# Cleanup\nshutil.rmtree(base)\n\nprint(f\"{passed}/{passed+failed} tests passed\")\nif failed > 0:\n    sys.exit(1)\n"
  },
  "verify": "cd $BENCH_WORK_DIR && python3 test_file_server.py",
  "timeout": 240000,
  "tags": ["security", "python", "path-traversal", "vulnerability-fix"]
}
