diff --git a/src/mvt/android/artifacts/getprop.py b/src/mvt/android/artifacts/getprop.py index a03491b..094eb5d 100644 --- a/src/mvt/android/artifacts/getprop.py +++ b/src/mvt/android/artifacts/getprop.py @@ -28,19 +28,16 @@ INTERESTING_PROPERTIES = [ class GetProp(AndroidArtifact): def parse(self, entry: str) -> None: self.results: List[Dict[str, str]] = [] - rxp = re.compile(r"^\[([^]]+)\]: \[(.*)\]$") + # A property value may span several lines: persist.sys.boot.reason.history + # prints one boot per line. Matching the whole section instead of line by + # line lets a value run to the first closing bracket that ends a line. + rxp = re.compile( + r"^[ \t]*\[([^]]+)\]: \[(.*?)\][ \t\r]*$", + re.MULTILINE | re.DOTALL, + ) - for line in entry.splitlines(): - line = line.strip() - if line == "": - continue - - match = rxp.match(line) - if not match: - continue - - prop_entry = {"name": match.group(1), "value": match.group(2)} - self.results.append(prop_entry) + for name, value in rxp.findall(entry): + self.results.append({"name": name, "value": value}) def get_device_timezone(self) -> str | None: """ diff --git a/tests/android/test_artifact_getprop.py b/tests/android/test_artifact_getprop.py index 7815fbd..0fbe742 100644 --- a/tests/android/test_artifact_getprop.py +++ b/tests/android/test_artifact_getprop.py @@ -48,3 +48,20 @@ class TestGetPropArtifact: {"name": "empty", "value": ""}, {"name": "valid", "value": "value"}, ] + + def test_multiline_value(self): + gp = GetProp() + gp.parse( + "[persist.sys.boot.reason.history]: [" + "reboot,ota,1697044974\n" + "reboot,watchdog,1696958574]\n" + "[ro.build.version.sdk]: [35]\n" + ) + + assert gp.results == [ + { + "name": "persist.sys.boot.reason.history", + "value": "reboot,ota,1697044974\nreboot,watchdog,1696958574", + }, + {"name": "ro.build.version.sdk", "value": "35"}, + ]