{"record":{"id":"e8b94460493d6369","repo":"docling-project/docling","slug":"refusing-to-expand-ooxml-package-exceeding-the-unc","errorCode":null,"errorMessage":"Refusing to expand OOXML package exceeding the uncompressed size limit","messagePattern":"Refusing to expand OOXML package exceeding the uncompressed size limit","errorType":"exception","errorClass":"SecurityError","httpStatus":null,"severity":"error","filePath":"docling/backend/msword_backend.py","lineNumber":227,"sourceCode":"    Only XML/relationship parts that actually carry a Strict namespace are\n    decoded and rewritten; every other member (images, fonts, ...) is copied\n    through with its original compression, avoiding a needless decode pass. Each\n    member is decompressed exactly once. The archive is validated against\n    zip-slip and zip-bomb attacks while it is read.\n    \"\"\"\n    normalized = BytesIO()\n    total_uncompressed = 0\n    with zipfile.ZipFile(normalized, \"w\", zipfile.ZIP_DEFLATED) as target:\n        for info in archive.infolist():\n            if not _is_safe_zip_member(info.filename):\n                raise SecurityError(f\"ZIP slip attempt: {info.filename}\")\n            if info.file_size > _MAX_MEMBER_UNCOMPRESSED_SIZE:\n                raise SecurityError(\n                    f\"Refusing to expand oversized OOXML part: {info.filename}\"\n                )\n            total_uncompressed += info.file_size\n            if total_uncompressed > _MAX_TOTAL_UNCOMPRESSED_SIZE:\n                raise SecurityError(\n                    \"Refusing to expand OOXML package exceeding the uncompressed size limit\"\n                )\n            content = archive.read(info.filename)\n            if (\n                info.filename.endswith((\".xml\", \".rels\"))\n                and _STRICT_OOXML_MARKER in content\n            ):\n                content = _STRICT_OOXML_NS_RE.sub(\n                    lambda match: _strict_ns_to_transitional(match.group(0)),\n                    content.decode(\"utf-8\"),\n                ).encode(\"utf-8\")\n            target.writestr(info, content)\n    normalized.seek(0)\n    return normalized\n\n\nclass MsWordDocumentBackend(DeclarativeDocumentBackend):\n    \"\"\"Backend for parsing Word documents (DOCX and DOC files).","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/backend/msword_backend.py#L209-L245","documentation":"Raised as a SecurityError when the running sum of uncompressed member sizes in an OOXML package exceeds _MAX_TOTAL_UNCOMPRESSED_SIZE (2 GiB, docling/backend/msword_backend.py:137). It is the cumulative counterpart of the per-member guard and protects memory during strict-OOXML normalization, which rewrites the archive into a BytesIO buffer.","triggerScenarios":"Loading a .docx where sum(info.file_size for all members) > 2 GiB. Each member individually passes the 512 MiB check but together they exceed the total budget; common with many large embedded images or fonts.","commonSituations":"Marketing/report docx files stuffed with full-resolution photos, scanned-image documents saved as docx, or malicious archives engineered to exhaust memory during normalization.","solutions":["Identify the largest members with unzip -v file.docx and compress/downsample embedded media before conversion.","Extract media with a tool (e.g. docx2txt or unzip) and re-zip without unused parts if the package is bloated.","For trusted in-house files only, raise _MAX_TOTAL_UNCOMPRESSED_SIZE and reinstall docling from source.","Catch SecurityError at the batch level and skip/quarantine the offending document."],"exampleFix":"# before\nres = document_converter.convert('huge_report.docx')\n\n# after\nfrom docling.exceptions import SecurityError\ntry:\n    res = document_converter.convert('huge_report.docx')\nexcept SecurityError:\n    res = convert_offline('huge_report.docx')  # pre-cleaned copy with downscaled images","handlingStrategy":"validation","validationCode":"import zipfile\nTOTAL_LIMIT = 2 * 1024 * 1024 * 1024\nwith zipfile.ZipFile(path) as z:\n    total = sum(i.file_size for i in z.infolist())\nif total > TOTAL_LIMIT:\n    raise ValueError(f'package inflates to {total} bytes; compress embedded media first')","typeGuard":null,"tryCatchPattern":"from docling.exceptions import SecurityError\ntry:\n    result = converter.convert(path)\nexcept SecurityError:\n    result = convert_with_downsampled_media(path)","preventionTips":["Check the uncompressed sum via zipfile before submitting large office documents","Downsample embedded media in generated docx files","Treat repeated hits on one source as an upstream content problem, not a docling bug"],"tags":["security","zip-bomb","docx","memory"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}