{"record":{"id":"f1c1704890949b72","repo":"invoke-ai/InvokeAI","slug":"image-path-outside-outputs-folder-potential-direc","errorCode":null,"errorMessage":"Image path outside outputs folder, potential directory traversal detected","messagePattern":"Image path outside outputs folder, potential directory traversal detected","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"invokeai/app/services/image_files/image_files_disk.py","lineNumber":273,"sourceCode":"\n        # Validate the filename itself (no path separators allowed in the filename)\n        basename = Path(filename).name\n        if basename != filename:\n            raise ValueError(\"Invalid image name, potential directory traversal detected\")\n\n        # Build the full path with optional subfolder\n        if image_subfolder:\n            self._validate_subfolder(image_subfolder)\n            image_path = base_folder / image_subfolder / basename\n        else:\n            image_path = base_folder / basename\n\n        # Ensure the image path is within the base folder to prevent directory traversal\n        resolved_base = base_folder.resolve()\n        resolved_image_path = image_path.resolve()\n\n        if not resolved_image_path.is_relative_to(resolved_base):\n            raise ValueError(\"Image path outside outputs folder, potential directory traversal detected\")\n\n        return resolved_image_path\n\n    @staticmethod\n    def _validate_subfolder(subfolder: str) -> None:\n        \"\"\"Validates a subfolder path to prevent directory traversal while allowing controlled subdirectories.\"\"\"\n        if not subfolder:\n            return\n        if \"\\\\\" in subfolder:\n            raise ValueError(\"Backslashes not allowed in subfolder path\")\n        if subfolder.startswith(\"/\"):\n            raise ValueError(\"Absolute paths not allowed in subfolder path\")\n        parts = subfolder.split(\"/\")\n        for part in parts:\n            if part == \"..\":\n                raise ValueError(\"Parent directory references not allowed in subfolder path\")\n            if part == \"\":\n                raise ValueError(\"Empty path segments not allowed in subfolder path\")","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/image_files/image_files_disk.py#L255-L291","documentation":"After building the final path, get_path resolves it and confirms it stays inside the resolved base folder (outputs or thumbnails). This final containment check catches cases where the resolved path escapes the base (e.g. symlinks or remaining traversal), raising ValueError otherwise.","triggerScenarios":"Calling get/save/stage_delete where the resolved path of base_folder + subfolder + basename is outside the base folder — e.g. a symlinked subfolder pointing elsewhere, or validation bypassed via unusual path forms.","commonSituations":"Output folder itself containing symlinks to external directories; mounting or relocating the outputs dir while retaining symlinks; OS-level path aliases (short names, case tricks).","solutions":["Remove symlinks inside the outputs/thumbnails folders or point them inside the base folder","Recreate the outputs folder with real directories (no symlinked subfolders)","Ensure the configured output folder path itself is a real, resolved directory"],"exampleFix":"// before\nln -s /external/data outputs/movies\n// after\nmkdir outputs/movies  # real directory inside the outputs folder","handlingStrategy":"validation","validationCode":"from pathlib import Path\nbase = Path('outputs').resolve()\ndef path_is_contained(name: str, subfolder: str = '') -> bool:\n    p = (base / subfolder / name).resolve()\n    return p.is_relative_to(base)\nassert path_is_contained('img.png', 'sub')","typeGuard":"def resolves_inside(name: str, base: Path) -> bool:\n    return (base / name).resolve().is_relative_to(base.resolve())","tryCatchPattern":"try:\n    path = service.get_path(image_name, image_subfolder=subfolder)\nexcept ValueError as e:\n    logger.error('Path containment failed: %s', e)\n    raise HTTPException(400, 'Path escapes outputs folder') from e","preventionTips":["Avoid symlinks inside the outputs/thumbnails folders","Audit the outputs directory periodically for links pointing outside","Keep the configured output path a real resolved directory"],"tags":["security","path-traversal","filesystem"],"backgroundTag":"directory-traversal","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}