Fix Android bugreport parser coverage - #871
Open
besendorf wants to merge 3 commits into
Open
Conversation
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR makes Android bugreport parsing match the data that is actually present in the Honor and Samsung reports used for the audit. It fixes parsers that stopped at the wrong boundary, parsed only one subsection, dropped per-user or per-UID context, or assigned values to the wrong record.
The result contract is intentionally cleaned up instead of preserving the old inconsistent shapes. Bugreport output is therefore marked as schema version 2.
The PR also fixes the repository-wide mypy failures caused by invariant module-registry list types. Those annotations do not change runtime module selection.
Compatibility and review notes
output_schema_version: 2from the command metadata to identify the new contract.nullare no longer retained as strings.from/tobecomeperiod_start/period_end, andversbecomesversion_code.Detailed changes
Output contract and section extraction
Files:
src/mvt/common/command.py,src/mvt/android/cmd_check_bugreport.py, andsrc/mvt/android/modules/bugreport/base.py.output_schema_versioncommand metadata and set Android bugreport analysis to version2.extract_command_section()helper for non-dumpsys command blocks such as system properties and process tables.------, including separators that contain command timing text.Why: the previous GetProp extraction looked for a line equal to
------. Real reports use decorated separator lines, so the parser could consume the rest of dumpstate. The explicit schema version makes the intentional normalized field changes detectable by downstream consumers.Activity and receiver resolver tables
Files:
src/mvt/android/artifacts/package_resolvers.py,dumpsys_package_activities.py,dumpsys_receivers.py, and the receiver bugreport wrapper.resolver_type,key,package_name,component, andfilter_count.Why: both old parsers only entered the Non-Data Actions subsection. MIME and scheme registrations in the reports were silently lost. The receiver parser also returned a different container shape from the activity parser, which made consumers handle equivalent Android structures differently.
Accessibility
Files:
src/mvt/android/artifacts/dumpsys_accessibility.pyand its bugreport wrapper.(user_id, component).package_nameandservice_name.accessibility_toolfor Android's(A11yTool)marker.Why: the old parser handled either installed services or one modern enabled-services line and emitted inconsistent
servicevalues. It could not show whether a service was merely installed, actively enabled or bound, crashed, or associated with another Android user.ADB state and trusted keys
File:
src/mvt/android/artifacts/dumpsys_adb.py.ABX\x00.last_connected: nullwhen ABX numeric metadata cannot be recovered reliably.Why: ADB keystores in the audited reports were ABX rather than plaintext XML. The old code left that data as raw bytes and did not expose the trusted keys. The recovery deliberately retains only fields MVT can parse reliably instead of inventing values from damaged binary tokens.
AppOps
File:
src/mvt/android/artifacts/dumpsys_appops.py.Uid ...block rather than depending on aUid 0starting point.state,capability,appWidgetVisible, and default operation modes on each package.name,mode, andentriesshape.eventfield and tolerate operations without timestamped entries.Why: the previous state machine lost UID context and several event attributes, could associate trailing entries with the wrong package or permission, and omitted running operations. These are fields already present in dumpsys and useful for understanding how and when an operation was used.
Battery Daily
File:
src/mvt/android/artifacts/dumpsys_battery_daily.py.period_startandperiod_end.verstoversion_code.occurrencescount rather than silently discarding them.update,uninstall, ordowngrade.previous_version_codeas an integer for downgrade records.Why: the report contains time-of-day information and repeated update records that were previously lost. Normalized integer versions prevent string-shaped data from leaking into comparisons and output, while explicit actions make the result self-describing.
Battery History
File:
src/mvt/android/artifacts/dumpsys_battery_history.py.RESET:TIME/TIME markers.timestampfor elapsed events when an anchor is available while retaining the originaltime_elapsed.Why: the old
if/elifparser emitted at most one event per line, recognized only narrow job/wake formats, and did not reconstruct timestamps from the anchors already present in the report. Decorated scheduler names also produced incorrect package names.Database operations
File:
src/mvt/android/artifacts/dumpsys_dbinfo.py.connection_numberandis_primary.pidandduration_msvalues.timestamp,action,status, SQL text, the operationpath, and the enclosingpool_path.Why: the previous parser effectively handled only a narrow operation line and cleared pool state too early, dropping later connections. Its regex also discarded duration, status, connection metadata, and explicit per-operation paths that Android had already provided.
Packages and permissions
Files:
src/mvt/android/artifacts/dumpsys_packages.pyand its bugreport wrapper.Packages:andHidden system packages:, recordingpackage_typeasactiveorhidden_system.app_id, version name/code, min/target SDK, install/update timestamps, and installer.nullto their corresponding Python/JSON types.firstInstallTimeto user 0 when the report uses the older layout.Why: the old parser stopped before hidden packages, flattened user-specific state, treated typed values as strings, and could place runtime permissions into the wrong scope. That made multi-user reports ambiguous and caused valid package/permission data to disappear.
Platform compatibility overrides
Files:
src/mvt/android/artifacts/dumpsys_platform_compat.pyand its bugreport wrapper.ChangeId(... rawOverrides={...})record instead of only change ID168419799.change_id, optionalchange_name, enabled/disabledchange_state,overridable,package_name, and typedoverride_value.Why: raw compatibility overrides are not an uninstall list. The old hard-coded change ID both mischaracterized the source and discarded overrides for every other Android compatibility change.
System properties
Files:
src/mvt/android/artifacts/getprop.pyand its bugreport wrapper.[name]: [value]property line.Why: the previous non-greedy expression required a non-empty value, and the wrapper's separator check could feed later dumpstate sections into GetProp.
Filesystem timestamps
File:
src/mvt/android/modules/bugreport/fs_timestamps.py.persist.sys.timezonefrom the report's system properties.modified_time.modified_time_utcwhen the timezone can be resolved.timezoneandtimestamp_source(zip_metadataorfilesystem_metadata).nulland log the unknown zone instead of guessing when the timezone cannot be resolved.Why: ZIP timestamps do not carry a timezone offset. Treating a naive archive timestamp as UTC changes the represented instant. The new metadata makes the source and conversion explicit.
New Processes bugreport module
Files:
src/mvt/android/artifacts/processes.py,src/mvt/android/modules/bugreport/processes.py, and module registration.Why: process-table columns vary by Android/vendor build. The fixed-position parser shifted values into the wrong fields whenever optional columns appeared, and the parser was not registered for normal bugreport analysis.
New Settings bugreport module
Files:
src/mvt/android/artifacts/settings.py,src/mvt/android/modules/bugreport/settings.py, and module registration.secure:user_0and retain each setting's emitted value.Why: bugreports already contain SettingsProvider data, but only AndroidQF inputs had a settings path. Without a bugreport parser, security-relevant settings present in the supplied files were ignored.
New Mounts bugreport module
Files:
src/mvt/android/artifacts/mounts.py,src/mvt/android/modules/bugreport/mounts.py, and module registration./proc/<pid>/mountinforecords from extracted bugreport files.is_system_partitionandis_read_write.Why: the bugreports contain per-process mount namespaces, while the existing artifact only understood the simpler
mountcommand form and was not wired into bugreport analysis. Deduplication keeps shared namespaces useful without emitting the same mount once per process.Tombstones: plaintext and protobuf together
Files:
src/mvt/android/artifacts/tombstone_crashes.pyandsrc/mvt/android/modules/bugreport/tombstones.py.tombstone_NNandtombstone_NN.pbunder onecrash_id.sourcesentry for each available representation with file name, file timestamp, parse status, parse error, and its parsed record.differences, excluding source file metadata.Why: Android bugreports commonly contain both representations of the same crash. The old module emitted duplicates and treated the choice as either/or. It also lost the usable representation if one parser failed. The canonical result gives consumers one crash while preserving provenance, parse failures, and source disagreements for forensic review.
Module registry typing and mypy
Files: the AndroidQF, Android backup, intrusion-log, iOS backup, iOS filesystem, and iOS mixed module package registries.
list[type[MVTModule]].Why: Python lists are invariant. Mypy inferred each constant as a list of one specialized module base and then rejected assignment or concatenation into
Command.modules: list[type[MVTModule]]. Declaring the intended shared base type resolves all five repository-wide errors without casts and without changing behavior.Strict parser result typing
Files: Accessibility, AppOps, Battery History, Packages, Processes, and Settings artifacts.
commandfield tostrbefore applying string operations; malformed non-string commands are ignored for IOC matching.Why: the CI environment uses mypy 2.3, which no longer accepts these empty result initializers without an explicit type and correctly treats dynamically built process fields as
str | int. These annotations describe the parser output already produced at runtime; the guard also prevents a malformed process record from reachingrsplit.