{"record":{"id":"03d2bdfe04df5c63","repo":"docling-project/docling","slug":"path-traversal-blocked-loc-resolves-outside-b","errorCode":null,"errorMessage":"Path traversal blocked: '{loc}' resolves outside base directory","messagePattern":"Path traversal blocked: '(.+?)' resolves outside base directory","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"docling/backend/utils/image_resource_loader.py","lineNumber":151,"sourceCode":"        abs_loc = loc\n\n        if base_path:\n            if loc.startswith(\"//\"):\n                abs_loc = \"https:\" + loc\n            elif not loc.startswith((\"http://\", \"https://\", \"data:\", \"#\")):\n                if ImageResourceLoader.is_remote_url(base_path):\n                    abs_loc = urljoin(base_path, loc)\n                elif ImageResourceLoader.is_local_path(base_path):\n                    if ImageResourceLoader.is_absolute_path(loc):\n                        raise ValueError(\n                            f\"Absolute paths are not allowed with local base_path: '{loc}'\"\n                        )\n\n                    base_dir = Path(base_path).parent.resolve()\n                    resolved_path = (base_dir / loc).resolve()\n\n                    if not resolved_path.is_relative_to(base_dir):\n                        raise ValueError(\n                            f\"Path traversal blocked: '{loc}' resolves outside base directory\"\n                        )\n                    abs_loc = str(resolved_path)\n                else:\n                    raise ValueError(f\"Invalid base_path format: '{base_path}'\")\n\n        _log.debug(f\"Resolved location {loc} to {abs_loc}\")\n        return abs_loc\n\n    def create_image_ref(\n        self, src_url: str, base_path: Optional[str]\n    ) -> Optional[ImageRef]:\n        try:\n            img_data = self.load_image_data(src_url, base_path)\n            if img_data:\n                img = Image.open(BytesIO(img_data))\n                return ImageRef.from_pil(img, dpi=int(img.info.get(\"dpi\", (72,))[0]))\n        except (","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/backend/utils/image_resource_loader.py#L133-L169","documentation":"ValueError raised after resolving (base_dir / loc).resolve() when the result escapes base_dir (checked with Path.is_relative_to). It blocks '../' traversal: a relative image reference that walks out of the document's directory is rejected before any file read.","triggerScenarios":"A document at /data/doc.html references <img src=\"../../etc/passwd.png\"> or '../../../home/user/secret.png'; the resolved absolute path no longer sits under the parent of base_path, so the guard fires.","commonSituations":"Untrusted HTML with traversal payloads, symlinked relative targets, or legitimately-shared parent directories when documents are served from a nested folder but assets live above it.","solutions":["Move/copy referenced assets under the document's directory (or a subdirectory of it) and fix the srcs.","Set base_path to the true common root of documents and assets so traversal resolves inside it.","For untrusted input, keep the guard and treat the hit as malicious content (log and skip the image)."],"exampleFix":"# before\n<img src=\"../../shared/logo.png\">  # base_path='/data/clients/a/doc.html' -> blocked\n\n# after\n# copy logo.png under /data/clients/a/assets/ and use:\n<img src=\"assets/logo.png\">","handlingStrategy":"validation","validationCode":"from pathlib import Path\nbase_dir = Path(base_path).parent.resolve()\ncandidate = (base_dir / loc).resolve()\nif not candidate.is_relative_to(base_dir):\n    raise ValueError(f'{loc} escapes document directory')","typeGuard":null,"tryCatchPattern":"try:\n    loc = loader.resolve_relative_path(src, base)\nexcept ValueError as e:\n    if 'Path traversal' in str(e):\n        log_security_event(src); loc = None\n    else:\n        raise","preventionTips":["Package assets inside the document's directory before conversion","Set base_path to the true common root when assets legitimately live above the document","Treat traversal hits in untrusted documents as malicious input"],"tags":["path-traversal","security","images","local-files"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}