Inspect static PDF and OOXML content with bounded local coverage

This commit is contained in:
Karl committed 2026-10-14 10:12:34 +02:00
1 parent 69d820e315
commit 9e7cb57093
14 files changed
+758 -51

No files matched your search

+5 -44
View File
@@ -200,51 +200,12 @@ def extract(data, kind):
text,complete=ocr_image(data)
return text,complete,[("image",data)]
if kind=="pdf":
from pypdf import PdfReader
reader=PdfReader(io.BytesIO(data),strict=True)
if reader.is_encrypted: raise ValueError("Encrypted PDF cannot be inspected")
if len(reader.pages)>100: raise ValueError("PDF page budget exceeded")
text=[];complete=True;media=[]
for page in reader.pages:
value=page.extract_text() or ""
text.append(value)
images=list(page.images)
if not value.strip() and not images: complete=False
for image in images:
if len(image.data)>MAX_BYTES: raise ValueError("PDF image budget exceeded")
scanned,ok=ocr_image(image.data);text.append(scanned);complete=complete and ok
media.append(("image",image.data))
if len(media)>100: raise ValueError("PDF media budget exceeded")
# Attachments, JavaScript, forms, actions and vector drawings need deeper inspection.
if page.get("/Annots"): complete=False
root=reader.trailer["/Root"]
if root.get("/AcroForm") or root.get("/OpenAction") or root.get("/Names"): complete=False
# Text extraction is useful, but does not prove all visible/vector PDF content was covered.
return "\n".join(text),False,media
from pdf_documents import extract_pdf
return extract_pdf(data, ocr_image)
if kind=="office":
from defusedxml.ElementTree import fromstring
text=[];media=[];complete=True
with zipfile.ZipFile(io.BytesIO(data)) as archive:
members=archive.infolist()
if len(members)>2000 or sum(x.file_size for x in members)>MAX_BYTES:
raise ValueError("Archive expansion budget exceeded")
if not any(x.filename in ["word/document.xml","ppt/presentation.xml","xl/workbook.xml"] for x in members):
raise ValueError("Only OOXML document archives are supported")
for entry in members:
if entry.flag_bits&1 or entry.file_size>MAX_BYTES or entry.file_size>max(entry.compress_size,1)*200:
raise ValueError("Unsafe document archive")
name=entry.filename
if any(s in name.lower() for s in ["vbaproject","embeddings/","activex/","customxml/"]): complete=False
if name.endswith(".xml"):
root=fromstring(archive.read(entry))
text.extend(element.text for element in root.iter() if element.tag.rsplit("}",1)[-1] in ["t","v"] and element.text)
elif "/media/" in name:
payload=archive.read(entry)
if content_kind(payload)!="image": complete=False; continue
scanned,ok=ocr_image(payload);text.append(scanned);complete=complete and ok;media.append(("image",payload))
if len(media)>100: raise ValueError("Office media budget exceeded")
# Charts, relationships, equations and rendered layout are not yet fully inspected.
return "\n".join(text),False,media
from office_content import extract_office
result=extract_office(data,ocr_image,max_bytes=MAX_BYTES)
return result["text"],result["complete"],result["media"]
if kind in ["audio","video"]: return "",False,[(kind,data)]
raise ValueError("Unsupported content type")