Inspect static PDF and OOXML content with bounded local coverage
This commit is contained in:
1 parent
69d820e315
commit
9e7cb57093
14 files changed
+758
-51
No files matched your search
@@ -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")
|
||||
|
||||
|
||||
Reference in new issue
Block a user