Coverage for src/local_deep_research/document_loaders/openpyxl_hardening.py: 90%
16 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-22 21:20 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-22 21:20 +0000
1"""Stop openpyxl from reading chartsheet drawings (images and charts).
3``.xlsx`` text extraction goes unstructured ``partition_xlsx`` ->
4``pandas.read_excel`` -> ``openpyxl.load_workbook(read_only=True)``.
5Even in read-only mode openpyxl loads every chartsheet's drawing via
6``openpyxl.reader.drawings.find_images``, which calls
7``archive.read(target)`` once **per image reference** and keeps every
8copy (and parses a chart part once per chart reference). One image
9entry referenced N times therefore costs N times its size: a ~100 KB
10upload can pin gigabytes, which no per-entry ceiling can bound.
12Images and charts contribute no text, and this application uses
13openpyxl only to extract text, so the reader's drawing hook is replaced
14with one that returns nothing. That removes the per-reference reads at
15their source instead of trying to count references in attacker-written
16XML. (Worksheet drawings are only read outside read-only mode, which
17this path never uses.)
19The patch is **process-wide and permanent**: once the first ``.xlsx``
20upload is processed, every openpyxl workbook loaded anywhere in this
21process has no images or charts. Nothing else in the application reads
22workbooks with openpyxl; anything added later that needs drawings must
23load them some other way.
24"""
26from __future__ import annotations
28from loguru import logger
31def _no_drawings(archive, path): # noqa: ARG001 - signature of find_images
32 """Stand-in for ``openpyxl.reader.drawings.find_images``."""
33 return [], []
36def disable_openpyxl_drawing_reads() -> bool:
37 """Replace openpyxl's drawing reader with a no-op (idempotent).
39 Returns True when the hook is in place. Returns False, with a
40 warning, when openpyxl is missing or no longer exposes the hook.
41 The upload caller refuses .xlsx files in that case.
42 """
43 try:
44 import openpyxl.reader.excel as excel_reader
45 except ImportError:
46 return False
47 # Checked by identity rather than a flag, so a reload of openpyxl
48 # (which restores the original hook) is patched again.
49 if getattr(excel_reader, "find_images", None) is _no_drawings:
50 return True
51 if not hasattr(excel_reader, "find_images"):
52 logger.warning(
53 "openpyxl no longer exposes reader.excel.find_images; "
54 ".xlsx drawings are read on load again"
55 )
56 return False
57 excel_reader.find_images = _no_drawings
58 return True