{"record":{"id":"0a053c79ababc19b","repo":"docling-project/docling","slug":"invalid-filename-contains-null-byte","errorCode":null,"errorMessage":"Invalid filename: contains null byte.","messagePattern":"Invalid filename: contains null byte\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"docling/models/stages/ocr/tesseract_ocr_cli_model.py","lineNumber":132,"sourceCode":"\n    @staticmethod\n    def _sanitize_cmd(cmd: str) -> str:\n        \"\"\"Validate and sanitize the Tesseract executable name/path to prevent injection.\n\n        Rejects values containing null bytes.\n        \"\"\"\n        if \"\\x00\" in cmd:\n            raise ValueError(\"Invalid Tesseract command: contains null byte.\")\n        return cmd\n\n    @staticmethod\n    def _sanitize_filename(filename: str) -> str:\n        \"\"\"Validate and sanitize a filename passed to the Tesseract CLI.\n\n        Rejects paths containing null bytes and resolves to an absolute path.\n        \"\"\"\n        if \"\\x00\" in filename:\n            raise ValueError(\"Invalid filename: contains null byte.\")\n        return str(Path(filename).resolve())\n\n    def _get_name_and_version(self) -> Tuple[str, str]:\n        if self._name is not None and self._version is not None:\n            return self._name, self._version  # type: ignore\n\n        cmd = [self._safe_tesseract_cmd, \"--version\"]\n\n        proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=False)\n        stdout, stderr = proc.communicate()\n\n        proc.wait()\n\n        # HACK: Windows versions of Tesseract output the version to stdout, Linux versions\n        # to stderr, so check both.\n        version_line = (\n            (stdout.decode(\"utf8\").strip() or stderr.decode(\"utf8\").strip())\n            .split(\"\\n\")[0]","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/models/stages/ocr/tesseract_ocr_cli_model.py#L114-L150","documentation":"Filenames handed to the Tesseract CLI (e.g. input images and output bases) are sanitized before subprocess invocation. A filename containing a NUL byte raises this ValueError, mirroring the path and command sanitizers, since NUL bytes cannot occur in valid filenames and signal malformed or injected input.","triggerScenarios":"Any flow that passes a filename containing \\x00 to the CLI-based Tesseract model — e.g. processing a list of files where one path came from binary or corrupted metadata.","commonSituations":"Batch processing paths read from untrusted manifests, archives with malformed entry names, or strings decoded from binary blobs.","solutions":["Clean or drop the offending path; strip control characters from file lists before processing.","Validate external path sources (manifests, uploads) with a null-byte check up front.","Log repr(path) for failed batches to identify the corrupted entry quickly."],"exampleFix":"# before\npaths = [\"page1.png\\x00\", \"page2.png\"]\n\n# after\npaths = [p for p in raw_paths if \"\\x00\" not in p]","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef safe_filenames(paths: list[str]) -> list[str]:\n    bad = [p for p in paths if \"\\x00\" in p]\n    if bad:\n        raise ValueError(f\"filenames contain NUL bytes: {bad!r}\")\n    return [str(Path(p).resolve()) for p in paths]","typeGuard":"def is_nul_free_filename(p: str) -> bool:\n    return bool(p) and \"\\x00\" not in p","tryCatchPattern":"try:\n    model.run_batch(files)\nexcept ValueError as e:\n    if \"Invalid filename\" in str(e):\n        files = [f for f in files if is_nul_free_filename(f)]\n        model.run_batch(files)\n    else:\n        raise","preventionTips":["Validate externally sourced filenames (uploads, manifests) before batch processing.","Decode path data strictly; treat undecodable/control-char entries as corrupt.","Skip-and-log bad entries rather than aborting whole batches when appropriate."],"tags":["ocr","tesseract","validation","security","injection"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}