{"record":{"id":"a348768db90aa28f","repo":"docling-project/docling","slug":"zip-slip-attempt-member-filename","errorCode":null,"errorMessage":"ZIP slip attempt: {member.filename}","messagePattern":"ZIP slip attempt: (.+?)","errorType":"validation","errorClass":"SecurityError","httpStatus":null,"severity":"critical","filePath":"docling/models/stages/ocr/easyocr_model.py","lineNumber":181,"sourceCode":"        recognition_models_by_name = {\n            model_name: model_details\n            for generation in rec_models_dict.values()\n            for model_name, model_details in generation.items()\n        }\n        for model_name in recognition_models:\n            if model_name in recognition_models_by_name:\n                download_list.append(recognition_models_by_name[model_name])\n\n        # Download models\n        for model_details in download_list:\n            buf = download_url_with_progress(model_details[\"url\"], progress=progress)\n            with zipfile.ZipFile(buf, \"r\") as zip_ref:\n                for member in zip_ref.infolist():\n                    member_path = os.path.realpath(\n                        os.path.join(local_dir, member.filename)\n                    )\n                    if not member_path.startswith(os.path.realpath(local_dir) + os.sep):\n                        raise SecurityError(f\"ZIP slip attempt: {member.filename}\")\n                    zip_ref.extract(member, local_dir)\n\n        return local_dir\n\n    def __call__(\n        self, conv_res: ConversionResult, page_batch: Iterable[Page]\n    ) -> Iterable[Page]:\n        if not self.enabled:\n            yield from page_batch\n            return\n\n        for page in page_batch:\n            assert page._backend is not None\n            if not page._backend.is_valid():\n                yield page\n            else:\n                with TimeRecorder(conv_res, \"ocr\"):\n                    ocr_rects = self.get_ocr_rects(page)","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/models/stages/ocr/easyocr_model.py#L163-L199","documentation":"When downloading EasyOCR model archives, docling extracts each zip member after checking that the resolved destination stays inside the target directory. If a member filename like '../../etc/passwd' would escape local_dir, a SecurityError is raised naming the offending entry. This is a defense against the ZIP slip path-traversal attack, and it means the downloaded archive is malformed or was tampered with, not that your code is wrong.","triggerScenarios":"Prefetching/downloading EasyOCR models (download_models) where the fetched zip contains entries whose paths resolve outside the extraction directory — malicious or corrupted archive, or a redirected/spoofed download URL.","commonSituations":"Compromised or misconfigured model host serving crafted archives; corporate proxies returning HTML/error pages saved as zips with odd entry names; corrupted downloads from interrupted transfers.","solutions":["Delete the partially downloaded cache/artifacts and retry the model download from the official source on a trusted network.","Verify the download URL and integrity of the model archive (hash) if your setup pins URLs.","If it recurs, fetch models manually from the EasyOCR upstream repository and place them in artifacts_path, and report the incident — a zip-slip hit can indicate tampering."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"def zip_is_safe(zip_path: Path, dest: Path) -> bool:\n    dest_real = os.path.realpath(dest)\n    with zipfile.ZipFile(zip_path) as zf:\n        return all(\n            os.path.realpath(os.path.join(dest_real, m.filename)).startswith(dest_real + os.sep)\n            for m in zf.infolist()\n        )","typeGuard":null,"tryCatchPattern":"try:\n    model_dir = model.download_models(local_dir=artifacts)\nexcept SecurityError as err:\n    logger.critical(\"Model archive failed ZIP-slip check: %s — possible tampering\", err)\n    raise  # do NOT bypass; retry from a trusted network/source instead","preventionTips":["Treat any zip-slip hit as a security incident: verify the download source and archive hashes before retrying.","Pin model download URLs to official hosts; avoid untrusted mirrors/proxies for model artifacts.","Keep the artifacts cache cleanable so a re-download is a cheap recovery path."],"tags":["zip-slip","security","easyocr","model-download","path-traversal"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}