Compare commits

..

1 Commits

Author SHA1 Message Date
Donncha Ó Cearbhaill
b2dd8b2476 Fix Makefile and PyProtject config for current Ruff 2025-12-19 13:40:15 +01:00
8 changed files with 27 additions and 144 deletions

View File

@@ -2,4 +2,4 @@ mkdocs==1.6.1
mkdocs-autorefs==1.4.3 mkdocs-autorefs==1.4.3
mkdocs-material==9.6.20 mkdocs-material==9.6.20
mkdocs-material-extensions==1.3.1 mkdocs-material-extensions==1.3.1
mkdocstrings==1.0.0 mkdocstrings==0.30.1

View File

@@ -17,7 +17,7 @@ classifiers = [
"Programming Language :: Python", "Programming Language :: Python",
] ]
dependencies = [ dependencies = [
"click==8.3.1", "click==8.3.0",
"rich==14.1.0", "rich==14.1.0",
"tld==0.13.1", "tld==0.13.1",
"requests==2.32.5", "requests==2.32.5",
@@ -27,11 +27,11 @@ dependencies = [
"iOSbackup==0.9.925", "iOSbackup==0.9.925",
"adb-shell[usb]==0.4.4", "adb-shell[usb]==0.4.4",
"libusb1==3.3.1", "libusb1==3.3.1",
"cryptography==46.0.5", "cryptography==46.0.3",
"PyYAML>=6.0.2", "PyYAML>=6.0.2",
"pyahocorasick==2.2.0", "pyahocorasick==2.2.0",
"betterproto==1.2.5", "betterproto==1.2.5",
"pydantic==2.12.5", "pydantic==2.12.3",
"pydantic-settings==2.10.1", "pydantic-settings==2.10.1",
"NSKeyedUnArchiver==1.5.2", "NSKeyedUnArchiver==1.5.2",
"python-dateutil==2.9.0.post0", "python-dateutil==2.9.0.post0",
@@ -81,8 +81,8 @@ addopts = "-ra -q --cov=mvt --cov-report html --junitxml=pytest.xml --cov-report
testpaths = ["tests"] testpaths = ["tests"]
[tool.ruff] [tool.ruff]
lint.select = ["C90", "E", "F", "W"] # flake8 default set select = ["C90", "E", "F", "W"] # flake8 default set
lint.ignore = [ ignore = [
"E501", # don't enforce line length violations "E501", # don't enforce line length violations
"C901", # complex-structure "C901", # complex-structure
@@ -95,10 +95,10 @@ lint.ignore = [
# "E203", # whitespace-before-punctuation # "E203", # whitespace-before-punctuation
] ]
[tool.ruff.lint.per-file-ignores] [tool.ruff.per-file-ignores]
"__init__.py" = ["F401"] # unused-import "__init__.py" = ["F401"] # unused-import
[tool.ruff.lint.mccabe] [tool.ruff.mccabe]
max-complexity = 10 max-complexity = 10
[tool.setuptools] [tool.setuptools]

View File

@@ -14,23 +14,12 @@ class DumpsysBatteryDailyArtifact(AndroidArtifact):
""" """
def serialize(self, record: dict) -> Union[dict, list]: def serialize(self, record: dict) -> Union[dict, list]:
action = record.get("action", "update")
package_name = record["package_name"]
vers = record["vers"]
if vers == "0":
data = f"Recorded uninstall of package {package_name} (vers 0)"
elif action == "downgrade":
prev_vers = record.get("previous_vers", "unknown")
data = f"Recorded downgrade of package {package_name} from vers {prev_vers} to vers {vers}"
else:
data = f"Recorded update of package {package_name} with vers {vers}"
return { return {
"timestamp": record["from"], "timestamp": record["from"],
"module": self.__class__.__name__, "module": self.__class__.__name__,
"event": "battery_daily", "event": "battery_daily",
"data": data, "data": f"Recorded update of package {record['package_name']} "
f"with vers {record['vers']}",
} }
def check_indicators(self) -> None: def check_indicators(self) -> None:
@@ -47,7 +36,6 @@ class DumpsysBatteryDailyArtifact(AndroidArtifact):
def parse(self, output: str) -> None: def parse(self, output: str) -> None:
daily = None daily = None
daily_updates = [] daily_updates = []
package_versions = {} # Track package versions to detect downgrades
for line in output.splitlines(): for line in output.splitlines():
if line.startswith(" Daily from "): if line.startswith(" Daily from "):
if len(daily_updates) > 0: if len(daily_updates) > 0:
@@ -76,44 +64,15 @@ class DumpsysBatteryDailyArtifact(AndroidArtifact):
break break
if not already_seen: if not already_seen:
update_record = { daily_updates.append(
"action": "update", {
"from": daily["from"], "action": "update",
"to": daily["to"], "from": daily["from"],
"package_name": package_name, "to": daily["to"],
"vers": vers_nr, "package_name": package_name,
} "vers": vers_nr,
}
# Check for uninstall (version 0) )
if vers_nr == "0":
self.log.warning(
"Detected uninstall of package %s (vers 0) on %s",
package_name,
daily["from"],
)
# Check for downgrade
elif package_name in package_versions:
try:
current_vers = int(vers_nr)
previous_vers = int(package_versions[package_name])
if current_vers < previous_vers:
update_record["action"] = "downgrade"
update_record["previous_vers"] = str(previous_vers)
self.log.warning(
"Detected downgrade of package %s from vers %d to vers %d on %s",
package_name,
previous_vers,
current_vers,
daily["from"],
)
except ValueError:
# If version numbers aren't integers, skip comparison
pass
# Update tracking dictionary
package_versions[package_name] = vers_nr
daily_updates.append(update_record)
if len(daily_updates) > 0: if len(daily_updates) > 0:
self.results.extend(daily_updates) self.results.extend(daily_updates)

View File

@@ -84,17 +84,13 @@ class BugReportModule(MVTModule):
return self._get_file_content(main_content.decode().strip()) return self._get_file_content(main_content.decode().strip())
except KeyError: except KeyError:
return None return None
else:
dumpstate_logs = self._get_files_by_pattern("dumpState_*.log")
if not dumpstate_logs:
return None
dumpstate_logs = self._get_files_by_pattern("dumpState_*.log")
if dumpstate_logs:
return self._get_file_content(dumpstate_logs[0]) return self._get_file_content(dumpstate_logs[0])
dumpsys_files = self._get_files_by_pattern("*/dumpsys.txt")
if dumpsys_files:
return self._get_file_content(dumpsys_files[0])
return None
def _get_file_modification_time(self, file_path: str) -> dict: def _get_file_modification_time(self, file_path: str) -> dict:
if self.zip_archive: if self.zip_archive:
file_timetuple = self.zip_archive.getinfo(file_path).date_time file_timetuple = self.zip_archive.getinfo(file_path).date_time

View File

@@ -34,20 +34,6 @@ class DumpsysReceivers(DumpsysReceiversArtifact, BugReportModule):
self.results = results if results else {} self.results = results if results else {}
def check_indicators(self) -> None:
for result in self.results:
if self.indicators:
receiver_name = self.results[result][0]["receiver"]
# return IoC if the stix2 process name a substring of the receiver name
ioc = self.indicators.check_receiver_prefix(receiver_name)
if ioc:
self.results[result][0]["matched_indicator"] = ioc
self.detected.append(result)
continue
def run(self) -> None: def run(self) -> None:
content = self._get_dumpstate_file() content = self._get_dumpstate_file()
if not content: if not content:

View File

@@ -52,7 +52,9 @@ class Indicators:
if os.path.isfile(path) and path.lower().endswith(".stix2"): if os.path.isfile(path) and path.lower().endswith(".stix2"):
self.parse_stix2(path) self.parse_stix2(path)
elif os.path.isdir(path): elif os.path.isdir(path):
for file in glob.glob(os.path.join(path, "**", "*.stix2"), recursive=True): for file in glob.glob(
os.path.join(path, "**", "*.stix2", recursive=True)
):
self.parse_stix2(file) self.parse_stix2(file)
else: else:
self.log.error( self.log.error(
@@ -766,30 +768,6 @@ class Indicators:
return None return None
def check_receiver_prefix(self, receiver_name: str) -> Union[dict, None]:
"""Check the provided receiver name against the list of indicators.
An IoC match is detected when a substring of the receiver matches the indicator
:param app_id: App ID to check against the list of indicators
:type app_id: str
:returns: Indicator details if matched, otherwise None
"""
if not receiver_name:
return None
for ioc in self.get_iocs("app_ids"):
if ioc["value"].lower() in receiver_name.lower():
self.log.warning(
'Found a known suspicious receiver with name "%s" '
'matching indicators from "%s"',
receiver_name,
ioc["name"],
)
return ioc
return None
def check_android_property_name(self, property_name: str) -> Optional[dict]: def check_android_property_name(self, property_name: str) -> Optional[dict]:
"""Check the android property name against the list of indicators. """Check the android property name against the list of indicators.

View File

@@ -3,4 +3,4 @@
# 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/
MVT_VERSION = "2.7.0" MVT_VERSION = "2.6.1"

View File

@@ -631,10 +631,6 @@
"build": "16H81", "build": "16H81",
"version": "12.5.7" "version": "12.5.7"
}, },
{
"version": "12.5.8",
"build": "16H88"
},
{ {
"build": "17A577", "build": "17A577",
"version": "13.0" "version": "13.0"
@@ -903,10 +899,6 @@
"version": "15.8.5", "version": "15.8.5",
"build": "19H394" "build": "19H394"
}, },
{
"version": "15.8.6",
"build": "19H402"
},
{ {
"build": "20A362", "build": "20A362",
"version": "16.0" "version": "16.0"
@@ -1016,10 +1008,6 @@
"version": "16.7.12", "version": "16.7.12",
"build": "20H364" "build": "20H364"
}, },
{
"version": "16.7.14",
"build": "20H370"
},
{ {
"version": "17.0", "version": "17.0",
"build": "21A327" "build": "21A327"
@@ -1176,18 +1164,6 @@
"version": "18.7.3", "version": "18.7.3",
"build": "22H217" "build": "22H217"
}, },
{
"version": "18.7.4",
"build": "22H218"
},
{
"version": "18.7.5",
"build": "22H311"
},
{
"version": "18.7.6",
"build": "22H320"
},
{ {
"version": "26", "version": "26",
"build": "23A341" "build": "23A341"
@@ -1203,17 +1179,5 @@
{ {
"version": "26.2", "version": "26.2",
"build": "23C55" "build": "23C55"
},
{
"version": "26.2.1",
"build": "23C71"
},
{
"version": "26.3",
"build": "23D127"
},
{
"version": "26.3.1",
"build": "23D8133"
} }
] ]