Commit Graph
1343 Commits
Author SHA1 Message Date
Janik Besendorf c615d5e304 Merge main into PR #957 and preserve ADB state bounds 2026-09-28 16:12:15 +02:00
besendorf 9bdc25f57d Merge pull request #949 from NeuroSaki987/codex/fix-windows-dumpsys-crlf
fix: parse dumpsys ADB output with CRLF line endings
2026-09-28 16:06:05 +02:00
besendorf 8238d80035 Merge branch 'main' into codex/fix-windows-dumpsys-crlf 2026-09-28 16:04:49 +02:00
besendorf 22d97f0a88 Merge pull request #954 from SomeoneUnlicensed/fix/windows-tests
Make the test suite pass on Windows
2026-09-28 16:01:08 +02:00
besendorf d224db5a78 Merge branch 'main' into fix/windows-tests 2026-09-28 15:54:55 +02:00
Janik Besendorf 658c7aa327 Test Windows path normalization and preserve backup safety coverage 2026-09-28 15:47:15 +02:00
itzzdev09 9316ac7fb0 Bound the ADB manager state by matching braces 2026-09-28 02:07:03 +05:30
besendorf 6ddb2c9671 Merge branch 'main' into codex/fix-windows-dumpsys-crlf 2026-09-27 22:25:21 +02:00
besendorf 165d87b97f Merge pull request #956 from itzzdev09/fix/battery-daily-malformed-update
Skip a malformed battery daily Update line instead of aborting
v2026.9.28
2026-09-27 20:44:35 +02:00
itzzdev09 d2e992e40e Skip a malformed battery daily Update line instead of aborting 2026-09-28 00:07:17 +05:30
besendorf aa3ae511ad Merge branch 'main' into codex/fix-windows-dumpsys-crlf 2026-09-27 19:24:54 +02:00
besendorf 7700749cfa Merge pull request #950 from itzzdev09/fix/url-domain-www-prefix
Strip only a whole www. prefix from parsed domains
2026-09-27 19:24:23 +02:00
besendorf 6eb1a53873 Merge branch 'main' into fix/url-domain-www-prefix 2026-09-27 19:20:51 +02:00
Neruo Saki 02aa23cb81 Merge branch 'main' into codex/fix-windows-dumpsys-crlf 2026-09-27 17:54:12 +08:00
besendorf 9c3db579ee Merge pull request #946 from va-resident/fix/accessibility-installed-service-count
Carry the accessibility service count the dump states
2026-09-25 19:38:10 +02:00
besendorf 983e970443 Merge branch 'main' into fix/accessibility-installed-service-count 2026-09-25 19:36:53 +02:00
besendorf cfaeb99bf7 Merge pull request #944 from va-resident/fix/bugreport-oem-wrapper-archive
Descend into an OEM wrapper archive in check-bugreport
2026-09-25 19:35:42 +02:00
besendorf 00a5f11124 Merge branch 'main' into fix/accessibility-installed-service-count 2026-09-25 19:30:09 +02:00
va-resident b1b9958f4d Descend into an OEM wrapper archive in check-bugreport
MIUI / HyperOS hands out a zip of app logs, ANR traces and tcpdump captures
with the real bugreport-<device>-<timestamp>.zip nested inside. The outer
archive carries none of the entry points the bug report modules read, so every
module reported it found no files and check-bugreport still exited 0 with an
empty result — an empty analysis that looks like a finished one.

Name the entry points once in modules/bugreport/base.py, next to the
_get_dumpstate_file() that tries them, and when an archive has none of them,
open its zip members in memory and use the first one that does.

Measured over 44 bug report collections: the 14 wrapper collections go from 0
artifacts to 260 artifacts and 586638 records; the 30 normal collections are
unchanged, the descent being unreachable for them.

Fixes #935
2026-09-25 21:21:40 +04:00
va-resident ad6caa155f Track accessibility state sections and unnamed services per user
Two issues from review:

* The set of printed state sections was global, so a section printed for
  one user decided the flags of another. A user with an installed list and
  no enabled section was marked enabled=False and got a LOW "installed, not
  enabled" alert. Sections are now tracked per user.

* A count-only record was added only when a user had no named component at
  all. A dump stating installedServiceCount=2 and naming one service read
  as complete. The count-only record is now added whenever the stated count
  exceeds the distinct named components for that user, and carries the
  difference in a new unnamed_service_count field. The module summary adds
  up that remainder.
2026-09-25 20:55:01 +04:00
besendorf c2030e6e84 Merge pull request #945 from va-resident/fix/bugreport-section-duration-line
Do not treat a section's timing line as a section boundary
2026-09-25 18:36:28 +02:00
SomeoneUnlicensed 99b3b5f014 Make the test suite pass on Windows
The Filesystem module stored the paths of an iOS dump with the separator
of the system checking it, so on Windows the process and file path
indicators, which split paths on "/", never matched. It now stores them
as POSIX paths.

The rest are test fixes:
- the completion install tests also redirect USERPROFILE, which
  Path.home() reads on Windows; they wrote to the real home folder;
- the plugin table helpers accept the light header Rich draws on
  consoles that cannot show the heavy one;
- the completion test quotes the command path, whose backslashes were
  dropped when COMP_WORDS was split;
- tests that need symbolic links or the sqlite3 binary are skipped when
  those are not available;
- two assertions no longer depend on the path separator or on the line
  ending text mode writes.
2026-09-24 21:29:45 +03:00
besendorf 9ca4254649 Fix formatting in test_artifact_dumpsys_adb.py 2026-09-24 09:38:18 -07:00
besendorf f1e52b297a test: cover mixed ADB state line endings
Regression for CRLF after the manager brace and LF after the outer brace.
2026-09-24 04:40:48 -07:00
besendorf 8c5278f540 Refactor JSON extraction logic in dumpsys_adb.py 2026-09-24 04:37:25 -07:00
besendorf df3b130ca8 Test w-prefixed subdomain indicator matching
Add test for URL matching with prefixed subdomain.
2026-09-24 04:34:52 -07:00
besendorf 3bb652f2d2 Test w-prefixed shortener detection 2026-09-24 04:29:33 -07:00
besendorf c399608345 Merge branch 'main' into fix/url-domain-www-prefix 2026-09-24 13:24:47 +02:00
itzzdev09 7f19d96297 Strip only a whole www. prefix from parsed domains 2026-09-23 19:27:08 +05:30
StarRailHub ff5ebf73cc fix: parse dumpsys ADB output with CRLF line endings 2026-09-23 12:11:58 +08:00
va-resident bdbc07a3b3 Carry the accessibility service count the dump states
Most builds never print the `installed services: {...}` block. They state
installedServiceCount=N in the user's attributes line and list nothing, so the
parser recorded no service at all and MVT logged "Identified a total of 0
accessibility services" — an artifact that says "no accessibility services"
about a dump that said there are five. The dump pasted in #744 is itself an
example: it states installedServiceCount=6 and names none.

Parse the count per user and, for a user whose services the dump did not list,
keep one record carrying it, raised as LOW: a count is a coverage statement,
not a running service.

Name the service state in the alert and split its severity, as asked on #744:
a service the dump states is switched off is LOW, enabled or bound is MEDIUM,
and a dump that does not state the enabled state stays MEDIUM, because "not
stated" is not "not enabled". A state section the dump never printed now reads
None rather than False. IOC matching is unaffected by the state: a disabled
service still returns CRITICAL on a match.

Measured over 163 bug reports carrying an accessibility section: MEDIUM alerts
305 -> 4, LOW 0 -> 407. The four that stay MEDIUM are the only records in the
set with enabled=True.

Fixes #744
2026-09-22 18:13:57 +03:00
va-resident 6f65eee85a Do not treat a section's timing line as a section boundary
extract_command_section() ends a section at any line starting with "------".
dumpstate prints a section's timing line when THAT section finishes, and it can
land in the middle of the section currently being written, so the section is
truncated at an arbitrary point and the rest is silently dropped.

Skip the timing line instead of returning it: it never reaches a parser as
content, and the real "------ <NAME> ------" boundary still ends the section.

Measured over 40 bug reports carrying a dumpstate: SYSTEM PROPERTIES was
truncated on 6 of them, losing 6559 properties, and 3 parsed no property at
all. On one Samsung archive getprop goes from 418 to 1259 properties and from
0 to 473 ro.* ones, bringing back ro.product.model,
ro.build.version.security_patch and ro.boot.verifiedbootstate. The other 34
archives are byte-identical.

Fixes #938
2026-09-22 18:11:42 +03:00
besendorf 777db67291 Merge pull request #926 from mvt-project/besendorf-patch-5
Remove adb-shell dependency from pyproject.toml
2026-09-22 15:36:20 +02:00
besendorf a0bc233eec Merge branch 'main' into besendorf-patch-5 2026-09-22 15:34:36 +02:00
Janik Besendorf 1db5d239d0 Merge main into PR 926 2026-09-22 15:33:17 +02:00
Donncha Ó Cearbhaill 2426178baa Enable automated PyPI deployments for tagged releases (#943)
* Limit PyPi deploys to tag relaeses

* Remove unnecessary tests and docs from sdist build
2026-09-22 13:02:49 +02:00
besendorf e0697a58e9 Merge pull request #937 from va-resident/fix/mountinfo-rw-both-layers
Fix mountinfo read-write detection across both option layers
v2026.9.21
2026-09-18 17:48:33 +02:00
va-residentandClaude Opus 5 d0fc0379b9 Fix mountinfo read-write detection across both option layers
/proc/PID/mountinfo carries two option sets with different meaning: fields[5]
are the per-mount (VFS) flags, the field after the "-" separator belongs to the
superblock. A mount is writable only if both allow it.

parse_mountinfo() merged both into one list and set
is_read_write = "rw" in options, so a "rw" VFS mount over a read-only
superblock was reported as writable. On stock Xiaomi-family builds that made
every read-only mi_ext customisation overlay a HIGH "system partition is
mounted as read-write".

Require "rw" in both layers, and let "rw" count as a suspicious mount option
only when the mount is actually writable; remount, noatime and nodiratime keep
their current meaning in either layer. mount_options and options_list still
carry both layers, so nothing downstream loses data.

Measured on 50 bug reports carrying mountinfo - the 28 where the rule changes
the output plus 22 controls, 14 brands, Android 10-16: HIGH 94 -> 0,
MEDIUM 119 -> 25, the 22 controls identical, and 36233 mount entries parsed
either way. Every removed alert is a read-only superblock under a "rw" VFS
mount; no report gains an alert. A partition that really is writable still
raises the HIGH, which the new test asserts explicitly.

Fixes #936

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 18:43:06 +03:00
besendorf 7973e90589 Merge pull request #934 from mvt-project/chore/update-iphone-backup-decrypt-0.10.0
Bump iphone_backup_decrypt to 0.10.0
2026-09-18 10:36:59 +02:00
Janik Besendorf 935747519f Bump iphone_backup_decrypt to 0.10.0 2026-09-18 10:22:21 +02:00
besendorf 178ac52aab Merge pull request #927 from mvt-project/auto/add-new-ios-releases
[auto] Update iOS releases and versions
2026-09-15 03:37:45 +02:00
DonnchaC f20fd14075 Add new iOS versions and build numbers 2026-09-15 00:37:07 +00:00
Janik Besendorf a9d787c446 Remove unused libusb1 and refresh dependency lockfile 2026-09-14 00:30:48 +02:00
besendorf 518becb825 Remove adb-shell dependency from pyproject.toml
not used anymore
2026-09-13 20:06:15 +02:00
besendorf a3d289c709 Merge pull request #924 from mvt-project/auto/add-new-ios-releases
[auto] Update iOS releases and versions
v2026.9.14
2026-09-08 19:52:51 +01:00
DonnchaC b4b0a06470 Add new iOS versions and build numbers 2026-09-08 18:16:19 +00:00
Donncha Ó Cearbhaill eceafdc785 Keep the test suite out of the user's MVT folders (#923)
The tests read the settings file of whoever runs them, and importing
mvt.common.config writes it back. On a machine whose config.yaml sets
NETWORK_ACCESS_ALLOWED to false, the plugin update and URL batch tests
fail before their mocked requests are reached, and every Command run in
the suite parses the indicators downloaded on that machine, which made
the suite take minutes instead of seconds.

MVT_CONFIG_FOLDER and MVT_DATA_FOLDER in the environment now relocate
the settings file and the downloaded indicators with their update-check
state. The test conftest points both at a throwaway folder before any
mvt module is imported, and removes it at exit. Subprocesses started by
the tests inherit the variables; the isolated interpreter helper already
gives them a temporary home.

On the machine that prompted this the suite goes from 6 failures in six
minutes to none in seven seconds, and config.yaml is left alone.
2026-09-08 01:04:43 +01:00
Donncha Ó Cearbhaill ffae240355 Skip AppleDouble sidecars when listing a sysdiagnose (#922)
Device-generated sysdiagnose archives carry a ._name entry beside every
file that has extended attributes, an ACL or Finder info; one iOS 26
archive held 1234 of them among 3648 members, and the count grows with
each release. bsdtar folds them back into the file on extraction and
hides them from listings, but tarfile returns them as regular members,
so check-sysdiagnose extracted them and handed them to every module.
A module that globs for plists or logs then tries to parse AppleDouble
headers and logs one warning per sidecar.

Leave them out of the file list, both for archives and for folders
extracted on a system that keeps them as files.
2026-09-08 01:03:05 +01:00
3623e430f4 Defer indicator loading until first use (#919)
* Defer indicator loading until first use

* Load indicators once at the start of a run

The lazy property loads indicators wherever they are first read, which
in Command.run() is inside the module loop, after init(). For
check-androidqf that means the whole acquisition is walked before a
missing --iocs file is reported, and the STIX parsing lines land between
the module list and the first module.

Read the indicators once after the module list is settled, so that a bad
--module name still loads nothing, and hand the same object to every
module. check-iocs reads them once too, so that a wrong --iocs path is
reported even when no stored result matches a module.

* Drop two test assertions the change does not need

Retrying after a failed indicator load is incidental to the property
rather than a requirement, so nothing should pin it. The exact wording
of the check-backup rejection belongs to that command's own tests; exit
code 1 already shows the path was rejected before indicators were
loaded.

* Create the output folder and command.log when a run starts

Command.__init__ created the --output folder and attached the
command.log handler, so `--list-modules -o out` or a rejected target
path left an empty folder behind for a run that never happened.

Do both at the top of run(), before the module list is resolved so that
its warnings still reach the log. The nested commands run by
check-androidqf re-attach the handler at the start of their runs, as
they did at construction. Lines the CLI logs between construction and
run(), such as the target path being checked, no longer reach
command.log; info.json records the target path.

This is the remaining part of #888.

* Cache the indicators with functools.cached_property

A property with a setter and a backing attribute does by hand what
cached_property does: compute on first read, store the result on the
instance, and accept assignment, which is how nested commands receive
their parent's indicators. A failed load is still not cached.

* Announce the target from the command so that it reaches command.log

The CLI logged which backup, filesystem or acquisition it was about to
check just before run(), which is now before command.log exists, so the
line only reached the console. Each command logs it from init() instead,
which runs once the log is attached, and run() logs the module list
after init() so that the target still comes first. Nested commands
without a target path log nothing, as before.

* Log the Android backup path from the typed local

mypy cannot determine the type of target_path in this command, which the
surrounding lines already silence, so read the announced path from the
local that carries the type instead of adding another ignore.

---------

Co-authored-by: bitmeta69 <206962233+bitmeta69@users.noreply.github.com>
Co-authored-by: besendorf <janik@besendorf.org>
Co-authored-by: Donncha Ó Cearbhaill <donncha.ocearbhaill@amnesty.org>
2026-09-08 01:01:02 +01:00
Donncha Ó Cearbhaill 60e652bafc Read bugreport file timestamps in the device's timezone (#921)
A bugreport zip stores each entry's time as the device's wall clock, and
an unpacked bugreport's mtimes are whatever the extraction left. The
bugreport modules read both as naive local datetimes, so a tombstone's
file_timestamp moved with the analysing machine's timezone and sat three
hours from the crash time the tombstone itself records for a device in
Nairobi.

check-bugreport now resolves the device's timezone once, from --timezone
or from persist.sys.timezone in the dumpstate's SYSTEM PROPERTIES, and
hands it to the modules as device_timezone the way check-androidqf does;
a zone already known, as when androidqf drives the bugreport inside its
own archive, is kept. Zip entry times are read in that zone and the
mtimes of an unpacked bugreport as UTC instants, so convert_datetime_to_iso
writes both in UTC. Without a zone the wall clock is kept naive and a
warning says so, and an unpacked bugreport gets a warning that its file
timestamps are the extraction's, not the device's. BugReportTimestamps
uses the same reading instead of parsing the properties itself.
2026-09-08 00:08:30 +01:00