{"record":{"id":"3687a8ef9af019b1","repo":"crewAIInc/crewAI","slug":"symlink-escapes-allowed-directory-self-path","errorCode":null,"errorMessage":"Symlink escapes allowed directory: {self.path}","messagePattern":"Symlink escapes allowed directory: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-files/src/crewai_files/core/sources.py","lineNumber":231,"sourceCode":"        description=\"Maximum file size in bytes.\",\n    )\n    _content: bytes | None = PrivateAttr(default=None)\n    _content_type: str = PrivateAttr()\n\n    @model_validator(mode=\"after\")\n    def _validate_file_exists(self) -> FilePath:\n        \"\"\"Validate that the file exists, is secure, and within size limits.\"\"\"\n        from crewai_files.processing.exceptions import FileTooLargeError\n\n        path_str = str(self.path)\n        if \"..\" in path_str:\n            raise ValueError(f\"Path traversal not allowed: {self.path}\")\n\n        if self.path.is_symlink():\n            resolved = self.path.resolve()\n            cwd = Path.cwd().resolve()\n            if not str(resolved).startswith(str(cwd)):\n                raise ValueError(f\"Symlink escapes allowed directory: {self.path}\")\n\n        if not self.path.exists():\n            raise ValueError(f\"File not found: {self.path}\")\n        if not self.path.is_file():\n            raise ValueError(f\"Path is not a file: {self.path}\")\n\n        actual_size = self.path.stat().st_size\n        if actual_size > self.max_size_bytes:\n            raise FileTooLargeError(\n                f\"File exceeds max size ({actual_size} > {self.max_size_bytes})\",\n                file_name=str(self.path),\n                actual_size=actual_size,\n                max_size=self.max_size_bytes,\n            )\n\n        self._content_type = detect_content_type_from_path(self.path, self.path.name)\n        return self\n","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-files/src/crewai_files/core/sources.py#L213-L249","documentation":"Validation error from FilePath: the path is a symlink, and resolving it yields a location that does not start with the current working directory (Path.cwd().resolve()). The guard is string-prefix based against CWD, not against an explicit allow-list, so even a safe symlink inside your project fails whenever your process CWD is elsewhere (e.g. server started from / while files live in /app). It exists to stop symlink-based escape of the allowed directory.","triggerScenarios":"FilePath(path=Path(\"data/linked.txt\")) where linked.txt -> /etc/secret, or a legitimate in-project symlink while the process was started from a directory that is not a prefix of the resolved target (systemd services, Docker WORKDIR mismatches).","commonSituations":"Monorepo symlinks to shared assets; Docker containers where CWD differs from the app dir; daemon processes launched from /; symlinked home-dir configs.","solutions":["chdir to the intended root (or launch the process with the correct working directory) so CWD is a prefix of the resolved symlink target.","Replace symlinks with real files or copy the target into the project.","Pass a direct, non-symlink path to the real file's location if it is reachable."],"exampleFix":"# before (service runs with cwd=/):\nsrc = FilePath(path=Path(\"/app/data/linked.txt\"))  # symlink -> /app/assets/x.txt\n\n# after:\nimport os\nos.chdir(\"/app\")  # or fix the unit file WorkingDirectory=/app\nsrc = FilePath(path=Path(\"data/linked.txt\"))","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef symlink_ok(p: Path) -> bool:\n    if not p.is_symlink():\n        return True\n    return str(p.resolve()).startswith(str(Path.cwd().resolve()))","typeGuard":null,"tryCatchPattern":"try:\n    FilePath(path=p)\nexcept ValidationError as e:\n    if \"Symlink escapes\" in str(e):\n        FilePath(path=p.resolve())  # use the real target directly","preventionTips":["Set the process working directory (or container WORKDIR / systemd WorkingDirectory) to the app root.","Avoid symlinks in user-data directories; copy files instead.","The allow-list is CWD-based — validate against Path.cwd() in tests too."],"tags":["security","symlink","validation","file-sources","working-directory"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}