import base64 import hashlib import io from pathlib import Path import struct import unittest import zipfile from engine import scan_download from office_macros import bounded_decompress, macro_payloads, MAX_CODE def fixture(): path = Path(__file__).resolve().parents[2] / 'tests/fixtures/office/benign-vba-project.base64.txt' data = base64.b64decode(path.read_text(), validate=False) assert hashlib.sha256(data).hexdigest() == '0ced1464b3677e98f5e3a8c5d80135e18dc98dca39299f1a8cfd2a00999fbf9f' return data def office(project=None, extra=None): output = io.BytesIO() with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as archive: archive.writestr('xl/workbook.xml', '') if project is not None: archive.writestr('xl/vbaProject.bin', project) if extra: archive.writestr(*extra) return output.getvalue() def policy(source='rule clean { condition: false }', enabled=True): return {'security': {'yara': {'enabled': True, 'scanOfficeMacros': enabled, 'rules': [{'id': 'macro-marker', 'source': source}]}}} class OfficeMacroTests(unittest.TestCase): def test_real_compressed_source_is_scanned(self): data = office(fixture()) self.assertNotIn(b'Hello from Python!', data) result = scan_download(data, policy('rule source_marker { strings: $m = "Hello from Python!" condition: $m }')) self.assertEqual(result['decision'], 'block') self.assertEqual(result['ruleId'], 'macro-marker') self.assertEqual(result['officeMacros']['modules'], 5) self.assertTrue(result['officeMacros']['present']) self.assertNotIn('Hello from Python!', str(result)) def test_benign_macros_are_not_blanket_blocked(self): result = scan_download(office(fixture()), policy()) self.assertEqual(result['decision'], 'allow') self.assertTrue(result['officeMacros']['complete']) def test_corrupt_project_fails_closed_and_can_be_disabled(self): data = office(b'bad project') self.assertEqual(scan_download(data, policy())['ruleId'], 'office-macro-inspection-failed') self.assertEqual(scan_download(data, policy(enabled=False))['decision'], 'allow') def test_macro_free_office(self): summary, payloads = macro_payloads(office()) self.assertEqual(summary, {'office': True, 'present': False, 'modules': 0, 'complete': True}) self.assertEqual(payloads, []) def test_legacy_project(self): self.assertEqual(macro_payloads(fixture())[0]['modules'], 5) def test_unsupported_embedded_or_xlm(self): for name in ('xl/macrosheets/sheet1.xml', 'word/embeddings/object.bin'): with self.subTest(name=name): self.assertEqual(scan_download(office(extra=(name, 'x')), policy())['decision'], 'block') def test_strict_compression_and_size_limits(self): self.assertEqual(bounded_decompress(b'\x01\x03\xb0\x00abc'), b'abc') for data in (b'', b'\x01\x03\xb0\x00a', b'\x01\x03\xb0\x01\xff\xff', b'\x01' + (struct.pack('