"""Real OPC ZIP fixtures for strict static OOXML coverage and hostile parts.""" import base64 import io import json import sys import unittest import zipfile from pathlib import Path sys.path.insert(0,str(Path(__file__).resolve().parent.parent/'apps/inspector')) from stage_timings import collect,stage,metadata,validated from office_content import extract_office,OfficeCoverageError,CT,REL,R,W,S,P,A,CP,MAIN,TYPE_PREFIX PNG=base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wl6b5sAAAAASUVORK5CYII=') def xml(tag,namespace,inside=''):return f'<{tag} xmlns="{namespace}" xmlns:r="{R}">{inside}'.encode() def relations(items):return xml('Relationships',REL,''.join(f'' for id_,type_,target,mode in items)) def package(family='docx',parts=None,main=None,relationships=None,extra_info=None): main_name={'docx':'word/document.xml','xlsx':'xl/workbook.xml','pptx':'ppt/presentation.xml'}[family] main_type=next(type_ for type_,values in MAIN.items() if values[0]==family) default={'docx':xml('document',W,'

Public company newsletter

'),'xlsx':xml('workbook',S,''),'pptx':xml('presentation',P,'')} values={main_name:(main_type,main or default[family])};values.update(parts or {}) if family=='xlsx' and 'xl/worksheets/sheet1.xml' not in values:values['xl/worksheets/sheet1.xml']=(TYPE_PREFIX+'spreadsheetml.worksheet+xml',xml('worksheet',S,'Public projected revenue')) if family=='pptx' and 'ppt/slides/slide1.xml' not in values:values['ppt/slides/slide1.xml']=(TYPE_PREFIX+'presentationml.slide+xml',xml('sld',P,f'Public quarterly update')) rels={'_rels/.rels':[('root',R+'/officeDocument',main_name,None)]} if family=='xlsx':rels['xl/_rels/workbook.xml.rels']=[('rId1',R+'/worksheet','worksheets/sheet1.xml',None)] if family=='pptx':rels['ppt/_rels/presentation.xml.rels']=[('rId1',R+'/slide','slides/slide1.xml',None)] rels.update(relationships or {}) types=f''+''.join(f'' for name,(type_,_payload) in values.items())+'' output=io.BytesIO() with zipfile.ZipFile(output,'w',zipfile.ZIP_DEFLATED) as archive: archive.writestr('[Content_Types].xml',types) for name,(_type,payload) in values.items():archive.writestr(name,payload) for name,items in rels.items():archive.writestr(name,relations(items)) if extra_info:archive.writestr(extra_info,b'PUBLIC') return output.getvalue() def extract(data,callback=lambda _data:('',True),**limits):return extract_office(data,callback,**limits) class OfficeTests(unittest.TestCase): def test_public_valid_docx_xlsx_and_pptx_static_parts_complete(self): for family in ['docx','xlsx','pptx']: result=extract(package(family));self.assertTrue(result['complete'],result['reasons']);self.assertEqual(result['family'],family);self.assertIn('Public',result['text']) def test_core_metadata_attributes_namespace_and_split_runs_included(self): main=xml('document',W,'

TOPSECRET

') metadata=xml('coreProperties',CP,'METADATA-SECRET') result=extract(package(parts={'docProps/core.xml':('application/vnd.openxmlformats-package.core-properties+xml',metadata)},main=main)) self.assertTrue(result['complete']);self.assertIn('ATTR-SECRET',result['text']);self.assertIn('METADATA-SECRET',result['text']);self.assertIn('TOPSECRET',result['text']);self.assertIn(W,result['text']) def test_shared_strings_and_hidden_unreferenced_parts_are_inspected(self): parts={'xl/sharedStrings.xml':(TYPE_PREFIX+'spreadsheetml.sharedStrings+xml',xml('sst',S,'SHARED-SECRET')),'xl/worksheets/hidden.xml':(TYPE_PREFIX+'spreadsheetml.worksheet+xml',xml('worksheet',S,'HIDDEN-SECRET'))} result=extract(package('xlsx',parts));self.assertTrue(result['complete']);self.assertIn('SHARED-SECRET',result['text']);self.assertIn('HIDDEN-SECRET',result['text']) def test_image_text_receives_ocr_and_cannot_escape_dlp_input(self): parts={'word/media/image1.png':('image/png',PNG)};calls=[] def ocr(payload):calls.append(payload);return ('RASTER-SECRET',True) result=extract(package(parts=parts),ocr);self.assertTrue(result['complete']);self.assertEqual(calls,[PNG]);self.assertIn('RASTER-SECRET',result['text']);self.assertEqual(len(result['media']),1) self.assertFalse(extract(package(parts=parts),lambda _data:('',False))['complete']) def test_external_relationship_metadata_scanned_but_coverage_incomplete(self): result=extract(package(relationships={'word/_rels/document.xml.rels':[('external',R+'/hyperlink','https://host/RELATIONSHIP-SECRET','External')]})) self.assertFalse(result['complete']);self.assertIn('RELATIONSHIP-SECRET',result['text']) def test_unsupported_font_ole_macro_equation_chart_and_vector_fail_closed(self): for name,type_,payload in [('word/fonts/private.odttf','application/vnd.openxmlformats-officedocument.obfuscatedFont',b'font'),('word/embeddings/a.bin','application/vnd.openxmlformats-officedocument.oleObject',b'OLE'),('word/vbaProject.bin','application/vnd.ms-office.vbaProject',b'VBA'),('word/chart.xml',TYPE_PREFIX+'drawingml.chart+xml',xml('chartSpace',A,''))]: self.assertFalse(extract(package(parts={name:(type_,payload)}))['complete']) for content in ['',f'','']: self.assertFalse(extract(package(main=xml('document',W,''+content+'')))['complete']) def test_unknown_xml_content_type_is_incomplete_even_if_text_readable(self): result=extract(package(parts={'word/private.xml':('application/x-private+xml',xml('document',W,'SECRET'))}));self.assertFalse(result['complete']);self.assertIn('SECRET',result['text']) def test_duplicate_case_paths_traversal_and_compression_bombs_rejected(self): for extra in ['word/../outside','WORD/document.xml']: with self.assertRaises(OfficeCoverageError):extract(package(extra_info=extra)) with self.assertRaises(OfficeCoverageError):extract(package(main=xml('document',W,'A'*100000))) def test_relationship_target_missing_escape_id_duplicate_and_role_mismatch_rejected(self): for items in [[('x',R+'/styles','missing.xml',None)],[('x',R+'/styles','../../../escape.xml',None)],[('x',R+'/styles','document.xml',None)],[('x',R+'/officeDocument','document.xml',None),('x',R+'/officeDocument','document.xml',None)]]: with self.assertRaises(OfficeCoverageError):extract(package(relationships={'word/_rels/document.xml.rels':items})) def test_dtd_and_bad_xml_are_rejected(self): for payload in [b']>&e;',b'']: with self.assertRaises(Exception):extract(package(main=payload)) def test_comments_and_processing_instructions_are_not_silently_discarded(self): result=extract(package(main=xml('document',W,'

Public

'))) self.assertTrue(result['complete']);self.assertIn('COMMENT-SECRET',result['text']) result=extract(package(main=xml('document',W,''))) self.assertFalse(result['complete']);self.assertIn('PI-SECRET',result['text']) def test_simple_formula_cache_covered_dynamic_dde_and_missing_cache_incomplete(self): def sheet(formula,cache):return xml('worksheet',S,''+formula+''+cache+'') for formula,cache,complete in [('SUM(1,2)','3',True),('IF(1,"CACHE-SECRET","Public")','CACHE-SECRET',True),('CHAR(83)&CHAR(69)','SE',False),('WEBSERVICE("https://host")','Public',False),("cmd|'/c calc'!A0",'0',False),('SUM(1,2)','',False)]: result=extract(package('xlsx',{'xl/worksheets/sheet1.xml':(TYPE_PREFIX+'spreadsheetml.worksheet+xml',sheet(formula,cache))})) self.assertEqual(result['complete'],complete,formula) if cache:self.assertIn(cache,result['text']) def test_trailing_data_and_local_extra_fields_cannot_claim_coverage(self): with self.assertRaises(OfficeCoverageError):extract(package()+b'TRAILING-SECRET') info=zipfile.ZipInfo('word/extra.bin');info.extra=b'\x01\x00\x03\x00ABC' with self.assertRaises(OfficeCoverageError):extract(package(extra_info=info)) def test_aggregate_stages_have_real_parent_timing_and_no_document_content(self): parts={'word/header'+str(index)+'.xml':(TYPE_PREFIX+'wordprocessingml.header+xml',xml('hdr',W,'

SYNTHETIC-PRIVATE-CONTENT

')) for index in range(90)} with collect({'diagnostics':{'enabled':True,'mode':'debug'}}) as state: with stage('parse'):result=extract(package(parts=parts)) self.assertTrue(result['complete']);proof=metadata(state);self.assertIs(validated(proof),proof) self.assertEqual([record['stage'] for record in proof['records']],['parse','office.archive','office.xml','office.relationships','office.ocr']) parent=proof['records'][0] for record in proof['records'][1:]: self.assertEqual(record['parentId'],parent['id']);self.assertGreaterEqual(record['startOffsetMs'],parent['startOffsetMs']);self.assertLessEqual(record['startOffsetMs']+record['durationMs'],parent['startOffsetMs']+parent['durationMs']);self.assertEqual(record['status'],'ok') self.assertNotIn('SYNTHETIC-PRIVATE-CONTENT',json.dumps(proof));self.assertNotIn('header90.xml',json.dumps(proof)) def test_error_stages_preserve_correlation_without_exception_text(self): def bad_ocr(_payload):raise ValueError('SYNTHETIC-PRIVATE-ERROR') cases=[(package()+b'TAIL',None,'office.archive'),(package(main=b''),None,'office.xml'),(package(relationships={'word/_rels/document.xml.rels':[('x',R+'/styles','missing.xml',None)]}),None,'office.relationships'),(package(parts={'word/media/image.png':('image/png',PNG)}),bad_ocr,'office.ocr')] for data,callback,expected in cases: with collect({'diagnostics':{'enabled':True,'mode':'debug'}}) as state: with self.assertRaises(Exception): with stage('parse'):extract(data,callback or (lambda _image:('',True))) proof=metadata(state);self.assertIs(validated(proof),proof);self.assertEqual(proof['records'][0]['status'],'error') record=next(record for record in proof['records'] if record['stage']==expected);self.assertEqual(record['status'],'error');self.assertEqual(record['parentId'],1);self.assertNotIn('SYNTHETIC-PRIVATE-ERROR',json.dumps(proof)) def test_disabled_or_operational_diagnostics_do_not_change_extraction(self): for policy in [{},{'diagnostics':{'enabled':False,'mode':'debug'}},{'diagnostics':{'enabled':True,'mode':'operational'}}]: with collect(policy) as state:result=extract(package()) self.assertTrue(result['complete']);self.assertIsNone(metadata(state)) def test_bounds_cannot_return_complete(self): data=package() for limits in [{'max_bytes':len(data)-1},{'max_parts':1},{'max_text':10}]: with self.assertRaises(OfficeCoverageError):extract(data,**limits) if __name__=='__main__':unittest.main()