From dc0650bf76d75fc889b11fc5f1c0a83c643c77f8 Mon Sep 17 00:00:00 2001 From: Janik Besendorf Date: Sat, 22 Aug 2026 14:16:23 +0200 Subject: [PATCH] Fix bugreport command section extraction --- src/mvt/android/artifacts/getprop.py | 8 +++--- src/mvt/android/modules/bugreport/base.py | 27 ++++++++++++++++++- .../modules/bugreport/dumpsys_getprop.py | 21 +++------------ tests/android/test_artifact_getprop.py | 9 +++++++ 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/mvt/android/artifacts/getprop.py b/src/mvt/android/artifacts/getprop.py index debcfc4..a03491b 100644 --- a/src/mvt/android/artifacts/getprop.py +++ b/src/mvt/android/artifacts/getprop.py @@ -28,18 +28,18 @@ INTERESTING_PROPERTIES = [ class GetProp(AndroidArtifact): def parse(self, entry: str) -> None: self.results: List[Dict[str, str]] = [] - rxp = re.compile(r"\[(.+?)\]: \[(.+?)\]") + rxp = re.compile(r"^\[([^]]+)\]: \[(.*)\]$") for line in entry.splitlines(): line = line.strip() if line == "": continue - matches = re.findall(rxp, line) - if not matches or len(matches[0]) != 2: + match = rxp.match(line) + if not match: continue - prop_entry = {"name": matches[0][0], "value": matches[0][1]} + prop_entry = {"name": match.group(1), "value": match.group(2)} self.results.append(prop_entry) def get_device_timezone(self) -> str | None: diff --git a/src/mvt/android/modules/bugreport/base.py b/src/mvt/android/modules/bugreport/base.py index 156e01c..01af8c9 100644 --- a/src/mvt/android/modules/bugreport/base.py +++ b/src/mvt/android/modules/bugreport/base.py @@ -72,7 +72,11 @@ class BugReportModule(MVTModule): if not self.extract_path: raise ValueError("extract_path is not set") joined = os.path.join(self.extract_path, file_path) - if not Path(joined).resolve().is_relative_to(Path(self.extract_path).resolve()): + if ( + not Path(joined) + .resolve() + .is_relative_to(Path(self.extract_path).resolve()) + ): raise ValueError("unsafe file_path") handle = open(joined, "rb") @@ -100,6 +104,27 @@ class BugReportModule(MVTModule): return None + @staticmethod + def extract_command_section(content: str, heading: str) -> str: + """Return a bugreport command section without consuming the next one. + + Bugreport separators include timing text, so looking for a line equal to + ``------`` is not sufficient and can accidentally feed the remainder of + dumpstate to a parser. + """ + lines: list[str] = [] + in_section = False + for line in content.splitlines(): + stripped = line.strip() + if not in_section: + if stripped.startswith(heading): + in_section = True + continue + if stripped.startswith("------"): + break + lines.append(line) + return "\n".join(lines) + def _get_file_modification_time(self, file_path: str) -> datetime.datetime: if self.zip_archive: file_timetuple = self.zip_archive.getinfo(file_path).date_time diff --git a/src/mvt/android/modules/bugreport/dumpsys_getprop.py b/src/mvt/android/modules/bugreport/dumpsys_getprop.py index 198a1d2..b9c20ea 100644 --- a/src/mvt/android/modules/bugreport/dumpsys_getprop.py +++ b/src/mvt/android/modules/bugreport/dumpsys_getprop.py @@ -44,21 +44,8 @@ class DumpsysGetProp(GetPropArtifact, BugReportModule): ) return - lines = [] - in_getprop = False - - for line in content.decode(errors="ignore").splitlines(): - if line.strip().startswith("------ SYSTEM PROPERTIES"): - in_getprop = True - continue - - if not in_getprop: - continue - - if line.strip() == "------": - break - - lines.append(line) - - self.parse("\n".join(lines)) + section = self.extract_command_section( + content.decode(errors="ignore"), "------ SYSTEM PROPERTIES" + ) + self.parse(section) self.log.info("Extracted %d Android system properties", len(self.results)) diff --git a/tests/android/test_artifact_getprop.py b/tests/android/test_artifact_getprop.py index d9fec6b..7815fbd 100644 --- a/tests/android/test_artifact_getprop.py +++ b/tests/android/test_artifact_getprop.py @@ -39,3 +39,12 @@ class TestGetPropArtifact: assert len(gp.alertstore.alerts) == 0 gp.check_indicators() assert len(gp.alertstore.alerts) == 1 + + def test_empty_values_and_invalid_lines(self): + gp = GetProp() + gp.parse("[empty]: []\n[valid]: [value]\n0\n[broken]: [value") + + assert gp.results == [ + {"name": "empty", "value": ""}, + {"name": "valid", "value": "value"}, + ]