diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 00471a527..d31e50507 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -41,6 +41,7 @@ jobs: run: | pytest --cov=src coveralls --service=github + ls -al flake8: runs-on: ubuntu-latest diff --git a/src/ms_ovba/Models/Entities/module_base.py b/src/ms_ovba/Models/Entities/module_base.py index 4d2f0b399..d871c0002 100644 --- a/src/ms_ovba/Models/Entities/module_base.py +++ b/src/ms_ovba/Models/Entities/module_base.py @@ -19,7 +19,7 @@ def __init__(self: T, name: str) -> None: self.streamName = DoubleEncodedString([0x001A, 0x0032], name) self.docString = DoubleEncodedString([0x001C, 0x0048], "") self.helpContext = IdSizeField(0x001E, 4, 0) - self.cookie = IdSizeField(0x002C, 2, 0xFFFF) + self._cookie = IdSizeField(0x002C, 2, 0xFFFF) # self.readonly = SimpleRecord(0x001E, 4, helpContext) # self.private = SimpleRecord(0x001E, 4, helpContext) @@ -32,55 +32,69 @@ def __init__(self: T, name: str) -> None: self._size = 0 # GUIDs - self._guid = [] + self._guids = [] - def set_guid(self: T, guid: str) -> None: + @property + def guids(self: T) -> str: + return self._guids + + @guids.setter + def guids(self: T, guid: str) -> None: if isinstance(guid, list): - self._guid = guid + self._guids = guid else: - self._guid = [guid] + self._guids = [guid] def add_guid(self: T, guid: str) -> None: - self._guid += guid - - def set_cache(self: T, cache: bytes) -> None: - self._cache = cache + """ + Append a guid to the list + """ + self._guid += [guid] - def get_cache(self: T) -> bytes: + @property + def cache(self: T) -> bytes: return self._cache - def set_cookie(self: T, value: int) -> None: - self.cookie = IdSizeField(0x002C, 2, value) + @cache.setter + def cache(self: T, cache: bytes) -> None: + self._cache = cache + + @property + def cookie(self: T) -> int: + return self._cookie.value - def get_cookie(self: T) -> int: - return self.cookie.value + @cookie.setter + def cookie(self: T, value: int) -> None: + self._cookie = IdSizeField(0x002C, 2, value) - def get_name(self: T) -> str: + @property + def name(self: T) -> str: return self.modName.value - def get_bin_path(self: T) -> str: + @property + def bin_path(self: T) -> str: return self._file_path + ".bin" def add_workspace(self: T, val1: int, val2: int, val3: int, val4: int, val5: int) -> None: self.workspace = [val1, val2, val3, val4, val5] - def pack(self: T, codepage_name: str, endien: str) -> bytes: + def pack(self: T, endien: str, cp_name: str) -> bytes: """ Pack the metadata for use in the dir stream. """ typeid_value = 0x0022 if self.type == 'Document' else 0x0021 type_id = PackedData("HI", typeid_value, 0) self.offsetRec = IdSizeField(0x0031, 4, len(self._cache)) - output = (self.modName.pack(codepage_name, endien) - + self.streamName.pack(codepage_name, endien) - + self.docString.pack(codepage_name, endien) - + self.offsetRec.pack(codepage_name, endien) - + self.helpContext.pack(codepage_name, endien) - + self.cookie.pack(codepage_name, endien) - + type_id.pack(codepage_name, endien)) + output = (self.modName.pack(endien, cp_name) + + self.streamName.pack(endien, cp_name) + + self.docString.pack(endien, cp_name) + + self.offsetRec.pack(endien) + + self.helpContext.pack(endien, cp_name) + + self._cookie.pack(endien) + + type_id.pack(endien)) footer = PackedData("HI", 0x002B, 0) - output += footer.pack(codepage_name, endien) + output += footer.pack(endien) return output def to_project_module_string(self: T) -> str: diff --git a/src/ms_ovba/Models/Entities/reference.py b/src/ms_ovba/Models/Entities/reference.py index a78bc90eb..a9b9d8368 100644 --- a/src/ms_ovba/Models/Entities/reference.py +++ b/src/ms_ovba/Models/Entities/reference.py @@ -13,21 +13,18 @@ class Reference(): """ 2.3.4.2.2.1 REFERENCE Record """ - def __init__(self: T, codepage_name: str, - ref: ReferenceRecord, + def __init__(self: T, ref: ReferenceRecord, name: str = None) -> None: - # is self._codepage_name even needed? - self._codepage_name = codepage_name self._ref = ref self._refname = name - def pack(self: T, cp_name: str, endien: str) -> bytes: + def pack(self: T, endien: str, cp_name: str) -> bytes: name_pack = b'' if self._refname is not None: name_de = DoubleEncodedString([0x0016, 0x003E], self._refname) - name_pack = name_de.pack(cp_name, endien) + name_pack = name_de.pack(endien, cp_name) - return name_pack + self._ref.pack(cp_name, endien) + return name_pack + self._ref.pack(endien, cp_name) @staticmethod def unpack(data: bytes, endien: str) -> T: diff --git a/src/ms_ovba/Models/Entities/reference_project.py b/src/ms_ovba/Models/Entities/reference_project.py index c95aea2b7..202656fc8 100644 --- a/src/ms_ovba/Models/Entities/reference_project.py +++ b/src/ms_ovba/Models/Entities/reference_project.py @@ -1,6 +1,6 @@ +import struct from ms_ovba.Models.Entities.reference_record import ReferenceRecord from ms_ovba.Models.Fields.project_reference import ProjectReference -from ms_ovba.Models.Fields.packed_data import PackedData from typing import TypeVar @@ -9,23 +9,19 @@ class ReferenceProject(ReferenceRecord): - def __init__(self: T, codepage_name: str, - ref: ProjectReference) -> None: - # is self._codepage_name even needed? - self._codepage_name = codepage_name + def __init__(self: T, ref: ProjectReference) -> None: self._ref = ref - def pack(self: T, cp_name: str, endien: str) -> bytes: + def pack(self: T, endien: str, cp_name: str) -> bytes: + endien_symbol = '<' if endien == 'little' else '>' lib_rel = self._ref.relative() libid_abs_size = len(self._ref) libid_rel_size = len(lib_rel) - format = ("HII" + str(libid_abs_size) + "sI" + + format = (endien_symbol + "HII" + str(libid_abs_size) + "sI" + str(libid_rel_size) + "sIH") ref_str = str(self._ref).encode(cp_name) ref_str_rel = str(lib_rel).encode(cp_name) - ref_project = PackedData(format, 0x000E, - libid_abs_size + libid_rel_size + 14, - libid_abs_size, ref_str, libid_rel_size, - ref_str_rel, 0x65BE0257, 0x0017) - - return (ref_project.pack(cp_name, endien)) + return struct.pack(format, 0x000E, + libid_abs_size + libid_rel_size + 14, + libid_abs_size, ref_str, libid_rel_size, + ref_str_rel, 0x65BE0257, 0x0017) diff --git a/src/ms_ovba/Models/Entities/reference_registered.py b/src/ms_ovba/Models/Entities/reference_registered.py index 5b358a6dc..b72508303 100644 --- a/src/ms_ovba/Models/Entities/reference_registered.py +++ b/src/ms_ovba/Models/Entities/reference_registered.py @@ -1,7 +1,6 @@ import struct from ms_ovba.Models.Entities.reference_record import ReferenceRecord from ms_ovba.Models.Fields.libid_reference import LibidReference -from ms_ovba.Models.Fields.packed_data import PackedData from typing import TypeVar @@ -13,24 +12,20 @@ class ReferenceRegistered(ReferenceRecord): 2.3.4.2.2.5 Specifies a reference to an Automation type library. """ - def __init__(self: T, codepage_name: str, - libid_ref: LibidReference) -> None: - # is self._codepage_name even needed? - self._codepage_name = codepage_name + def __init__(self: T, libid_ref: LibidReference) -> None: self._libid_ref = libid_ref @property def libid(self: T) -> LibidReference: return self._libid_ref - def pack(self: T, cp_name: str, endien: str) -> bytes: + def pack(self: T, endien: str, cp_name: str) -> bytes: + endien_symbol = '<' if endien == 'little' else '>' strlen = len(self._libid_ref) - format = "HII" + str(strlen) + "sIH" + format = endien_symbol + "HII" + str(strlen) + "sIH" lib_str = str(self._libid_ref).encode(cp_name) - ref_registered = PackedData(format, 0x000D, strlen + 10, - strlen, lib_str, 0, 0) - - return ref_registered.pack(cp_name, endien) + return struct.pack(format, 0x000D, strlen + 10, + strlen, lib_str, 0, 0) @staticmethod def unpack(data: bytes, endien: str) -> T: @@ -66,4 +61,4 @@ def unpack(data: bytes, endien: str) -> T: pass libid_ref = LibidReference.unpack(libid_ref_bytes) - return ReferenceRegistered("", libid_ref) + return ReferenceRegistered(libid_ref) diff --git a/src/ms_ovba/Models/Fields/doubleEncodedString.py b/src/ms_ovba/Models/Fields/doubleEncodedString.py index 09c43166c..52332a4b1 100644 --- a/src/ms_ovba/Models/Fields/doubleEncodedString.py +++ b/src/ms_ovba/Models/Fields/doubleEncodedString.py @@ -12,13 +12,17 @@ class DoubleEncodedString(): """ def __init__(self: T, ids: list, text: str) -> None: self.ids = ids - self.value = text + self._value = text - def pack(self: T, codepage_name: str, endien: str) -> bytes: - encoded = self.value.encode(codepage_name) + @property + def value(self) -> str: + return self._value + + def pack(self: T, endien: str, cp_name: str) -> bytes: + encoded = self._value.encode(cp_name) self.mod_name1 = IdSizeField(self.ids[0], len(encoded), encoded) format = "utf_16_le" if endien == 'little' else "utf_16_be" - encoded = self.value.encode(format) + encoded = self._value.encode(format) self.mod_name2 = IdSizeField(self.ids[1], len(encoded), encoded) - return (self.mod_name1.pack(codepage_name, endien) - + self.mod_name2.pack(codepage_name, endien)) + return (self.mod_name1.pack(endien) + + self.mod_name2.pack(endien)) diff --git a/src/ms_ovba/Models/Fields/idSizeField.py b/src/ms_ovba/Models/Fields/idSizeField.py index cc6b5f061..6a1b5cae8 100644 --- a/src/ms_ovba/Models/Fields/idSizeField.py +++ b/src/ms_ovba/Models/Fields/idSizeField.py @@ -16,14 +16,17 @@ def __init__(self: T, id: int, size: int, value: Any) -> None: self._size = size self._value = value - def pack(self: T, codepage_name: str, endien: str) -> bytes: + @property + def value(self: T) -> Any: + return self._value + + def pack(self: T, endien: str, cp_name: str = None) -> bytes: endien_symbol = '<' if endien == 'little' else '>' format = endien_symbol + "HI" if isinstance(self._value, str): self.stringValue = self._value self._value = bytes(self._value, encoding="ascii") - format += str(self._size) + "s" - elif isinstance(self._value, bytes): + if isinstance(self._value, bytes): format += str(self._size) + "s" elif self._size == 2: format += "H" diff --git a/src/ms_ovba/Models/Fields/packed_data.py b/src/ms_ovba/Models/Fields/packed_data.py index 7ee87ae38..70e8430f8 100644 --- a/src/ms_ovba/Models/Fields/packed_data.py +++ b/src/ms_ovba/Models/Fields/packed_data.py @@ -7,12 +7,14 @@ class PackedData(): """ - Multivalue field with a packing format + Multivalue field with a packing format. + This class allows a user to define a data format, + and render it at a later time. """ def __init__(self: T, format: str, *values: Any) -> None: self.values = values self.format = format - def pack(self: T, codepage_name: str, endien: str) -> bytes: + def pack(self: T, endien: str, cp_name: str = None) -> bytes: endien_symbol = '<' if endien == 'little' else '>' return struct.pack(endien_symbol + self.format, *self.values) diff --git a/src/ms_ovba/Views/dirStream.py b/src/ms_ovba/Views/dirStream.py index db22fb96f..f0cf29191 100644 --- a/src/ms_ovba/Views/dirStream.py +++ b/src/ms_ovba/Views/dirStream.py @@ -19,30 +19,30 @@ class DirStream(): def __init__(self: T, project: VbaProject) -> None: self.project = project - self._include_compat = False + self._include_compat = project.compat def to_bytes(self: T) -> bytes: information = self._load_information() endien = self.project.endien - codepage_name = self.project.get_codepage_name() + cp_name = self.project.codepage_name pack_symbol = '<' if endien == 'little' else '>' # should be 0xFFFF - cookie_value = self.project.get_project_cookie() + cookie_value = self.project.project_cookie self.project_cookie = IdSizeField(19, 2, cookie_value) references = self.project.references modules = self.project.modules output = b'' for record in information: - output += record.pack(codepage_name, endien) + output += record.pack(endien, cp_name) for record in references: - output += record.pack(codepage_name, endien) + output += record.pack(endien, cp_name) modules_header = IdSizeField(0x000F, 2, len(modules)) - output += (modules_header.pack(codepage_name, endien) - + self.project_cookie.pack(codepage_name, endien)) + output += (modules_header.pack(endien) + + self.project_cookie.pack(endien)) for record in modules: - output += record.pack(codepage_name, endien) + output += record.pack(endien, cp_name) output += struct.pack(pack_symbol + "HI", 16, 0) return output diff --git a/src/ms_ovba/Views/project.py b/src/ms_ovba/Views/project.py index 5f6eff9de..6c03fe844 100644 --- a/src/ms_ovba/Views/project.py +++ b/src/ms_ovba/Views/project.py @@ -16,7 +16,7 @@ def __init__(self: T, project: VbaProject) -> None: # Attributes # A list of attributes and values - self.attributes = {} + self.attributes = project.attributes # The HostExtenderInfo string guid = "{3832D640-CF90-11CF-8E43-00A0C911005A}" @@ -27,10 +27,10 @@ def add_attribute(self: T, name: str, value: str) -> None: def to_bytes(self: T) -> bytes: project = self.project - codepage_name = project.get_codepage_name() + codepage_name = project.codepage_name # Use \x0D0A line endings...however python encodes that. eol = b'\x0D\x0A' - project_id = project.get_project_id() + project_id = project.project_id id = bytearray(project_id, codepage_name) result = b'ID="' + id + b'"' + eol modules = project.modules @@ -42,10 +42,10 @@ def to_bytes(self: T) -> bytes: result += self._attr(key, self.attributes[key]) cmg = ms_ovba_crypto.encrypt( project_id, - project.get_protection_state() + project.protection_state ) - dpb = ms_ovba_crypto.encrypt(project_id, project.get_password()) - gc = ms_ovba_crypto.encrypt(project_id, project.get_visibility_state()) + dpb = ms_ovba_crypto.encrypt(project_id, project.password) + gc = ms_ovba_crypto.encrypt(project_id, project.visibility_state) result += (bytes('CMG="', codepage_name) + binascii.hexlify(cmg).upper() + b'\x22\x0D\x0A') @@ -74,7 +74,7 @@ def write_file(self: T) -> None: bin_f.close() def _attr(self: T, name: str, value: str) -> str: - codepage_name = self.project.get_codepage_name() + codepage_name = self.project.codepage_name eol = b'\x0D\x0A' b_name = bytes(name, codepage_name) b_value = bytes(value, codepage_name) diff --git a/src/ms_ovba/Views/project_ole_file.py b/src/ms_ovba/Views/project_ole_file.py index b88c52e26..86c07fc5b 100644 --- a/src/ms_ovba/Views/project_ole_file.py +++ b/src/ms_ovba/Views/project_ole_file.py @@ -33,7 +33,7 @@ def _build_ole_directory(project: VbaProject) -> RootDirectory: storage.set_modified(project.default_date) for module in project.get_modules(): module.write_file() - dir = StreamDirectory(module.get_name(), module.get_bin_path()) + dir = StreamDirectory(module.name, module.bin_path) storage.add_directory(dir) module = DirStream(project) @@ -48,7 +48,7 @@ def _build_ole_directory(project: VbaProject) -> RootDirectory: directory.add_directory(storage) - if project.get_include_projectwm(): + if project.projectwm: module = ProjectWm(project) module.write_file() stream = StreamDirectory("PROJECTwm", "projectwm.bin") diff --git a/src/ms_ovba/Views/project_view.py b/src/ms_ovba/Views/project_view.py index 865d0d596..b030a1287 100644 --- a/src/ms_ovba/Views/project_view.py +++ b/src/ms_ovba/Views/project_view.py @@ -10,12 +10,9 @@ class ProjectView: """ The _VBA_PROJECT data view for the vbaProject """ - def __init__(self: T, project: VbaProject) -> None: + def __init__(self: T, project: VbaProject, reserved: int = 3) -> None: self.project = project - self._reserved3 = 0x0003 - - def set_reserved3(self: T, value: int) -> None: - self._reserved3 = value + self._reserved3 = reserved def to_bytes(self: T) -> bytes: endien_symbol = '<' if self.project.endien == 'little' else '>' @@ -23,11 +20,11 @@ def to_bytes(self: T) -> bytes: output = b'' reserved1 = 0x61CC reserved2 = 0x00 - cache_version = self.project.get_performance_cache_version() + cache_version = self.project.performance_cache_version output += struct.pack(format, reserved1, cache_version, reserved2, self._reserved3) - return output + self.project.get_performance_cache() + return output + self.project.performance_cache def write_file(self: T) -> None: bin_f = open("vba_project.bin", "wb") diff --git a/src/ms_ovba/vbaProject.py b/src/ms_ovba/vbaProject.py index a610f4f5b..bad5457f3 100644 --- a/src/ms_ovba/vbaProject.py +++ b/src/ms_ovba/vbaProject.py @@ -29,9 +29,13 @@ def __init__(self: T) -> None: self.references = [] self.modules = [] + # A list of attributes and values + self.attributes = {} + self._project_cookie = 0xFFFF self._project_wm = False + self._compat = False self._default_date = Filetime.from_msfiletime(0x0000000000000000) # Getters and Setters @@ -43,19 +47,28 @@ def default_date(self: T) -> Filetime: def default_date(self: T, date: Filetime) -> None: self._default_date = date - def set_project_id(self: T, id: str) -> None: + @property + def project_id(self: T) -> str: + return self._project_id + + @project_id.setter + def project_id(self: T, id: str) -> None: self._project_id = id - def get_project_id(self: T) -> str: - return self._project_id + @property + def protection_state(self: T) -> int: + return self._protection_state - def set_protection_state(self: T, state: int) -> None: + @protection_state.setter + def protection_state(self: T, state: int) -> None: self._protection_state = state - def get_protection_state(self: T) -> int: - return self._protection_state + @property + def visibility_state(self: T) -> bytes: + return self._visibility_state - def set_visibility_state(self: T, state: int) -> None: + @visibility_state.setter + def visibility_state(self: T, state: int) -> None: """ 0 = not visible 255 = visible @@ -64,48 +77,75 @@ def set_visibility_state(self: T, state: int) -> None: raise Exception("Bad visibility value.") self._visibility_state = state - def get_visibility_state(self: T) -> bytes: - return self._visibility_state + @property + def password(self: T) -> bytes: + return self._password - def set_password(self: T, value: bytes) -> None: + @password.setter + def password(self: T, value: bytes) -> None: self._password = value - def get_password(self: T) -> bytes: - return self._password + @property + def performance_cache(self: T) -> bytes: + return self._performance_cache - def set_performance_cache(self: T, cache: bytes) -> None: + @performance_cache.setter + def performance_cache(self: T, cache: bytes) -> None: self._performance_cache = cache - def get_performance_cache(self: T) -> None: - return self._performance_cache + @property + def performance_cache_version(self: T) -> int: + return self._performance_cache_version - def set_performance_cache_version(self: T, version: int) -> None: + @performance_cache_version.setter + def performance_cache_version(self: T, version: int) -> None: self._performance_cache_version = version - def get_performance_cache_version(self: T) -> int: - return self._performance_cache_version - - def get_codepage_name(self: T) -> str: + @property + def codepage_name(self: T) -> str: return self._codepage_name - def set_project_cookie(self: T, value: int) -> None: - self._project_cookie = value + @codepage_name.setter + def codepage_name(self: T, name: str) -> None: + self._codepage_name = name - def get_project_cookie(self: T) -> int: + @property + def project_cookie(self: T) -> int: return self._project_cookie + @project_cookie.setter + def project_cookie(self: T, value: int) -> None: + self._project_cookie = value + def get_modules(self: T) -> list: return self.modules - def set_include_projectwm(self: T, value: bool) -> None: - self._project_wm = value - - def get_include_projectwm(self: T) -> bool: + @property + def projectwm(self: T) -> bool: return self._project_wm + def include_projectwm(self: T) -> bool: + self._project_wm = True + + def exclude_projectwm(self: T) -> bool: + self._project_wm = False + + @property + def compat(self: T) -> bool: + return self._compat + + def include_compat(self: T) -> None: + self._compat = True + + def exclude_compat(self: T) -> None: + self._compat = False + # Appenders def add_module(self: T, mod: ModuleBase) -> None: self.modules.append(mod) def add_reference(self: T, ref: ReferenceRegistered) -> None: self.references.append(ref) + + def add_attribute(self: T, name: str, value: str) -> None: + self.attributes[name] = value diff --git a/tests/Functional/test_dirObject.pyt b/tests/Functional/test_dirObject.pyt deleted file mode 100644 index 7a86f26a2..000000000 --- a/tests/Functional/test_dirObject.pyt +++ /dev/null @@ -1,109 +0,0 @@ -import struct -import uuid -from ms_ovba_compression.ms_ovba import MsOvba -from ms_pcode_assembler.module_cache import ModuleCache -from ms_ovba.vbaProject import VbaProject -from ms_ovba.Views.dirStream import DirStream -from ms_ovba.Models.Fields.libid_reference import LibidReference -from ms_ovba.Models.Entities.doc_module import DocModule -from ms_ovba.Models.Entities.std_module import StdModule -from ms_ovba.Models.Entities.reference import ( - Reference -) -from ms_ovba.Models.Entities.reference_registered import ( - ReferenceRegistered -) - - -def test_dirstream() -> None: - ''' - The cache is not yet tested. - ''' - module_cache = ModuleCache(0xB5, 0x08F3, signature=3) - # Read the data from the demo file and decompress it. - f = open('tests/blank/vbaProject.bin', 'rb') - offset = 0x1EC0 - length = 0x0232 - f.seek(offset) - container = f.read(length) - ms_ovba = MsOvba() - decompressed_stream = ms_ovba.decompress(container) - - # Create a project with the same attributes - project = VbaProject() - stream = DirStream(project) - stream.include_compat() - codepage = 0x04E4 - codepage_name = "cp" + str(codepage) - guid = uuid.UUID('0002043000000000C000000000000046') - libid_ref = ReferenceRegistered(codepage_name, LibidReference( - guid, - "2.0", - "0", - "C:\\Windows\\System32\\stdole2.tlb", - "OLE Automation" - )) - ole_reference = Reference(codepage_name, libid_ref, "stdole") - guid = uuid.UUID('2DF8D04C5BFA101BBDE500AA0044DE52') - libid_ref2 = ReferenceRegistered(codepage_name, LibidReference( - guid, - "2.0", - "0", - "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE16\\MSO.DLL", - "Microsoft Office 16.0 Object Library" - )) - office_reference = Reference(codepage_name, libid_ref2, "Office") - project.add_reference(ole_reference) - project.add_reference(office_reference) - project.set_project_cookie(0x08F3) - - indirect_table = ("02 80 FE FF FF FF FF FF 20 00 00 00 FF FF FF FF", - "30 00 00 00 02 01 FF FF 00 00 00 00 00 00 00 00", - "FF FF FF FF FF FF FF FF 00 00 00 00 2E 00 43 00", - "1D 00 00 00 25 00 00 00 FF FF FF FF 40 00 00 00") - module_cache.indirect_table = bytes.fromhex(" ".join(indirect_table)) - object_table = ("02 00 53 4C FF FF FF FF 00 00 01 00 53 10 FF FF", - "FF FF 00 00 01 00 53 94 FF FF FF FF 00 00 00 00", - "02 3C FF FF FF FF 00 00") - module_cache.object_table = bytes.fromhex(" ".join(object_table)) - module_cache.misc = [[-1, 0x18], 0xFF, 0, [1, "00000000"]] - module_cache.header.data2 = 0x0123 - module_cache.header.data3 = 0x88 - module_cache.header.sata4 = 8 - this_workbook = DocModule("ThisWorkbook") - this_workbook.set_cookie(0xB81C) - module_cache.cookie = 0xB81C - guid = uuid.UUID('0002081900000000C000000000000046') - this_workbook.set_guid(guid) - module_cache.guid = [guid] - this_workbook.set_cache(module_cache.to_bytes()) - - sheet1 = DocModule("Sheet1") - sheet1.set_cookie(0x9B9A) - module_cache.cookie = 0x9B9A - guid = uuid.UUID('0002082000000000C000000000000046') - module_cache.guid = [guid] - sheet1.set_guid(guid) - sheet1.set_cache(module_cache.to_bytes()) - - module1 = StdModule("Module1") - module1.set_cookie(0xB241) - module_cache.clear_variables() - module_cache.cookie = 0xB241 - module_cache.misc = [[-1, 2], 0xFFFF, 0, [0, "FFFFFFFF"]] - module_cache.header.data2 = 3 - module_cache.header.data3 = 0 - module_cache.header.sata4 = 7 - module_cache.indirect_table = struct.pack(" None: + cls._rand = seeds + + @classmethod + def randint(cls: Type[T], param1: int, param2: int) -> int: + return cls._rand.pop(0) + + +@pytest.fixture(autouse=True) +def run_around_tests() -> None: + # Code that will run before your test, for example: + + # A test function will be run at this point + yield + # Code that will run after your test + root = "src/ms_ovba/blank_files/" + root2 = "tests/blank/" + names = [root + "ThisWorkbook.cls", root + "Sheet1.cls", + root2 + "Module1.bas"] + remove_module(names) + names = ["dir.bin", "projectwm.bin", "project.bin", "vba_project.bin"] + map(os.remove, names) + + +def remove_module(names: str) -> None: + for name in names: + os.remove(name + ".new") + os.remove(name + ".bin") + + +def assert_module_matches_bin(module_path: str, + cache_size: int, + bin_path: str, + bin_offset: int, + bin_length: int) -> bool: + m = open(module_path, "rb") + b = open(bin_path, "rb") + b.seek(bin_offset) + assert m.read(cache_size) == b.read(cache_size) + ms_ovba = MsOvba() + m_uncompressed = ms_ovba.decompress(m.read()) + b_uncompressed = ms_ovba.decompress(b.read(bin_length - cache_size)) + assert m_uncompressed == b_uncompressed + + +stdole_lib = LibidReference( + uuid.UUID("00020430-0000-0000-C000-000000000046"), + "2.0", + "0", + "C:\\Windows\\System32\\stdole2.tlb", + "OLE Automation" + ) + + +@unittest.mock.patch('random.randint', NotSoRandom.randint) +def test_full_file() -> None: + """ + Create an exact reproduction of a complete "empty" vba excel addin. + """ + rand = [0x41, 0xBC, 0x7B, 0x7B, 0x37, 0x7B, 0x7B, 0x7B] + NotSoRandom.set_seed(rand) + project = VbaProject() + project.default_date = Filetime.from_msfiletime(0x01D92433C2B823C0) + project.include_projectwm() + libid_ref = ReferenceRegistered(stdole_lib) + ole_reference = Reference(libid_ref, "stdole") + libid_ref2 = ReferenceRegistered(LibidReference( + uuid.UUID("2DF8D04C5BFA101BBDE500AA0044DE52"), + "2.0", + "0", + "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE16\\MSO.DLL", + "Microsoft Office 16.0 Object Library" + )) + office_reference = Reference(libid_ref2, "Office") + project.add_reference(ole_reference) + project.add_reference(office_reference) + proj_cookie = 0x08F3 + project.project_cookie = proj_cookie + project.project_id = '{9E394C0B-697E-4AEE-9FA6-446F51FB30DC}' + project.performance_cache_version = 0x00B5 + + base_path = "src/ms_ovba/blank_files/" + # Add Modules + this_workbook = create_doc_module(project, "ThisWorkbook", 0xB81C, + "0002081900000000C000000000000046", + base_path + "ThisWorkbook.cls") + + sheet1 = create_doc_module(project, "Sheet1", 0x9B9A, + "0002082000000000C000000000000046", + base_path + "Sheet1.cls") + + module1 = StdModule("Module1") + cookie = 0xB241 + module1.cookie = cookie + module_cache = ModuleCache(0xB5, proj_cookie, signature=3) + module_cache.misc = [[-1, 2], 0xFFFF, 0, [0, "FFFFFFFF"]] + module_cache.header.data2 = 0 + module_cache.header.data3 = 3 + module_cache.header.data4 = 0 + module_cache.indirect_table = struct.pack(" bytes: + cache = ProjectCache(0x04E4, proj_cookie, 0x65BE0257) + cache._hex = 0x65BE0257 + module_array = [] + i = 0 + id = [0x227, 0x22B, 0x22C] + for module in modules: + hex = 0x65BE0263 if i == 2 else cache._hex + module_array.append( + (module.name, 50, 70 + i, hex, id[i], + module.cookie, len(module.cache), [], -1) + ) + i += 1 + + cache._modules = module_array + + cache.add_library(str(LibidReference( + uuid.UUID("000204EF-0000-0000-C000-000000000046"), + "4.2", + "9", + "C:\\Program Files\\Common Files\\Microsoft Shared\\VBA" + "\\VBA7.1\\VBE7.DLL", + "Visual Basic For Applications" + ))) + cache.add_library(str(LibidReference( + uuid.UUID("00020813-0000-0000-C000-000000000046"), + "1.9", + "0", + "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE", + "Microsoft Excel 16.0 Object Library" + ))) + cache.add_library(str(stdole_lib)) + cache.add_library(str(LibidReference( + uuid.UUID("2DF8D04C-5BFA-101B-BDE5-00AA0044DE52"), + "2.8", + "0", + "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE16\\MSO.DLL", + "Microsoft Office 16.0 Object Library" + ))) + + # User Class + cache._user = [2, 2, 1] + + # Compile Time Data + cache._compile = [0x0212, 0x010214, 0x010216, + 0x0218, 0x01021a, 0x01021c] + + # Data + cache._data = [0x222, 0xffff, 17, -1, -1, -1, -1, 1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1] + + # Footer? + cache._post_f_data = [(13, 0x230), (14, 0x218), (43, 0x200)] + cache._post_data = [ + b'\xf1q\x9a\xee\xc0\xe0\xc4F\xa2\xf8l|\xf9{s\x06', + b'vS\x9e\xe1B\x85\xfeF\xa1\x8b0E\x08tCU', + b'"\x93\xba>\xc3\x82\xfcD\x88\xcav\x96\xe5\x061"' + ] + cache._post_footer = 0x30 + cache._w0 = 0x117 + cache._w2 = 0x2ba0 + cache._identifiers = [ + (b"Excel", 4, 0x2b80), (b"VBA", 4, 0xe2f7), (b"Win16", 4, 0x7ec1), + (b"Win32", 4, 0x7f07), (b"Win64", 4, 0x7f78), (b"Mac", 4, 0xb2b3), + (b"VBA6", 4, 0x23ad), (b"VBA7", 4, 0x23ae), + (b"Project1", 4, 0x170a), + (b"stdole", 4, 0x6093), (b"VBAProject", 4, 0xbfbe), + (b"Office", 4, 0x7515), (b"ThisWorkbook", 4, 0xe37c), + (b"_Evaluate", 128, 0xd918, 0, 0x103, -1), + (b"Sheet1", 4, 0x1ae8), (b"Module1", 4, 0x1162), + (b"Workbook", 4, 0x186b) + ] + + hex = ("20 02 02 00 FF FF 22 02 FF FF FF FF 24 02 03 00", + "FF FF 27 02 00 00 03 00 FF FF FF FF FF FF 2B 02", + "01 00 03 00 2D 02 02 00 05 00 0E 02 01 00 FF FF", + "10 02 00 00 FF FF FF FF FF FF FF FF FF FF FF FF", + "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF", + "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF") + + hex2 = ("06 00 10 00 00 00 01 00 36 00 00 00 00 00 00 00", + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + "00 00") + + cache._footer = [ + bytes.fromhex(" ".join(hex)), + bytes.fromhex(" ".join(hex2)) + ] + return cache.to_bytes() + + +def create_doc_module(project: VbaProject, name: str, + cookie: int, guid_s: str, path: str) -> DocModule: + mod = DocModule(name) + mod.cookie = cookie + guid = uuid.UUID(guid_s) + mod.add_guid(guid) + module_path = path + mod.add_file(module_path) + mod.normalize_file() + + cache_ver = project.performance_cache_version + proj_cookie = project.project_cookie + module_cache = ModuleCache(cache_ver, proj_cookie, signature=3) + module_cache.header.data2 = 0 + module_cache.header.data3 = 0x123 + module_cache.header.data4 = 0x88 + module_cache.misc = [[-1, 8], 0x18, 0, [1, "00000000"]] + indirect_table = ("02 80 FE FF FF FF FF FF 20 00 00 00 FF FF FF FF", + "30 00 00 00 02 01 FF FF 00 00 00 00 00 00 00 00", + "FF FF FF FF FF FF FF FF 00 00 00 00 2E 00 43 00", + "1D 00 00 00 25 00 00 00 FF FF FF FF 40 00 00 00") + module_cache.indirect_table = bytes.fromhex(" ".join(indirect_table)) + + object_table = [[2, 0x4C53], [1, 0x1053], [1, 0x9453], [0, 0x3C02]] + object_table_bytes = b'' + for entry in object_table: + object_table_bytes += struct.pack(" None: - cls._rand = seeds - - @classmethod - def randint(cls: Type[T], param1: int, param2: int) -> int: - return cls._rand.pop(0) - - -@pytest.fixture(autouse=True) -def run_around_tests() -> None: - # Code that will run before your test, for example: - - # A test function will be run at this point - yield - # Code that will run after your test - root = "src/ms_ovba/blank_files/" - root2 = "tests/blank/" - names = [root + "ThisWorkbook.cls", root + "Sheet1.cls", - root2 + "Module1.bas"] - remove_module(names) - names = ["dir.bin", "projectWm.bin", "project.bin", "vba_project.bin"] - map(os.remove, names) - - -def remove_module(names: str) -> None: - for name in names: - os.remove(name + ".new") - os.remove(name + ".bin") - - -def module_matches_bin(module_path: str, - cache_size: int, - bin_path: str, - bin_offset: int, - bin_length: int) -> bool: - m = open(module_path, "rb") - b = open(bin_path, "rb") - b.seek(bin_offset) - if m.read(cache_size) != b.read(cache_size): - return False - ms_ovba = MsOvba() - m_uncompressed = ms_ovba.decompress(m.read()) - b_uncompressed = ms_ovba.decompress(b.read(bin_length)) - return m_uncompressed == b_uncompressed - - -@unittest.mock.patch('random.randint', NotSoRandom.randint) -def test_full_file() -> None: - rand = [0x41, 0xBC, 0x7B, 0x7B, 0x37, 0x7B, 0x7B, 0x7B] - NotSoRandom.set_seed(rand) - project = VbaProject() - project.default_date = Filetime.from_msfiletime(0x01D92433C2B823C0) - project.set_include_projectwm(True) - codepage = 0x04E4 - codepage_name = "cp" + str(codepage) - libid_ref = ReferenceRegistered(codepage_name, LibidReference( - uuid.UUID("0002043000000000C000000000000046"), - "2.0", - "0", - "C:\\Windows\\System32\\stdole2.tlb", - "OLE Automation" - )) - ole_reference = Reference(codepage_name, libid_ref, "stdole") - libid_ref2 = ReferenceRegistered(codepage_name, LibidReference( - uuid.UUID("2DF8D04C5BFA101BBDE500AA0044DE52"), - "2.0", - "0", - "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE16\\MSO.DLL", - "Microsoft Office 16.0 Object Library" - )) - office_reference = Reference(codepage_name, libid_ref2, "Office") - project.add_reference(ole_reference) - project.add_reference(office_reference) - proj_cookie = 0x08F3 - project.set_project_cookie(proj_cookie) - project.set_project_id('{9E394C0B-697E-4AEE-9FA6-446F51FB30DC}') - project.set_performance_cache(create_cache()) - project.set_performance_cache_version(0x00B5) - - base_path = "src/ms_ovba/blank_files/" - # Add Modules - this_workbook = create_doc_module(project, "ThisWorkbook", 0xB81C, - "0002081900000000C000000000000046", - base_path + "ThisWorkbook.cls") - - sheet1 = create_doc_module(project, "Sheet1", 0x9B9A, - "0002082000000000C000000000000046", - base_path + "Sheet1.cls") - - module1 = StdModule("Module1") - cookie = 0xB241 - module1.set_cookie(cookie) - module_cache = ModuleCache(0xB5, proj_cookie, signature=3) - module_cache.header.data2 = 3 - module_cache.header.data4 = 2 - module_cache.misc = [[-1, 0], 0xFFFF, 0, [0, "FFFFFFFF"]] - module_cache.indirect_table = struct.pack(" bytes: - modules = [] - this_workbook = DocModule("ThisWorkbook") - this_workbook.cookie.value = 0xB81C - modules.append(this_workbook) - sheet1 = DocModule("Sheet1") - sheet1.cookie.value = 0x9B9A - modules.append(sheet1) - module1 = StdModule("Module1") - module1.cookie.value = 0xB241 - modules.append(module1) - - libraries = [] - libraries.append(LibidReference( - uuid.UUID("000204EF-0000-0000-C000-000000000046"), - "4.2", - "9", - "C:\\Program Files\\Common Files\\Microsoft Shared\\VBA" - "\\VBA7.1\\VBE7.DLL", - "Visual Basic For Applications" - )) - libraries.append(LibidReference( - uuid.UUID("00020813-0000-0000-C000-000000000046"), - "1.9", - "0", - "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE", - "Microsoft Excel 16.0 Object Library" - )) - libraries.append(LibidReference( - uuid.UUID("00020430-0000-0000-C000-000000000046"), - "2.0", - "0", - "C:\\Windows\\System32\\stdole2.tlb", - "OLE Automation" - )) - libraries.append(LibidReference( - uuid.UUID("2DF8D04C-5BFA-101B-BDE5-00AA0044DE52"), - "2.8", - "0", - "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE16\\MSO.DLL", - "Microsoft Office 16.0 Object Library" - )) - ca = (b'' - + b'\xFF\x09\x04\x00\x00\x09\x04\x00\x00\xE4\x04\x03\x00\x00\x00\x00' - + b'\x00\x00\x00\x00\x00\x01\x00\x04\x00\x02\x00') - i = 0 - for lib in libraries: - lib_str = bytearray(str(lib), "utf_16_le") - ca += struct.pack(" DocModule: - mod = DocModule(name) - mod.set_cookie(cookie) - guid = uuid.UUID(guid_s) - mod.set_guid(guid) - module_path = path - mod.add_file(module_path) - mod.normalize_file() - - cache_ver = project.get_performance_cache_version() - proj_cookie = project.get_project_cookie() - module_cache = ModuleCache(cache_ver, proj_cookie, signature=3) - module_cache.header.data3 = 0x88 - module_cache.header.data4 = 8 - module_cache.misc = [[-1, 0x18], 0x18, 0, [1, "00000000"]] - indirect_table = ("02 80 FE FF FF FF FF FF 20 00 00 00 FF FF FF FF", - "30 00 00 00 02 01 FF FF 00 00 00 00 00 00 00 00", - "FF FF FF FF FF FF FF FF 00 00 00 00 2E 00 43 00", - "1D 00 00 00 25 00 00 00 FF FF FF FF 40 00 00 00") - module_cache.indirect_table = bytes.fromhex(" ".join(indirect_table)) - - object_table = [[2, 0x4C53], [1, 0x1053], [1, 0x9453], [0, 0x3C02]] - object_table_bytes = b'' - for entry in object_table: - object_table_bytes += struct.pack(" bytes: def test_constructor1() -> None: ref_proj = "" - ref = Reference("cp1", ref_proj) + ref = Reference(ref_proj) assert isinstance(ref, Reference) def test_constructor2() -> None: - ref = Reference("cp1", "", "VBAProject1") + ref = Reference("", "VBAProject1") assert isinstance(ref, Reference) def test_pack() -> None: ref_proj = MockRefProj() - ref = Reference("cp1", ref_proj, "VBAProject1") + ref = Reference(ref_proj, "VBAProject1") expected_hex = ("16 00 0B 00 00 00 56 42 41 50 72 6F 6A 65 63 74", "31 3E 00 16 00 00 00 56 00 42 00 41 00 50 00 72", @@ -47,8 +47,8 @@ def test_pack() -> None: "00") expected = bytes.fromhex(" ".join(expected_hex)) codepage = 0x04E4 - codepage_name = "cp" + str(codepage) + cp_name = "cp" + str(codepage) path = 'ms_ovba.Models.Entities.reference.DoubleEncodedString' with mock.patch(path, MockDEString): - results = ref.pack(codepage_name, 'little') + results = ref.pack('little', cp_name) assert results == expected diff --git a/tests/Unit/Models/Entities/test_reference_project.py b/tests/Unit/Models/Entities/test_reference_project.py index c3008e028..1026fa175 100644 --- a/tests/Unit/Models/Entities/test_reference_project.py +++ b/tests/Unit/Models/Entities/test_reference_project.py @@ -3,7 +3,7 @@ def test_constructor() -> None: ref = MockProjectReference() - module = ReferenceProject("cp1", ref) + module = ReferenceProject(ref) assert isinstance(module, ReferenceProject) @@ -38,7 +38,7 @@ def test_pack() -> None: "BE 65 17 00") expected = bytes.fromhex(" ".join(expected_hex)) codepage = 0x04E4 - codepage_name = "cp" + str(codepage) - ref_proj = ReferenceProject(codepage_name, ref) - results = ref_proj.pack(codepage_name, 'little') + cp_name = "cp" + str(codepage) + ref_proj = ReferenceProject(ref) + results = ref_proj.pack('little', cp_name) assert results == expected diff --git a/tests/Unit/Models/Entities/test_reference_registered.py b/tests/Unit/Models/Entities/test_reference_registered.py index 1287e05e6..92fd7f370 100644 --- a/tests/Unit/Models/Entities/test_reference_registered.py +++ b/tests/Unit/Models/Entities/test_reference_registered.py @@ -1,3 +1,4 @@ +import pytest from unittest import mock from ms_ovba.Models.Entities.reference_registered import ReferenceRegistered @@ -21,7 +22,7 @@ def unpack(data): def test_constructor() -> None: ref = MockLibid1() - module = ReferenceRegistered("cp1", ref) + module = ReferenceRegistered(ref) assert isinstance(module, ReferenceRegistered) @@ -35,9 +36,9 @@ def test_pack() -> None: "74 6F 6D 61 74 69 6F 6E 00 00 00 00 00 00") expected = bytes.fromhex(" ".join(expected_hex)) codepage = 0x04E4 - codepage_name = "cp" + str(codepage) - ref_reg = ReferenceRegistered(codepage_name, MockLibid1()) - results = ref_reg.pack(codepage_name, 'little') + cp_name = "cp" + str(codepage) + ref_reg = ReferenceRegistered(MockLibid1()) + results = ref_reg.pack('little', cp_name) assert results == expected @@ -53,7 +54,22 @@ def test_unpack(): path = 'ms_ovba.Models.Entities.reference_registered.LibidReference' with mock.patch(path, MockLibid2): ref = ReferenceRegistered.unpack(data, "little") - assert ref.libid.data == (b'*\\G{00020430-0000-0000-C000-' + - b'000000000046}#2.0#0#' + - b'C:\\Windows\\system32\\stdole2.tlb#' + - b'OLE Automation') + assert ref.libid.data == ( + b'*\\G{00020430-0000-0000-C000-000000000046}#2.0#0#' + + b'C:\\Windows\\system32\\stdole2.tlb#OLE Automation' + ) + + +def test_bad_id(): + hex = ("0C 00 68 00 00 00 5E 00 00 00 2A 5C 47 7B 30 30", + "30 32 30 34 33 30 2D 30 30 30 30 2D 30 30 30 30", + "2D 43 30 30 30 2D 30 30 30 30 30 30 30 30 30 30", + "34 36 7D 23 32 2E 30 23 30 23 43 3A 5C 57 69 6E", + "64 6F 77 73 5C 73 79 73 74 65 6D 33 32 5C 73 74", + "64 6F 6C 65 32 2E 74 6C 62 23 4F 4C 45 20 41 75", + "74 6F 6D 61 74 69 6F 6E 00 00 00 00 00 00") + data = bytes.fromhex(" ".join(hex)) + path = 'ms_ovba.Models.Entities.reference_registered.LibidReference' + with mock.patch(path, MockLibid2): + with pytest.raises(Exception): + ReferenceRegistered.unpack(data, "little") diff --git a/tests/Unit/Models/Fields/test_double_encoded_string.py b/tests/Unit/Models/Fields/test_double_encoded_string.py new file mode 100644 index 000000000..94377392d --- /dev/null +++ b/tests/Unit/Models/Fields/test_double_encoded_string.py @@ -0,0 +1,23 @@ +from ms_ovba.Models.Fields.doubleEncodedString import ( + DoubleEncodedString +) + + +def test_constructor() -> None: + de_string = DoubleEncodedString([0x01, 0x02], "Foo") + assert isinstance(de_string, DoubleEncodedString) + + +def test_value_property() -> None: + expected = "Foo" + de_string = DoubleEncodedString([0x01, 0x02], expected) + assert de_string.value == expected + + +def test_pack() -> None: + codepage = 0x04E4 + cp_name = "cp" + str(codepage) + de_string = DoubleEncodedString([0x01, 0x02], "Foo") + expected = (b'\x01\x00\x03\x00\x00\x00Foo' + + b'\x02\x00\x06\x00\x00\x00F\x00o\x00o\x00') + assert de_string.pack("little", cp_name) == expected diff --git a/tests/Unit/Models/Fields/test_idSizeField.py b/tests/Unit/Models/Fields/test_idSizeField.py index 033ce3be9..d345bb00a 100644 --- a/tests/Unit/Models/Fields/test_idSizeField.py +++ b/tests/Unit/Models/Fields/test_idSizeField.py @@ -2,7 +2,25 @@ from ms_ovba.Models.Fields.idSizeField import IdSizeField +def test_string() -> None: + field = IdSizeField(1, 2, "Hi") + expected = b'\x01\x00\x02\x00\x00\x00Hi' + assert field.pack("little") == expected + + +def test_H(): + field = IdSizeField(1, 2, 3) + expected = b'\x01\x00\x02\x00\x00\x00\x03\x00' + assert field.pack("little") == expected + + +def test_I(): + field = IdSizeField(1, 4, 3) + expected = b'\x01\x00\x04\x00\x00\x00\x03\x00\x00\x00' + assert field.pack("little") == expected + + def test_bad_value() -> None: field = IdSizeField(2, 3, 6) with pytest.raises(Exception): - field.pack(1234, "little") + field.pack("little") diff --git a/tests/Unit/Models/Fields/test_project_reference.py b/tests/Unit/Models/Fields/test_project_reference.py index 578ba5ade..9c8b12dc7 100644 --- a/tests/Unit/Models/Fields/test_project_reference.py +++ b/tests/Unit/Models/Fields/test_project_reference.py @@ -1,3 +1,4 @@ +import pytest from ms_ovba.Models.Fields.project_reference import ProjectReference @@ -17,6 +18,24 @@ def test_len() -> None: assert len(ref) == 48 -def test_str() -> None: +@pytest.mark.parametrize("data, embedded, expected", [ + ( + "C:\\Example Path\\Example-ReferencedProject.xls", + True, + "*\\CC:\\Example Path\\Example-ReferencedProject.xls" + ), + ( + "/Example Path/Example-ReferencedProject.xls", + False, + "*\\B/Example Path/Example-ReferencedProject.xls" + ) +]) +def test_str(data, embedded, expected) -> None: + ref = ProjectReference(data, embedded) + assert str(ref) == expected + + +def test_relative() -> None: ref = ProjectReference("C:\\Example Path\\Example-ReferencedProject.xls") - assert str(ref) == "*\\CC:\\Example Path\\Example-ReferencedProject.xls" + rel = ref.relative() + assert str(rel) == "*\\CExample-ReferencedProject.xls" diff --git a/tests/Unit/Models/test_vbaProject.py b/tests/Unit/Models/test_vbaProject.py deleted file mode 100644 index dfe9ae557..000000000 --- a/tests/Unit/Models/test_vbaProject.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest -from ms_ovba.vbaProject import VbaProject - - -def test_set_get_visibility() -> None: - project = VbaProject() - project.set_visibility_state(0) - assert project.get_visibility_state() == 0 - - -def test_set_get_protection() -> None: - project = VbaProject() - project.set_protection_state(0) - assert project.get_protection_state() == 0 - - -def test_set_get_password() -> None: - project = VbaProject() - project.set_password(0) - assert project.get_password() == 0 - - -def test_bad_visibility() -> None: - """ - Visibility must be zero or 0xFF - """ - project = VbaProject() - with pytest.raises(Exception): - project.set_visibility_state(1) diff --git a/tests/Unit/Views/test_project.py b/tests/Unit/Views/test_project.py index 6fae080d0..659cc1c94 100644 --- a/tests/Unit/Views/test_project.py +++ b/tests/Unit/Views/test_project.py @@ -47,21 +47,11 @@ def __init__(self) -> None: self.modules = [Mod("ThisWorkbook"), Mod("Sheet1"), mod1] self.project_id = '{9E394C0B-697E-4AEE-9FA6-446F51FB30DC}' - - def get_codepage_name(self): - return 'cp1252' - - def get_project_id(self): - return self.project_id - - def get_protection_state(self): - return b'\x00\x00\x00\x00' - - def get_password(self): - return b'\x00' - - def get_visibility_state(self): - return b'\xFF' + self.codepage_name = 'cp1252' + self.protection_state = b'\x00\x00\x00\x00' + self.password = b'\x00' + self.visibility_state = b'\xFF' + self.attributes = {} @unittest.mock.patch('random.randint', NotSoRandom.randint) diff --git a/tests/Unit/test_vbaProject.py b/tests/Unit/test_vbaProject.py new file mode 100644 index 000000000..1ca43dcc6 --- /dev/null +++ b/tests/Unit/test_vbaProject.py @@ -0,0 +1,78 @@ +import pytest +from ms_ovba.vbaProject import VbaProject + + +def test_set_get_default_date() -> None: + project = VbaProject() + date = project.default_date + project.default_date = date + assert project.default_date == date + + +def test_set_get_project_id() -> None: + project = VbaProject() + project.project_id = '{test}' + assert project.project_id == '{test}' + + +def test_set_get_visibility() -> None: + project = VbaProject() + project.visibility_state = 0 + assert project.visibility_state == 0 + + +def test_set_get_protection() -> None: + project = VbaProject() + project.protection_state = 0 + assert project.protection_state == 0 + + +def test_set_get_password() -> None: + project = VbaProject() + project.password = 0 + assert project.password == 0 + + +def test_set_get_cache() -> None: + project = VbaProject() + project.performance_cache = b'0' + assert project.performance_cache == b'0' + + +def test_set_get_cache_version() -> None: + project = VbaProject() + project.performance_cache_version = 0x01 + assert project.performance_cache_version == 1 + + +def test_set_get_codepage() -> None: + project = VbaProject() + project.codepage_name = 'cp1253' + assert project.codepage_name == 'cp1253' + + +def test_set_get_cookie() -> None: + project = VbaProject() + project.project_cookie = 0x02 + assert project.project_cookie == 2 + + +def test_include_projectwm() -> None: + project = VbaProject() + project.include_projectwm() + assert project.projectwm + + +def test_exclude_projectwm() -> None: + project = VbaProject() + project.exclude_projectwm() + assert not project.projectwm + + +def test_bad_visibility() -> None: + """ + Visibility must be zero or 0xFF + """ + project = VbaProject() + with pytest.raises(Exception): + project.visibility_state = 1