{"record":{"id":"eeb7683a780fe55c","repo":"invoke-ai/InvokeAI","slug":"parent-directory-references-not-allowed-in-subfold-eeb768","errorCode":null,"errorMessage":"Parent directory references not allowed in subfolder path","messagePattern":"Parent directory references not allowed in subfolder path","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"invokeai/app/services/video_files/video_files_disk.py","lineNumber":216,"sourceCode":"        graph = sidecar.get(\"invokeai_graph\")\n        return graph if isinstance(graph, str) else None\n\n    def validate_path(self, path: Union[str, Path]) -> bool:\n        path = path if isinstance(path, Path) else Path(path)\n        return path.exists()\n\n    @staticmethod\n    def _validate_subfolder(subfolder: str) -> None:\n        \"\"\"Validates a subfolder path to prevent directory traversal.\"\"\"\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        for part in subfolder.split(\"/\"):\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\")\n\n    def __get_sidecar_path(self, video_name: str, video_subfolder: str = \"\") -> Path:\n        sidecar_name = Path(video_name).stem + \".json\"\n        if video_subfolder:\n            self._validate_subfolder(video_subfolder)\n            sidecar_path = self.__sidecars_folder / video_subfolder / sidecar_name\n        else:\n            sidecar_path = self.__sidecars_folder / sidecar_name\n        resolved_base = self.__sidecars_folder.resolve()\n        resolved_sidecar_path = sidecar_path.resolve()\n        if not resolved_sidecar_path.is_relative_to(resolved_base):\n            raise ValueError(\"Sidecar path outside outputs folder, potential directory traversal detected\")\n        return resolved_sidecar_path\n\n    def __read_sidecar(self, video_name: str, video_subfolder: str = \"\") -> Optional[dict]:\n        path = self.__get_sidecar_path(video_name, video_subfolder=video_subfolder)","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/video_files/video_files_disk.py#L198-L234","documentation":"_validate_subfolder rejects any '/'-separated subfolder containing a '..' segment, because '..' would escape the video files base directory on disk. This guard runs before any path is joined (get_path and __get_sidecar_path). It prevents directory-traversal writes/reads outside the outputs root.","triggerScenarios":"Calling get_path(video_name, subfolder=...) or any sidecar operation (save, stage_delete, read) with a subfolder containing '..' segments, e.g. '../shared', 'a/../../b'.","commonSituations":"Client-supplied subfolder strings passed through unvalidated from an API request or workflow node; path building with os.path.join-style relative navigation on Windows/Unix; attempts to store videos outside the outputs folder.","solutions":["Remove '..' components from the subfolder before calling the API; compute the desired path relative to the base folder","Normalize user input: strip or resolve '..' and validate against an allowlist of folders","Use pathlib and check Path(subfolder) resolves inside the base dir before passing it"],"exampleFix":"// before\nget_path(name, subfolder=\"../shared\")\n// after\nsubfolder = \"shared\"  # or posixpath.normpath(raw).strip(\"./\") validated to be relative\nget_path(name, subfolder=subfolder)","handlingStrategy":"validation","validationCode":"def is_safe_subfolder(sub: str) -> bool:\n    parts = sub.split(\"/\") if sub else []\n    return bool(parts) and all(p not in (\"\", \".\", \"..\") for p in parts) and \"\\\\\" not in sub and not sub.startswith(\"/\")","typeGuard":"def valid_subfolder(sub: str) -> str | None:\n    return sub if is_safe_subfolder(sub) else None","tryCatchPattern":"try:\n    path = service.get_path(video_name, subfolder=sub)\nexcept ValueError as e:\n    if \"subfolder\" in str(e):\n        path = service.get_path(video_name, subfolder=\"\")  # fall back to root\n    else:\n        raise","preventionTips":["Never build subfolders from raw user input without normalizing","Use posixpath.normpath and reject results containing '..' or leading '/'","Store the subfolder as a list of validated segments rather than a joined string"],"tags":["security","path-traversal","filesystem"],"backgroundTag":"directory-traversal-blocked","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}