30 lines
1.3 KiB
Python
30 lines
1.3 KiB
Python
"""Bounded, non-extracting Office container validation; Python stdlib only."""
|
|
import sys
|
|
import zipfile
|
|
|
|
path, ext, entries, maximum = sys.argv[1:]
|
|
if ext in ('doc', 'xls', 'ppt'):
|
|
with open(path, 'rb') as source:
|
|
if source.read(8) != bytes.fromhex('d0cf11e0a1b11ae1'):
|
|
raise ValueError('not OLE')
|
|
else:
|
|
expected = {'docx': 'word/document.xml', 'xlsx': 'xl/workbook.xml', 'pptx': 'ppt/presentation.xml'}[ext]
|
|
with zipfile.ZipFile(path) as archive:
|
|
info = archive.infolist()
|
|
names = [entry.filename for entry in info]
|
|
if len(info) > int(entries) or len(set(names)) != len(names):
|
|
raise ValueError('entry limit or duplicate')
|
|
if expected not in names or '[Content_Types].xml' not in names:
|
|
raise ValueError('format mismatch')
|
|
if sum(entry.file_size for entry in info) > int(maximum):
|
|
raise ValueError('uncompressed size limit')
|
|
total = 0
|
|
for entry in info:
|
|
if entry.flag_bits & 1:
|
|
raise ValueError('encrypted')
|
|
with archive.open(entry) as stream:
|
|
while chunk := stream.read(65536):
|
|
total += len(chunk)
|
|
if total > int(maximum):
|
|
raise ValueError('uncompressed size limit')
|