diff --git a/src/mvt/android/artifacts/dumpsys_adb.py b/src/mvt/android/artifacts/dumpsys_adb.py index 4ed1669..d2a33fb 100644 --- a/src/mvt/android/artifacts/dumpsys_adb.py +++ b/src/mvt/android/artifacts/dumpsys_adb.py @@ -131,10 +131,17 @@ class DumpsysADBArtifact(AndroidArtifact): ) return - # TODO: Parse AdbDebuggingManager line in output. - start_of_json = content.find(b"\n{") + 2 - end_of_json = content.rfind(b"}\n") - 2 - json_content = content[start_of_json:end_of_json].rstrip() + start_of_json = content.find(b"\n{") + if start_of_json == -1: + self.log.error("Unable to find ADB manager state in dumpsys output") + return + + end_of_json = content.rfind(b"}\n") + if end_of_json == -1 or end_of_json <= start_of_json: + self.log.error("Unable to find complete ADB manager state in dumpsys output") + return + + json_content = content[start_of_json + 2 : end_of_json - 2].rstrip() parsed = self.indented_dump_parser(json_content) if parsed.get("debugging_manager") is None: diff --git a/src/mvt/android/artifacts/dumpsys_packages.py b/src/mvt/android/artifacts/dumpsys_packages.py index 59047aa..81da22e 100644 --- a/src/mvt/android/artifacts/dumpsys_packages.py +++ b/src/mvt/android/artifacts/dumpsys_packages.py @@ -14,9 +14,12 @@ from .artifact import AndroidArtifact class DumpsysPackagesArtifact(AndroidArtifact): def check_indicators(self) -> None: + alerted_root_packages = set() for result in self.results: - # XXX: De-duplication Package detections if result["package_name"] in ROOT_PACKAGES: + if result["package_name"] in alerted_root_packages: + continue + alerted_root_packages.add(result["package_name"]) self.alertstore.medium( f'Found an installed package related to rooting/jailbreaking: "{result["package_name"]}"', "", diff --git a/src/mvt/android/artifacts/settings.py b/src/mvt/android/artifacts/settings.py index 4649666..17c3bf2 100644 --- a/src/mvt/android/artifacts/settings.py +++ b/src/mvt/android/artifacts/settings.py @@ -67,11 +67,14 @@ class Settings(AndroidArtifact): # Check if one of the dangerous settings is using an unsafe # value (different than the one specified). if danger["key"] == key and danger["safe_value"] != value: - self.log.warning( - 'Found suspicious "%s" setting "%s = %s" (%s)', - namespace, - key, - value, - danger["description"], + self.alertstore.medium( + f'Found suspicious "{namespace}" setting "{key} = {value}" ({danger["description"]})', + "", + { + "namespace": namespace, + "key": key, + "value": value, + "description": danger["description"], + }, ) break diff --git a/src/mvt/android/artifacts/tombstone_crashes.py b/src/mvt/android/artifacts/tombstone_crashes.py index e97049b..9a878aa 100644 --- a/src/mvt/android/artifacts/tombstone_crashes.py +++ b/src/mvt/android/artifacts/tombstone_crashes.py @@ -101,8 +101,7 @@ class TombstoneCrashArtifact(AndroidArtifact): continue if result.get("command_line", []): - command_name = result.get("command_line")[0].split("/")[-1] - command_name = result["command_line"][0] + command_name = result["command_line"][0].split("/")[-1] ioc_match = self.indicators.check_process(command_name) if ioc_match: self.alertstore.critical( @@ -262,7 +261,7 @@ class TombstoneCrashArtifact(AndroidArtifact): @staticmethod def _parse_timestamp_string(timestamp: str) -> str: timestamp_parsed = parser.parse(timestamp) - # HACK: Swap the local timestamp to UTC, so keep the original time and avoid timezone conversion. + # Preserve the source wall-clock time while returning the project-wide ISO format. local_timestamp = timestamp_parsed.replace(tzinfo=datetime.timezone.utc) return convert_datetime_to_iso(local_timestamp) diff --git a/src/mvt/android/modules/androidqf/aqf_files.py b/src/mvt/android/modules/androidqf/aqf_files.py index 53f4b68..de9b44a 100644 --- a/src/mvt/android/modules/androidqf/aqf_files.py +++ b/src/mvt/android/modules/androidqf/aqf_files.py @@ -41,7 +41,7 @@ class AQFFiles(AndroidQFModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -145,8 +145,8 @@ class AQFFiles(AndroidQFModule): # Convert the UTC timestamp to local time on Android device's local timezone local_timestamp = utc_timestamp.astimezone(device_timezone) - # HACK: We only output the UTC timestamp in convert_datetime_to_iso, we - # set the timestamp timezone to UTC, to avoid the timezone conversion again. + # Preserve the device-local wall-clock time while using + # the project-wide ISO conversion helper. local_timestamp = local_timestamp.replace( tzinfo=datetime.timezone.utc ) diff --git a/src/mvt/android/modules/androidqf/aqf_getprop.py b/src/mvt/android/modules/androidqf/aqf_getprop.py index f41029b..938fb80 100644 --- a/src/mvt/android/modules/androidqf/aqf_getprop.py +++ b/src/mvt/android/modules/androidqf/aqf_getprop.py @@ -22,7 +22,7 @@ class AQFGetProp(GetPropArtifact, AndroidQFModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -32,7 +32,7 @@ class AQFGetProp(GetPropArtifact, AndroidQFModule): log=log, results=results, ) - self.results: list = [] + self.results: list = [] if results is None else results def run(self) -> None: getprop_files = self._get_files_by_pattern("*/getprop.txt") diff --git a/src/mvt/android/modules/androidqf/aqf_log_timestamps.py b/src/mvt/android/modules/androidqf/aqf_log_timestamps.py index 1070d1b..305d6be 100644 --- a/src/mvt/android/modules/androidqf/aqf_log_timestamps.py +++ b/src/mvt/android/modules/androidqf/aqf_log_timestamps.py @@ -27,7 +27,7 @@ class AQFLogTimestamps(FileTimestampsArtifact, AndroidQFModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/androidqf/aqf_packages.py b/src/mvt/android/modules/androidqf/aqf_packages.py index 0ec0122..264fd1e 100644 --- a/src/mvt/android/modules/androidqf/aqf_packages.py +++ b/src/mvt/android/modules/androidqf/aqf_packages.py @@ -30,7 +30,7 @@ class AQFPackages(AndroidQFModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/androidqf/aqf_processes.py b/src/mvt/android/modules/androidqf/aqf_processes.py index 4c69ca0..b940e67 100644 --- a/src/mvt/android/modules/androidqf/aqf_processes.py +++ b/src/mvt/android/modules/androidqf/aqf_processes.py @@ -22,7 +22,7 @@ class AQFProcesses(ProcessesArtifact, AndroidQFModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/androidqf/aqf_settings.py b/src/mvt/android/modules/androidqf/aqf_settings.py index aae9021..8d5bb51 100644 --- a/src/mvt/android/modules/androidqf/aqf_settings.py +++ b/src/mvt/android/modules/androidqf/aqf_settings.py @@ -22,7 +22,7 @@ class AQFSettings(SettingsArtifact, AndroidQFModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -32,7 +32,7 @@ class AQFSettings(SettingsArtifact, AndroidQFModule): log=log, results=results, ) - self.results: dict = {} + self.results: dict = results if results is not None else {} def run(self) -> None: for setting_file in self._get_files_by_pattern("*/settings_*.txt"): diff --git a/src/mvt/android/modules/androidqf/base.py b/src/mvt/android/modules/androidqf/base.py index be898fc..b0304d0 100644 --- a/src/mvt/android/modules/androidqf/base.py +++ b/src/mvt/android/modules/androidqf/base.py @@ -23,7 +23,7 @@ class AndroidQFModule(MVTModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/androidqf/mounts.py b/src/mvt/android/modules/androidqf/mounts.py index fb1274a..ea446c2 100644 --- a/src/mvt/android/modules/androidqf/mounts.py +++ b/src/mvt/android/modules/androidqf/mounts.py @@ -32,7 +32,7 @@ class Mounts(MountsArtifact, AndroidQFModule): log=log, results=results, ) - self.results: list = [] + self.results: list = [] if results is None else results def run(self) -> None: """ @@ -66,6 +66,9 @@ class Mounts(MountsArtifact, AndroidQFModule): # AndroidQF format: array of strings like # "/dev/block/dm-12 on / type ext4 (ro,seclabel,noatime)" mount_content = "\n".join(json_data) + else: + self.log.error("Expected mounts.json to contain a list of mount lines") + return self.parse(mount_content) except Exception as exc: self.log.error("Failed to parse mount information: %s", exc) diff --git a/src/mvt/android/modules/androidqf/sms.py b/src/mvt/android/modules/androidqf/sms.py index 46cc3f6..bcbd226 100644 --- a/src/mvt/android/modules/androidqf/sms.py +++ b/src/mvt/android/modules/androidqf/sms.py @@ -21,10 +21,6 @@ from .base import AndroidQFModule class SMS(AndroidQFModule): """ This module analyse SMS file in backup - - XXX: We should also de-duplicate this AQF module, but first we - need to add tests for loading encrypted SMS backups through the backup - sub-module. """ def __init__( diff --git a/src/mvt/android/modules/backup/base.py b/src/mvt/android/modules/backup/base.py index d49a945..6383e4b 100644 --- a/src/mvt/android/modules/backup/base.py +++ b/src/mvt/android/modules/backup/base.py @@ -22,7 +22,7 @@ class BackupModule(MVTModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/backup/sms.py b/src/mvt/android/modules/backup/sms.py index 1c75587..6f8306a 100644 --- a/src/mvt/android/modules/backup/sms.py +++ b/src/mvt/android/modules/backup/sms.py @@ -20,7 +20,7 @@ class SMS(BackupModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/base.py b/src/mvt/android/modules/bugreport/base.py index 70ede77..e73b119 100644 --- a/src/mvt/android/modules/bugreport/base.py +++ b/src/mvt/android/modules/bugreport/base.py @@ -22,7 +22,7 @@ class BugReportModule(MVTModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_accessibility.py b/src/mvt/android/modules/bugreport/dumpsys_accessibility.py index 0c0f294..72208c0 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_accessibility.py +++ b/src/mvt/android/modules/bugreport/dumpsys_accessibility.py @@ -22,7 +22,7 @@ class DumpsysAccessibility(DumpsysAccessibilityArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_activities.py b/src/mvt/android/modules/bugreport/dumpsys_activities.py index bfceebf..2800e95 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_activities.py +++ b/src/mvt/android/modules/bugreport/dumpsys_activities.py @@ -24,7 +24,7 @@ class DumpsysActivities(DumpsysPackageActivitiesArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_adb_state.py b/src/mvt/android/modules/bugreport/dumpsys_adb_state.py index 07d4694..506af30 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_adb_state.py +++ b/src/mvt/android/modules/bugreport/dumpsys_adb_state.py @@ -22,7 +22,7 @@ class DumpsysADBState(DumpsysADBArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_appops.py b/src/mvt/android/modules/bugreport/dumpsys_appops.py index f3ab41c..91122cb 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_appops.py +++ b/src/mvt/android/modules/bugreport/dumpsys_appops.py @@ -22,7 +22,7 @@ class DumpsysAppops(DumpsysAppopsArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_battery_daily.py b/src/mvt/android/modules/bugreport/dumpsys_battery_daily.py index 365d193..a7c0c72 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_battery_daily.py +++ b/src/mvt/android/modules/bugreport/dumpsys_battery_daily.py @@ -22,7 +22,7 @@ class DumpsysBatteryDaily(DumpsysBatteryDailyArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_battery_history.py b/src/mvt/android/modules/bugreport/dumpsys_battery_history.py index 2e0f468..42d395d 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_battery_history.py +++ b/src/mvt/android/modules/bugreport/dumpsys_battery_history.py @@ -22,7 +22,7 @@ class DumpsysBatteryHistory(DumpsysBatteryHistoryArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_dbinfo.py b/src/mvt/android/modules/bugreport/dumpsys_dbinfo.py index 96b0bf3..13ba8b3 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_dbinfo.py +++ b/src/mvt/android/modules/bugreport/dumpsys_dbinfo.py @@ -24,7 +24,7 @@ class DumpsysDBInfo(DumpsysDBInfoArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_getprop.py b/src/mvt/android/modules/bugreport/dumpsys_getprop.py index 2bb5cd6..198a1d2 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_getprop.py +++ b/src/mvt/android/modules/bugreport/dumpsys_getprop.py @@ -22,7 +22,7 @@ class DumpsysGetProp(GetPropArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_packages.py b/src/mvt/android/modules/bugreport/dumpsys_packages.py index 6bc5d27..aebec4c 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_packages.py +++ b/src/mvt/android/modules/bugreport/dumpsys_packages.py @@ -23,7 +23,7 @@ class DumpsysPackages(DumpsysPackagesArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/dumpsys_platform_compat.py b/src/mvt/android/modules/bugreport/dumpsys_platform_compat.py index 29e58f3..968bc25 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_platform_compat.py +++ b/src/mvt/android/modules/bugreport/dumpsys_platform_compat.py @@ -22,7 +22,7 @@ class DumpsysPlatformCompat(DumpsysPlatformCompatArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -42,8 +42,10 @@ class DumpsysPlatformCompat(DumpsysPlatformCompatArtifact, BugReportModule): ) return - data = data.decode("utf-8", errors="replace") - content = self.extract_dumpsys_section(data, "DUMP OF SERVICE platform_compat:") + decoded_data = data.decode("utf-8", errors="replace") + content = self.extract_dumpsys_section( + decoded_data, "DUMP OF SERVICE platform_compat:" + ) self.parse(content) self.log.info("Found %d uninstalled apps", len(self.results)) diff --git a/src/mvt/android/modules/bugreport/dumpsys_receivers.py b/src/mvt/android/modules/bugreport/dumpsys_receivers.py index 1c4a028..ded9069 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_receivers.py +++ b/src/mvt/android/modules/bugreport/dumpsys_receivers.py @@ -22,7 +22,7 @@ class DumpsysReceivers(DumpsysReceiversArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/fs_timestamps.py b/src/mvt/android/modules/bugreport/fs_timestamps.py index 000d076..5a2ca48 100644 --- a/src/mvt/android/modules/bugreport/fs_timestamps.py +++ b/src/mvt/android/modules/bugreport/fs_timestamps.py @@ -24,7 +24,7 @@ class BugReportTimestamps(FileTimestampsArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/modules/bugreport/tombstones.py b/src/mvt/android/modules/bugreport/tombstones.py index 58ef254..c4a7afb 100644 --- a/src/mvt/android/modules/bugreport/tombstones.py +++ b/src/mvt/android/modules/bugreport/tombstones.py @@ -23,7 +23,7 @@ class Tombstones(TombstoneCrashArtifact, BugReportModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/android/parsers/backup.py b/src/mvt/android/parsers/backup.py index 105b4f2..c81ecd0 100644 --- a/src/mvt/android/parsers/backup.py +++ b/src/mvt/android/parsers/backup.py @@ -29,9 +29,6 @@ class InvalidBackupPassword(AndroidBackupParsingError): pass -# TODO: Need to clean all the following code and conform it to the coding style. - - def to_utf8_bytes(input_bytes): output = [] for byte in input_bytes: @@ -157,13 +154,13 @@ def decrypt_backup_data(encrypted_backup, password, encryption_algo, format_vers checksum_salt=checksum_salt, ) - # Decrypt and unpad backup data using derivied key. + # Decrypt and unpad backup data using derived key. cipher = Cipher(algorithms.AES(master_key), modes.CBC(master_iv)) decryptor = cipher.decryptor() decrypted_tar = decryptor.update(encrypted_data) + decryptor.finalize() unpadder = padding.PKCS7(128).unpadder() - return unpadder.update(decrypted_tar) + return unpadder.update(decrypted_tar) + unpadder.finalize() def parse_backup_file(data, password=None): @@ -210,6 +207,8 @@ def parse_tar_for_sms(data): or member.name.endswith("_mms_backup") ): dhandler = tar.extractfile(member) + if not dhandler: + continue res.extend(parse_sms_file(dhandler.read())) return res diff --git a/src/mvt/common/artifact.py b/src/mvt/common/artifact.py index af0ba98..8d7b60d 100644 --- a/src/mvt/common/artifact.py +++ b/src/mvt/common/artifact.py @@ -8,5 +8,6 @@ from .module import MVTModule class Artifact(MVTModule): """Base class for artifacts. - XXX: Inheriting from MVTModule to have the same signature as other modules. Not sure if this is a good idea. + Artifacts share the MVTModule lifecycle so commands can run artifacts and + extraction modules through the same interface. """ diff --git a/src/mvt/common/command.py b/src/mvt/common/command.py index 03d2b00..8a43d17 100644 --- a/src/mvt/common/command.py +++ b/src/mvt/common/command.py @@ -273,7 +273,6 @@ class Command: ): continue - # FIXME: do we need the logger here module_logger = logging.getLogger(module.__module__) m = module( diff --git a/src/mvt/common/module.py b/src/mvt/common/module.py index f555e40..2d8a395 100644 --- a/src/mvt/common/module.py +++ b/src/mvt/common/module.py @@ -51,7 +51,7 @@ class MVTModule: results_path: Optional[str] = None, module_options: Optional[Dict[str, Any]] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: """Initialize module. @@ -71,15 +71,13 @@ class MVTModule: self.file_path: Optional[str] = file_path self.target_path: Optional[str] = target_path self.results_path: Optional[str] = results_path - self.module_options: Optional[Dict[str, Any]] = ( - module_options if module_options else {} - ) + self.module_options: Dict[str, Any] = module_options if module_options else {} self.log = log self.indicators: Optional[Indicators] = None self.alertstore: AlertStore = AlertStore(log=log) - self.results: ModuleResults = results if results else [] + self.results: ModuleResults = results if results is not None else [] self.timeline: ModuleTimeline = [] @classmethod @@ -109,11 +107,14 @@ class MVTModule: name = self.get_slug() if self.results: + converted_results: Any if isinstance(self.results, dict): converted_results = self.results else: converted_results = [ - asdict(result) if is_dataclass(result) else result + asdict(result) + if is_dataclass(result) and not isinstance(result, type) + else result for result in self.results ] results_file_name = f"{name}.json" diff --git a/src/mvt/common/module_types.py b/src/mvt/common/module_types.py index 41bbdaa..06fdc12 100644 --- a/src/mvt/common/module_types.py +++ b/src/mvt/common/module_types.py @@ -16,7 +16,10 @@ from typing import Any, Dict, List, Union ModuleAtomicResult = Dict[str, Any] -ModuleResults = List[ModuleAtomicResult] +# Extraction modules historically use either a list of records or grouped +# dictionaries keyed by source path. Keep this alias broad until those shapes +# are modeled per module. +ModuleResults = Any @dataclass diff --git a/src/mvt/ios/modules/backup/backup_info.py b/src/mvt/ios/modules/backup/backup_info.py index 9e07200..6aadd45 100644 --- a/src/mvt/ios/modules/backup/backup_info.py +++ b/src/mvt/ios/modules/backup/backup_info.py @@ -25,7 +25,7 @@ class BackupInfo(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -36,7 +36,7 @@ class BackupInfo(IOSExtraction): results=results, ) - self.results: dict = {} + self.results: dict = results if results is not None else {} def run(self) -> None: if not self.target_path: diff --git a/src/mvt/ios/modules/backup/configuration_profiles.py b/src/mvt/ios/modules/backup/configuration_profiles.py index aa4f499..9c0343e 100644 --- a/src/mvt/ios/modules/backup/configuration_profiles.py +++ b/src/mvt/ios/modules/backup/configuration_profiles.py @@ -33,7 +33,7 @@ class ConfigurationProfiles(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/backup/manifest.py b/src/mvt/ios/modules/backup/manifest.py index 0eaa6bd..cc74cbb 100644 --- a/src/mvt/ios/modules/backup/manifest.py +++ b/src/mvt/ios/modules/backup/manifest.py @@ -33,7 +33,7 @@ class Manifest(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/backup/profile_events.py b/src/mvt/ios/modules/backup/profile_events.py index 7bd7b94..1fc6d7e 100644 --- a/src/mvt/ios/modules/backup/profile_events.py +++ b/src/mvt/ios/modules/backup/profile_events.py @@ -34,7 +34,7 @@ class ProfileEvents(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/base.py b/src/mvt/ios/modules/base.py index 3ff5509..5ac9071 100644 --- a/src/mvt/ios/modules/base.py +++ b/src/mvt/ios/modules/base.py @@ -30,7 +30,7 @@ class IOSExtraction(MVTModule): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -52,7 +52,7 @@ class IOSExtraction(MVTModule): :param file_path: Path to the malformed database file. """ - # TODO: Find a better solution. + # SQLite's immutable mode cannot open databases with active WAL files. if not forced: # If the database is open, do not use immutable if os.path.isfile(file_path + "-shm"): diff --git a/src/mvt/ios/modules/fs/analytics.py b/src/mvt/ios/modules/fs/analytics.py index bf53745..0a6e50c 100644 --- a/src/mvt/ios/modules/fs/analytics.py +++ b/src/mvt/ios/modules/fs/analytics.py @@ -34,7 +34,7 @@ class Analytics(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -44,7 +44,7 @@ class Analytics(IOSExtraction): log=log, results=results, ) - self.results: list = [] + self.results: list = [] if results is None else results def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult: return { diff --git a/src/mvt/ios/modules/fs/analytics_ios_versions.py b/src/mvt/ios/modules/fs/analytics_ios_versions.py index 5fb300e..783bb45 100644 --- a/src/mvt/ios/modules/fs/analytics_ios_versions.py +++ b/src/mvt/ios/modules/fs/analytics_ios_versions.py @@ -30,7 +30,7 @@ class AnalyticsIOSVersions(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/fs/cache_files.py b/src/mvt/ios/modules/fs/cache_files.py index 021924a..54a34eb 100644 --- a/src/mvt/ios/modules/fs/cache_files.py +++ b/src/mvt/ios/modules/fs/cache_files.py @@ -25,7 +25,7 @@ class CacheFiles(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -99,6 +99,10 @@ class CacheFiles(IOSExtraction): def run(self) -> None: self.results: dict = {} + if not self.target_path: + self.log.error("No filesystem dump path provided") + return + for root, _, files in os.walk(self.target_path): for file_name in files: if file_name != "Cache.db": diff --git a/src/mvt/ios/modules/fs/filesystem.py b/src/mvt/ios/modules/fs/filesystem.py index de3aa65..563d039 100644 --- a/src/mvt/ios/modules/fs/filesystem.py +++ b/src/mvt/ios/modules/fs/filesystem.py @@ -29,7 +29,7 @@ class Filesystem(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -73,6 +73,10 @@ class Filesystem(IOSExtraction): ) def run(self) -> None: + if not self.target_path: + self.log.error("No filesystem dump path provided") + return + for root, dirs, files in os.walk(self.target_path): for dir_name in dirs: try: diff --git a/src/mvt/ios/modules/fs/net_netusage.py b/src/mvt/ios/modules/fs/net_netusage.py index 23b97f3..91451d6 100644 --- a/src/mvt/ios/modules/fs/net_netusage.py +++ b/src/mvt/ios/modules/fs/net_netusage.py @@ -30,7 +30,7 @@ class Netusage(NetBase): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/fs/safari_favicon.py b/src/mvt/ios/modules/fs/safari_favicon.py index c5c078b..a9c0b65 100644 --- a/src/mvt/ios/modules/fs/safari_favicon.py +++ b/src/mvt/ios/modules/fs/safari_favicon.py @@ -31,7 +31,7 @@ class SafariFavicon(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -41,7 +41,7 @@ class SafariFavicon(IOSExtraction): log=log, results=results, ) - self.results: list = [] + self.results: list = [] if results is None else results def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult: return { diff --git a/src/mvt/ios/modules/fs/shutdownlog.py b/src/mvt/ios/modules/fs/shutdownlog.py index 3910bed..6c1fca7 100644 --- a/src/mvt/ios/modules/fs/shutdownlog.py +++ b/src/mvt/ios/modules/fs/shutdownlog.py @@ -30,7 +30,7 @@ class ShutdownLog(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/fs/version_history.py b/src/mvt/ios/modules/fs/version_history.py index bd5515c..c3c583c 100644 --- a/src/mvt/ios/modules/fs/version_history.py +++ b/src/mvt/ios/modules/fs/version_history.py @@ -32,7 +32,7 @@ class IOSVersionHistory(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -42,7 +42,7 @@ class IOSVersionHistory(IOSExtraction): log=log, results=results, ) - self.results: list = [] + self.results: list = [] if results is None else results def serialize(self, record: ModuleAtomicResult) -> ModuleSerializedResult: return { diff --git a/src/mvt/ios/modules/fs/webkit_indexeddb.py b/src/mvt/ios/modules/fs/webkit_indexeddb.py index 58cea42..42f0895 100644 --- a/src/mvt/ios/modules/fs/webkit_indexeddb.py +++ b/src/mvt/ios/modules/fs/webkit_indexeddb.py @@ -34,7 +34,7 @@ class WebkitIndexedDB(WebkitBase): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/fs/webkit_localstorage.py b/src/mvt/ios/modules/fs/webkit_localstorage.py index 2b94fd1..d1ad05f 100644 --- a/src/mvt/ios/modules/fs/webkit_localstorage.py +++ b/src/mvt/ios/modules/fs/webkit_localstorage.py @@ -32,7 +32,7 @@ class WebkitLocalStorage(WebkitBase): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/fs/webkit_safariviewservice.py b/src/mvt/ios/modules/fs/webkit_safariviewservice.py index caa7eef..62c9481 100644 --- a/src/mvt/ios/modules/fs/webkit_safariviewservice.py +++ b/src/mvt/ios/modules/fs/webkit_safariviewservice.py @@ -28,7 +28,7 @@ class WebkitSafariViewService(WebkitBase): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/applications.py b/src/mvt/ios/modules/mixed/applications.py index d508910..45b88cd 100644 --- a/src/mvt/ios/modules/mixed/applications.py +++ b/src/mvt/ios/modules/mixed/applications.py @@ -41,7 +41,7 @@ class Applications(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/calendar.py b/src/mvt/ios/modules/mixed/calendar.py index dddd11c..40f8ad9 100644 --- a/src/mvt/ios/modules/mixed/calendar.py +++ b/src/mvt/ios/modules/mixed/calendar.py @@ -31,7 +31,7 @@ class Calendar(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/calls.py b/src/mvt/ios/modules/mixed/calls.py index 4d411bf..197f8bf 100644 --- a/src/mvt/ios/modules/mixed/calls.py +++ b/src/mvt/ios/modules/mixed/calls.py @@ -27,7 +27,7 @@ class Calls(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: list = [], + results: Optional[list] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/chrome_favicon.py b/src/mvt/ios/modules/mixed/chrome_favicon.py index 872e60e..af3df2e 100644 --- a/src/mvt/ios/modules/mixed/chrome_favicon.py +++ b/src/mvt/ios/modules/mixed/chrome_favicon.py @@ -31,7 +31,7 @@ class ChromeFavicon(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/chrome_history.py b/src/mvt/ios/modules/mixed/chrome_history.py index 2ab1821..4c5afae 100644 --- a/src/mvt/ios/modules/mixed/chrome_history.py +++ b/src/mvt/ios/modules/mixed/chrome_history.py @@ -33,7 +33,7 @@ class ChromeHistory(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/contacts.py b/src/mvt/ios/modules/mixed/contacts.py index e2ed3dd..ca0470c 100644 --- a/src/mvt/ios/modules/mixed/contacts.py +++ b/src/mvt/ios/modules/mixed/contacts.py @@ -29,7 +29,7 @@ class Contacts(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/firefox_favicon.py b/src/mvt/ios/modules/mixed/firefox_favicon.py index 8df6165..ad92e73 100644 --- a/src/mvt/ios/modules/mixed/firefox_favicon.py +++ b/src/mvt/ios/modules/mixed/firefox_favicon.py @@ -33,7 +33,7 @@ class FirefoxFavicon(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/firefox_history.py b/src/mvt/ios/modules/mixed/firefox_history.py index 8b13279..7ab1eba 100644 --- a/src/mvt/ios/modules/mixed/firefox_history.py +++ b/src/mvt/ios/modules/mixed/firefox_history.py @@ -37,7 +37,7 @@ class FirefoxHistory(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/global_preferences.py b/src/mvt/ios/modules/mixed/global_preferences.py index 04b11bc..97a2aef 100644 --- a/src/mvt/ios/modules/mixed/global_preferences.py +++ b/src/mvt/ios/modules/mixed/global_preferences.py @@ -27,7 +27,7 @@ class GlobalPreferences(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/idstatuscache.py b/src/mvt/ios/modules/mixed/idstatuscache.py index 34d5bda..c841f1e 100644 --- a/src/mvt/ios/modules/mixed/idstatuscache.py +++ b/src/mvt/ios/modules/mixed/idstatuscache.py @@ -36,7 +36,7 @@ class IDStatusCache(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/interactionc.py b/src/mvt/ios/modules/mixed/interactionc.py index ba07bcd..81a67e2 100644 --- a/src/mvt/ios/modules/mixed/interactionc.py +++ b/src/mvt/ios/modules/mixed/interactionc.py @@ -228,7 +228,7 @@ class InteractionC(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/locationd.py b/src/mvt/ios/modules/mixed/locationd.py index b022bc8..86de85b 100644 --- a/src/mvt/ios/modules/mixed/locationd.py +++ b/src/mvt/ios/modules/mixed/locationd.py @@ -36,7 +36,7 @@ class LocationdClients(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -147,7 +147,6 @@ class LocationdClients(IOSExtraction): # Some migration information are int and not dicts if not isinstance(file_plist[key], dict): continue - # FIXME: unclear key format in iOS 17 result = file_plist[key] result["package"] = key.rstrip(":") for timestamp in self.timestamps: diff --git a/src/mvt/ios/modules/mixed/net_datausage.py b/src/mvt/ios/modules/mixed/net_datausage.py index 713a7f9..61b0eef 100644 --- a/src/mvt/ios/modules/mixed/net_datausage.py +++ b/src/mvt/ios/modules/mixed/net_datausage.py @@ -31,7 +31,7 @@ class Datausage(NetBase): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/osanalytics_addaily.py b/src/mvt/ios/modules/mixed/osanalytics_addaily.py index 7f3243f..8a5db3f 100644 --- a/src/mvt/ios/modules/mixed/osanalytics_addaily.py +++ b/src/mvt/ios/modules/mixed/osanalytics_addaily.py @@ -35,7 +35,7 @@ class OSAnalyticsADDaily(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/safari_browserstate.py b/src/mvt/ios/modules/mixed/safari_browserstate.py index 20a594c..48998f1 100644 --- a/src/mvt/ios/modules/mixed/safari_browserstate.py +++ b/src/mvt/ios/modules/mixed/safari_browserstate.py @@ -36,7 +36,7 @@ class SafariBrowserState(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/safari_history.py b/src/mvt/ios/modules/mixed/safari_history.py index 4583ffb..9ba32ad 100644 --- a/src/mvt/ios/modules/mixed/safari_history.py +++ b/src/mvt/ios/modules/mixed/safari_history.py @@ -38,7 +38,7 @@ class SafariHistory(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/shortcuts.py b/src/mvt/ios/modules/mixed/shortcuts.py index 9ca2739..f8899a4 100644 --- a/src/mvt/ios/modules/mixed/shortcuts.py +++ b/src/mvt/ios/modules/mixed/shortcuts.py @@ -37,7 +37,7 @@ class Shortcuts(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/sms.py b/src/mvt/ios/modules/mixed/sms.py index bf6890b..76e0048 100644 --- a/src/mvt/ios/modules/mixed/sms.py +++ b/src/mvt/ios/modules/mixed/sms.py @@ -35,7 +35,7 @@ class SMS(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/sms_attachments.py b/src/mvt/ios/modules/mixed/sms_attachments.py index 981bf39..d02bd27 100644 --- a/src/mvt/ios/modules/mixed/sms_attachments.py +++ b/src/mvt/ios/modules/mixed/sms_attachments.py @@ -35,7 +35,7 @@ class SMSAttachments(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/tcc.py b/src/mvt/ios/modules/mixed/tcc.py index b0c54d4..ed878c5 100644 --- a/src/mvt/ios/modules/mixed/tcc.py +++ b/src/mvt/ios/modules/mixed/tcc.py @@ -56,7 +56,7 @@ class TCC(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/webkit_resource_load_statistics.py b/src/mvt/ios/modules/mixed/webkit_resource_load_statistics.py index d0ce4dd..55cd740 100644 --- a/src/mvt/ios/modules/mixed/webkit_resource_load_statistics.py +++ b/src/mvt/ios/modules/mixed/webkit_resource_load_statistics.py @@ -37,7 +37,7 @@ class WebkitResourceLoadStatistics(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/mixed/webkit_session_resource_log.py b/src/mvt/ios/modules/mixed/webkit_session_resource_log.py index b56cc5e..d8b9a85 100644 --- a/src/mvt/ios/modules/mixed/webkit_session_resource_log.py +++ b/src/mvt/ios/modules/mixed/webkit_session_resource_log.py @@ -39,7 +39,7 @@ class WebkitSessionResourceLog(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, @@ -50,7 +50,7 @@ class WebkitSessionResourceLog(IOSExtraction): results=results, ) - self.results: dict = {} + self.results: dict = results if results is not None else {} @staticmethod def _extract_domains(entries): @@ -77,14 +77,21 @@ class WebkitSessionResourceLog(IOSExtraction): entry["redirect_destination"] ) - # TODO: Currently not used. - # subframe_origins = self._extract_domains( - # entry["subframe_under_origin"]) - # subresource_domains = self._extract_domains( - # entry["subresource_under_origin"]) + subframe_origins = self._extract_domains( + entry["subframe_under_origin"] + ) + subresource_domains = self._extract_domains( + entry["subresource_under_origin"] + ) all_origins = list( - set([entry["origin"]] + source_domains + destination_domains) + set( + [entry["origin"]] + + source_domains + + destination_domains + + subframe_origins + + subresource_domains + ) ) ioc_match = self.indicators.check_urls(all_origins) diff --git a/src/mvt/ios/modules/mixed/whatsapp.py b/src/mvt/ios/modules/mixed/whatsapp.py index e28d4cb..2a72c6f 100644 --- a/src/mvt/ios/modules/mixed/whatsapp.py +++ b/src/mvt/ios/modules/mixed/whatsapp.py @@ -33,7 +33,7 @@ class Whatsapp(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/src/mvt/ios/modules/net_base.py b/src/mvt/ios/modules/net_base.py index de47f88..99a851d 100644 --- a/src/mvt/ios/modules/net_base.py +++ b/src/mvt/ios/modules/net_base.py @@ -30,7 +30,7 @@ class NetBase(IOSExtraction): results_path: Optional[str] = None, module_options: Optional[dict] = None, log: logging.Logger = logging.getLogger(__name__), - results: ModuleResults = [], + results: Optional[ModuleResults] = None, ) -> None: super().__init__( file_path=file_path, diff --git a/tests/android_androidqf/test_settings.py b/tests/android_androidqf/test_settings.py index ef7386a..ce14460 100644 --- a/tests/android_androidqf/test_settings.py +++ b/tests/android_androidqf/test_settings.py @@ -21,4 +21,5 @@ class TestSettingsModule: run_module(m) assert len(m.results) == 1 assert "random" in m.results.keys() - assert len(m.alertstore.alerts) == 0 + assert len(m.alertstore.alerts) == 1 + assert "samsung_errorlog_agree" in m.alertstore.alerts[0].message