dumpsys prints a blank line after every namespace block and after a
change history, and the generation registry and any vendor dumps follow
the last block before the section trailer. A record ran until the next
`_id:` line, heading or trailer, so the last row of the section
absorbed those dumps into whichever field came last.
A blank line now closes the record being read; lines that follow it
without an `_id:` are skipped until the next row or heading. The fixture
carries a generation registry after its last block, modelled on the
AOSP dump, and the last row is pinned to exactly its own fields.
A dumpsys settings row can end in `tag:` and, on some vendor builds, in
`isValuePreservedInRestore:` or a bare `notPreservedInRestore` token.
The parser only peeled `default:` and `defaultSystemSet:` off the end
of a record, so on a row without a default those tokens stayed inside
the value, and on a row with one they landed in `defaultSystemSet`. A
value of `0 tag:null` is not the safe value `0`, so a setting at its
safe value was reported as dangerous; these are the false positives
measured in #912.
The metadata keys are ordered fields like the rest of the record, so
`_split_fields` reads them now, which also replaces the separate
handling of the default. The bare token has no `key:` shape and is
printed last, so it is stripped first and recorded as
`isValuePreservedInRestore: false`.
The bugreport fixture gains the three row shapes from #912: a restore
flag after `defaultSystemSet:`, and a `tag:` or a bare token directly
after the value. Two of them sit on dangerous settings at their safe
value, so the unchanged alert count of one is the false-positive check.
The `dumpsys settings` parser matched a single regex per line, which
truncated every value that spans more than one line and, when
`defaultSystemSet:` did not fall on the first line, left the trailing
`default:` metadata inside the value. It also keyed results by setting
name within a namespace, so a name recorded twice kept only the last row
and a row without a `pkg:` field was dropped entirely.
Replace it with a line loop that accumulates one record at a time and
splits the `key:value` fields once the whole record has been read.
Results become a list of records carrying the fields dumpsys prints:
namespace, user, _id, name, value, pkg, default and defaultSystemSet,
plus the per-setting change history. History timestamps are printed
without a year, so they are resolved against the "ending at:" time of
the section and serialized into the timeline. This shows which package
changed a security-relevant setting, and when.
The androidqf settings module shares this artifact, so it now emits the
same record shape.
Keys are matched as bare prefixes, so `Caused by:` inside an abort message
reaches the `Cause` key, fails the key comparison and raises — and the
per-line loop turns that into an error that drops the entire text
tombstone, stack trace included.
A key mismatch means "this line is not that key", not "this file is
broken": decline the line and let the remaining keys have their turn. A
line with no colon is declined the same way instead of raising on the
unpack. The same trap has a second form in the field, HiSilicon/Huawei
tombstones printing `code around pc:` against the `code` key.
from_ab() raises InvalidAndroidBackup instead of exiting when it runs as a
sub-command, which check-androidqf catches to skip the backup modules. The
two password branches still called sys.exit(1) unconditionally, and since
run_backup_cmd() runs inside finish(), that ended the parent run before the
intrusion-logs command and before the timeline, alerts, urls, info and run
manifest were stored — leaving an output directory that looks complete but
has no alerts.json.
Also drop "as backup.ab is malformed" from the skip warning: it covers a
missing or wrong password too.
* intrusion_logs: alert on certificate events and run heuristics without IOCs
SecurityEvent.check_indicators() returned early when no indicator set was
loaded, so none of its heuristic alerts (key integrity, wipe failure, crypto
self-test, certificate events) reached the alert store on a default run. On
top of that, cert_authority_installed and cert_validation_failure only emitted
log.warning and never alerted even when indicators were present.
Run the heuristic alerts independently of the loaded indicators (matching the
accessibility fix in #807) and surface the two certificate events through the
alert store at medium severity. A successfully installed root CA and a
certificate validation failure are interception/MITM-relevant signals that
belong in the alert report.
Adds regression tests for both certificate events and for heuristics firing
with no indicators loaded.
* intrusion_logs: gate certificate authority install alert on success
Failed install attempts log a warning instead of raising the
"Certificate authority installed" alert. Add a regression test
covering success encoded as bool and as int.
---------
Co-authored-by: John Kavanagh <668351+kavanista@users.noreply.github.com>
Co-authored-by: besendorf <janik@besendorf.org>
* Run bugreport and backup modules during check-androidqf
Adding support to automatically run ADB backup and bugreport modules
automatically when running the check-androidqf command. This is a first
step to deduplicate the code for Android modules.
* Deduplicate modules which are run by the sub-commands.
* Raise the proper NoAndroidQFBackup exception when a back-up isn't found
* Remove check-adb command and update docs
* Remove check-apk code and old dependencies
* Major refactor to add structured alerting and typed indicators
This commit makes a structural change to MVT by changing binary
detected/not detected logic into a structured multi-level system
of alerts. This gives far more power to extend MVT and manage
alerts.
This commit also begins the process of adding proper typing for
key objects used in MVT including Indicators, IndicatorMatches,
and ModuleResults. This will also be keep to programmatically using
the output of MVT.
* Fix up, remove ADB module base
* Rework old detections tracking into stuctured alert levels
* Quote STIX path in log line
* Fix profile events log line
* Close open archive (zip/tar) file handles
* Fix root_binaries and mounts modules to use alertstore
* Update tests to use alertstore instead of detected attribute
* Fix alertstore method calls - use high() instead of warning()
* Fix remaining test errors
- Add log_latest() call in root_binaries to log each alert
- Fix UnboundLocalError in cmd_check_androidqf by initializing bugreport variable
- Remove incorrect backup.close() call since load_backup() returns bytes
- Remove duplicate from_ab method in cmd_check_backup that was using old attributes
* Log alerts on add
* Remove slug from alertstore calls
* update alerts.py
* update alerts.py
* move indicator_match to alert object
* .
* - Remove timeline_detected and route to alertstore
* fix typing for mypy
* Remove unused type imports
* Fix check_receiver_prefix and check_android_property_name
- check_receiver_prefix() used dict syntax (ioc["value"]) on Indicator
dataclass objects from get_iocs(). Changed to ioc.value/ioc.name.
- check_receiver_prefix() returned raw ioc instead of IndicatorMatch.
Now returns IndicatorMatch with descriptive message.
- Fixed return type annotations on both methods to Optional[IndicatorMatch].
- Removed unused Union import.
* Fix residual self.detected usage in packages and dumpsys_receivers
These modules still used self.detected.append() which no longer exists
after the alertstore migration. Converted to alertstore calls:
- packages.py: ROOT_PACKAGES detection → alertstore.high()
- dumpsys_receivers.py: receiver IOC match → alertstore.critical()
* Fix SMS module alertstore.high() call passing slug as message
The first argument was self.get_slug() (module slug) instead of a
human-readable message. The module is already auto-detected via
AlertStore._get_calling_module(). Also removed redundant log_latest().
* Apply suggestions from code review
Fix JSON serialization in `module.save_to_json` and fix argument order in iOS alertstore calls.
Co-authored-by: tes <tesitura@users.noreply.github.com>
* Remove unsupported ADB modules
* Fail removed check-adb command
* Fix alert serialization and logging
* Close sqlite connections in iOS modules
* Fix DEBUG messages not reaching handlers, save_to_json for dictionary results and TypeError on mixed event_time types in safary_history
* add matched_indicator via alertstore instead of directly modifying json objects
* Alert on battery daily uninstall and downgrade
* Lower alert severity to medium for suspicious items
* Switch version to 2026.4.28 CalVer
---------
Co-authored-by: Donncha Ó Cearbhaill <donncha.ocearbhaill@amnesty.org>
Co-authored-by: tes <tesitura@users.noreply.github.com>
Co-authored-by: Janik Besendorf <janik.besendorf@reporter-ohne-grenzen.de>
* Run bugreport and backup modules during check-androidqf
Adding support to automatically run ADB backup and bugreport modules
automatically when running the check-androidqf command. This is a first
step to deduplicate the code for Android modules.
* Deduplicate modules which are run by the sub-commands.
* Raise the proper NoAndroidQFBackup exception when a back-up isn't found
* add missing import
* Fix imports and remove duplicate hashes param
* Rename from_folder to from_dir in tests
---------
Co-authored-by: besendorf <janik@besendorf.org>
* Use local timestamp for Files module timeline.
Most other Android timestamps appear to be local time. The
results timeline is more useful if all the timestamps
are consistent. I would prefer to use UTC, but that would
mean converting all the other timestamps to UTC as well. We probably
do not have sufficient information to do that accurately,
especially if the device is moving between timezones..
* Add file timestamp modules to add logs into timeline
* Handle case were we cannot load device timezone
* Fix crash if prop file does not exist
* Move _get_file_modification_time to BugReportModule
* Add backport for timezone and fix Tombstone module to use local time.
* Fix import for backported Zoneinfo
* Fix ruff error
* Add initial parser for ADB dumpsys
* Add ADBState tests and support for AndroidQF and
check-adb
* Handle case where ADB is not available in device dumpsys