mirror of
https://github.com/mvt-project/mvt.git
synced 2026-10-10 18:03:56 +02:00
Keep settings history timestamps on 29 February
dumpsys prints the settings change history without a year, so
_resolve_timestamp() parsed the value with strptime("%m-%d ...") and only
then replaced the year. strptime() parses a value without a year in 1900,
which has no 29 February, so every change made on a leap day raised
ValueError and lost its timestamp, dropping it from the timeline and from
the alert of a dangerous setting.
Parse the value together with a year instead, starting from the year the
section was dumped and going back until the date exists and is not later
than the dump. Eight years always reach a leap year.
This also removes the DeprecationWarning Python 3.13 raises for parsing a
day of month without a year (python/cpython#70647).
This commit is contained in:
1 parent
94e9cd3712
commit
10f739d669
2 files changed
+48
-8
No files matched your search
@@ -255,19 +255,24 @@ class Settings(AndroidArtifact):
|
|||||||
dumpsys prints the change history without a year, so it is resolved
|
dumpsys prints the change history without a year, so it is resolved
|
||||||
against the time the section was dumped: the most recent matching date
|
against the time the section was dumped: the most recent matching date
|
||||||
at or before that time.
|
at or before that time.
|
||||||
|
|
||||||
|
The year is parsed together with the value, because strptime() would
|
||||||
|
otherwise parse it in 1900, which has no 29 February. Going back eight
|
||||||
|
years always reaches a leap year.
|
||||||
"""
|
"""
|
||||||
if section_end is None:
|
if section_end is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
for year in range(section_end.year, section_end.year - 8, -1):
|
||||||
partial = datetime.strptime(value, "%m-%d %H:%M:%S.%f")
|
try:
|
||||||
timestamp = partial.replace(year=section_end.year)
|
timestamp = datetime.strptime(f"{year}-{value}", "%Y-%m-%d %H:%M:%S.%f")
|
||||||
if timestamp > section_end:
|
except ValueError:
|
||||||
timestamp = partial.replace(year=section_end.year - 1)
|
continue
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return convert_datetime_to_iso(timestamp)
|
if timestamp <= section_end:
|
||||||
|
return convert_datetime_to_iso(timestamp)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def _parse_history(
|
def _parse_history(
|
||||||
self, line: str, section_end: Optional[datetime]
|
self, line: str, section_end: Optional[datetime]
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from mvt.android.artifacts.settings import Settings
|
from mvt.android.artifacts.settings import Settings
|
||||||
|
|
||||||
from ..utils import get_artifact
|
from ..utils import get_artifact
|
||||||
@@ -21,6 +23,21 @@ def find(settings: Settings, name: str) -> list:
|
|||||||
return [result for result in settings.results if result["name"] == name]
|
return [result for result in settings.results if result["name"] == name]
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_history_time(time: str, section_end: str) -> Optional[str]:
|
||||||
|
settings = Settings()
|
||||||
|
settings.parse(
|
||||||
|
"SECURE SETTINGS (user 0)\n"
|
||||||
|
"_id:240 name:accessibility_enabled pkg:android value:1\n"
|
||||||
|
"\tHistory (accessibility_enabled)\n"
|
||||||
|
f"\t\ttime:{time} mode:update oldValue:0 newValue:1 "
|
||||||
|
"package:com.example.helper\n"
|
||||||
|
"\n"
|
||||||
|
"--------- 0.019s was the duration of dumpsys settings, "
|
||||||
|
f"ending at: {section_end}\n"
|
||||||
|
)
|
||||||
|
return settings.results[0]["history"][0]["timestamp"]
|
||||||
|
|
||||||
|
|
||||||
class TestSettingsArtifact:
|
class TestSettingsArtifact:
|
||||||
def test_parsing(self):
|
def test_parsing(self):
|
||||||
settings = parse_bugreport_settings()
|
settings = parse_bugreport_settings()
|
||||||
@@ -159,6 +176,24 @@ class TestSettingsArtifact:
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def test_leap_day_history_keeps_its_timestamp(self):
|
||||||
|
assert (
|
||||||
|
resolve_history_time("02-29 22:41:07.980", "2024-03-05 23:14:28")
|
||||||
|
== "2024-02-29 22:41:07.980000"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_leap_day_history_resolved_to_the_last_leap_year(self):
|
||||||
|
# The most recent 29 February at or before the dump can be several
|
||||||
|
# years back when the dump was taken in a year without one.
|
||||||
|
assert (
|
||||||
|
resolve_history_time("02-29 22:41:07.980", "2025-03-01 10:00:00")
|
||||||
|
== "2024-02-29 22:41:07.980000"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
resolve_history_time("02-29 22:41:07.980", "2024-01-10 10:00:00")
|
||||||
|
== "2020-02-29 22:41:07.980000"
|
||||||
|
)
|
||||||
|
|
||||||
def test_dangerous_setting_is_detected_with_the_changing_package(self):
|
def test_dangerous_setting_is_detected_with_the_changing_package(self):
|
||||||
settings = parse_bugreport_settings()
|
settings = parse_bugreport_settings()
|
||||||
settings.check_indicators()
|
settings.check_indicators()
|
||||||
|
|||||||
Reference in new issue
Block a user