docling-project/docling · error · ValueError

Path traversal blocked: '{loc}' resolves outside base direct

Error message

Path traversal blocked: '{loc}' resolves outside base directory

What it means

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.

Source

Thrown at docling/backend/utils/image_resource_loader.py:151

        abs_loc = loc

        if base_path:
            if loc.startswith("//"):
                abs_loc = "https:" + loc
            elif not loc.startswith(("http://", "https://", "data:", "#")):
                if ImageResourceLoader.is_remote_url(base_path):
                    abs_loc = urljoin(base_path, loc)
                elif ImageResourceLoader.is_local_path(base_path):
                    if ImageResourceLoader.is_absolute_path(loc):
                        raise ValueError(
                            f"Absolute paths are not allowed with local base_path: '{loc}'"
                        )

                    base_dir = Path(base_path).parent.resolve()
                    resolved_path = (base_dir / loc).resolve()

                    if not resolved_path.is_relative_to(base_dir):
                        raise ValueError(
                            f"Path traversal blocked: '{loc}' resolves outside base directory"
                        )
                    abs_loc = str(resolved_path)
                else:
                    raise ValueError(f"Invalid base_path format: '{base_path}'")

        _log.debug(f"Resolved location {loc} to {abs_loc}")
        return abs_loc

    def create_image_ref(
        self, src_url: str, base_path: Optional[str]
    ) -> Optional[ImageRef]:
        try:
            img_data = self.load_image_data(src_url, base_path)
            if img_data:
                img = Image.open(BytesIO(img_data))
                return ImageRef.from_pil(img, dpi=int(img.info.get("dpi", (72,))[0]))
        except (

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Move/copy referenced assets under the document's directory (or a subdirectory of it) and fix the srcs.
  2. Set base_path to the true common root of documents and assets so traversal resolves inside it.
  3. For untrusted input, keep the guard and treat the hit as malicious content (log and skip the image).

Example fix

# before
<img src="../../shared/logo.png">  # base_path='/data/clients/a/doc.html' -> blocked

# after
# copy logo.png under /data/clients/a/assets/ and use:
<img src="assets/logo.png">
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
base_dir = Path(base_path).parent.resolve()
candidate = (base_dir / loc).resolve()
if not candidate.is_relative_to(base_dir):
    raise ValueError(f'{loc} escapes document directory')

Try / catch

try:
    loc = loader.resolve_relative_path(src, base)
except ValueError as e:
    if 'Path traversal' in str(e):
        log_security_event(src); loc = None
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14). Data as JSON: /api/errors/03d2bdfe04df5c63. Report an issue: GitHub.