diff --git a/mvt/android/modules/adb/__init__.py b/mvt/android/modules/adb/__init__.py index ab36c5d..0c1fde7 100644 --- a/mvt/android/modules/adb/__init__.py +++ b/mvt/android/modules/adb/__init__.py @@ -6,6 +6,7 @@ from .chrome_history import ChromeHistory from .dumpsys_accessibility import DumpsysAccessibility from .dumpsys_activities import DumpsysActivities +from .dumpsys_appops import DumpsysAppOps from .dumpsys_battery_daily import DumpsysBatteryDaily from .dumpsys_battery_history import DumpsysBatteryHistory from .dumpsys_dbinfo import DumpsysDBInfo @@ -25,5 +26,5 @@ from .whatsapp import Whatsapp ADB_MODULES = [ChromeHistory, SMS, Whatsapp, Processes, Getprop, Settings, SELinuxStatus, DumpsysBatteryHistory, DumpsysBatteryDaily, DumpsysReceivers, DumpsysActivities, DumpsysAccessibility, - DumpsysDBInfo, DumpsysFull, Packages, Logcat, RootBinaries, - Files] + DumpsysDBInfo, DumpsysFull, DumpsysAppOps, Packages, Logcat, + RootBinaries, Files] diff --git a/mvt/android/modules/adb/dumpsys_appops.py b/mvt/android/modules/adb/dumpsys_appops.py new file mode 100644 index 0000000..4747adc --- /dev/null +++ b/mvt/android/modules/adb/dumpsys_appops.py @@ -0,0 +1,66 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 The MVT Project Authors. +# Use of this software is governed by the MVT License 1.1 that can be found at +# https://license.mvt.re/1.1/ + +import logging +import re + +from mvt.android.parsers.dumpsys import parse_dumpsys_appops + +from .base import AndroidExtraction + +log = logging.getLogger(__name__) + + +class DumpsysAppOps(AndroidExtraction): + """This module extracts records from App-op Manager.""" + + slug = "dumpsys_appops" + + def __init__(self, file_path=None, base_folder=None, output_folder=None, + serial=None, fast_mode=False, log=None, results=[]): + super().__init__(file_path=file_path, base_folder=base_folder, + output_folder=output_folder, fast_mode=fast_mode, + log=log, results=results) + + def serialize(self, record): + records = [] + for perm in record["permissions"]: + if "entries" not in perm: + continue + + for entry in perm["entries"]: + if "timestamp" in entry: + records.append({ + "timestamp": entry["timestamp"], + "module": self.__class__.__name__, + "event": entry["access"], + "data": f"{record['package_id']} access to {perm['name']} : {entry['access']}", + }) + + return records + + def check_indicators(self): + for result in self.results: + if self.indicators: + ioc = self.indicators.check_app_id(result.get("package_name")) + if ioc: + result["matched_indicator"] = ioc + self.detected.append(result) + continue + for perm in result["permissions"]: + if perm["name"] == "REQUEST_INSTALL_PACKAGES" and perm["access"] == "allow": + self.log.info("Package %s with REQUEST_INSTALL_PACKAGES permission", result["package_id"]) + + + + def run(self): + self._adb_connect() + output = self._adb_command("dumpsys appops") + self._adb_disconnect() + + self.results = parse_dumpsys_appops(output) + + self.log.info("Extracted a total of %d records from app-ops manager", + len(self.results)) diff --git a/mvt/android/modules/bugreport/__init__.py b/mvt/android/modules/bugreport/__init__.py index 129ccaa..e0f9546 100644 --- a/mvt/android/modules/bugreport/__init__.py +++ b/mvt/android/modules/bugreport/__init__.py @@ -5,6 +5,7 @@ from .accessibility import Accessibility from .activities import Activities +from .appops import Appops from .battery_daily import BatteryDaily from .battery_history import BatteryHistory from .dbinfo import DBInfo @@ -12,5 +13,5 @@ from .getprop import Getprop from .packages import Packages from .receivers import Receivers -BUGREPORT_MODULES = [Accessibility, Activities, BatteryDaily, BatteryHistory, - DBInfo, Getprop, Packages, Receivers] +BUGREPORT_MODULES = [Accessibility, Activities, Appops, BatteryDaily, + BatteryHistory, DBInfo, Getprop, Packages, Receivers] diff --git a/mvt/android/modules/bugreport/appops.py b/mvt/android/modules/bugreport/appops.py new file mode 100644 index 0000000..950dacc --- /dev/null +++ b/mvt/android/modules/bugreport/appops.py @@ -0,0 +1,76 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 The MVT Project Authors. +# Use of this software is governed by the MVT License 1.1 that can be found at +# https://license.mvt.re/1.1/ + +import logging + +from mvt.android.parsers import parse_dumpsys_appops + +from .base import BugReportModule + +log = logging.getLogger(__name__) + + +class Appops(BugReportModule): + """This module extracts information on package from App-Ops Manager.""" + + def __init__(self, file_path=None, base_folder=None, output_folder=None, + serial=None, fast_mode=False, log=None, results=[]): + super().__init__(file_path=file_path, base_folder=base_folder, + output_folder=output_folder, fast_mode=fast_mode, + log=log, results=results) + + def serialize(self, record): + records = [] + for perm in record["permissions"]: + if "entries" not in perm: + continue + + for entry in perm["entries"]: + if "timestamp" in entry: + records.append({ + "timestamp": entry["timestamp"], + "module": self.__class__.__name__, + "event": entry["access"], + "data": f"{record['package_id']} access to {perm['name']} : {entry['access']}", + }) + + return records + + def check_indicators(self): + for result in self.results: + if self.indicators: + ioc = self.indicators.check_app_id(result.get("package_name")) + if ioc: + result["matched_indicator"] = ioc + self.detected.append(result) + continue + for perm in result["permissions"]: + if perm["name"] == "REQUEST_INSTALL_PACKAGES" and perm["access"] == "allow": + self.log.info("Package %s with REQUEST_INSTALL_PACKAGES permission", result["package_id"]) + + def run(self): + content = self._get_dumpstate_file() + if not content: + self.log.error("Unable to find dumpstate file. Did you provide a valid bug report archive?") + return + + lines = [] + in_appops = False + for line in content.decode(errors="ignore").splitlines(): + if line.strip() == "DUMP OF SERVICE appops:": + in_appops = True + continue + + if not in_appops: + continue + + if line.strip().startswith("------------------------------------------------------------------------------"): + break + + lines.append(line) + + self.results = parse_dumpsys_appops("\n".join(lines)) + + self.log.info("Identified a total of %d packages in App-Ops Manager", len(self.results)) diff --git a/mvt/android/parsers/__init__.py b/mvt/android/parsers/__init__.py index c8b0507..a882b92 100644 --- a/mvt/android/parsers/__init__.py +++ b/mvt/android/parsers/__init__.py @@ -4,6 +4,7 @@ # https://license.mvt.re/1.1/ from .dumpsys import (parse_dumpsys_accessibility, + parse_dumpsys_appops, parse_dumpsys_activity_resolver_table, parse_dumpsys_battery_daily, parse_dumpsys_battery_history, parse_dumpsys_dbinfo, diff --git a/mvt/android/parsers/dumpsys.py b/mvt/android/parsers/dumpsys.py index b079ae2..eba050a 100644 --- a/mvt/android/parsers/dumpsys.py +++ b/mvt/android/parsers/dumpsys.py @@ -4,6 +4,8 @@ # https://license.mvt.re/1.1/ import re +from datetime import datetime +from mvt.common.utils import convert_timestamp_to_iso def parse_dumpsys_accessibility(output): @@ -285,3 +287,81 @@ def parse_dumpsys_receiver_resolver_table(output): }) return results + + +def parse_dumpsys_appops(output): + results = [] + perm = {} + package = {} + entry = {} + uid = None + in_packages = False + + for line in output.splitlines(): + if line.startswith(" Uid 0:"): + in_packages = True + + if not in_packages: + continue + + # In packages + if line.startswith(" Uid "): + uid = line[6:-1] + continue + + if line.startswith(" Package "): + if entry != {}: + perm["entries"].append(entry) + entry = {} + if package != {}: + if perm != {}: + package["permissions"].append(perm) + perm = {} + results.append(package) + package = { + "package_id": line[12:-1], + "permissions": [], + "uid": uid, + } + continue + + if line.startswith(" ") and line[6] != " ": + # Permission name READ_EXTERNAL_STORAGE (allow): + if entry != {}: + perm["entries"].append(entry) + entry = {} + if perm != {}: + package["permissions"].append(perm) + perm = {} + perm["name"] = line.split()[0] + perm["entries"] = [] + if len(line.split()) > 1: + perm["access"] = line.split()[1][1:-2] + continue + + if line.startswith(" "): + # Permission entry like Reject: [fg-s]2021-05-19 22:02:52.054 (-314d1h25m2s33ms) + if entry != {}: + perm["entries"].append(entry) + entry = {} + entry["access"] = line.split(":")[0].strip() + entry["type"] = line[line.find("[")+1:line.find("]")] + try: + entry["timestamp"] = convert_timestamp_to_iso( + datetime.strptime( + line[line.find("]")+1:line.find("(")].strip(), + "%Y-%m-%d %H:%M:%S.%f")) + except ValueError: + # Invalid date format + pass + + if line.strip() == "": + break + + if entry != {}: + perm["entries"].append(entry) + if perm != {}: + package["permissions"].append(perm) + if package != {}: + results.append(package) + return results diff --git a/tests/android/test_bugreport_appops.py b/tests/android/test_bugreport_appops.py new file mode 100644 index 0000000..26f5412 --- /dev/null +++ b/tests/android/test_bugreport_appops.py @@ -0,0 +1,31 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 The MVT Project Authors. +# Use of this software is governed by the MVT License 1.1 that can be found at +# https://license.mvt.re/1.1/ + +import logging +import os +from pathlib import Path + +from mvt.common.indicators import Indicators +from mvt.common.module import run_module +from mvt.android.modules.bugreport.appops import Appops + +from ..utils import get_artifact_folder + + +class TestAppopsModule: + def test_appops_parsing(self): + fpath = os.path.join(get_artifact_folder(), "android_data/bugreport/") + m = Appops(base_folder=fpath, log=logging, results=[]) + folder_files = [] + parent_path = Path(fpath).absolute().as_posix() + for root, subdirs, subfiles in os.walk(os.path.abspath(fpath)): + for file_name in subfiles: + folder_files.append(os.path.relpath(os.path.join(root, file_name), parent_path)) + m.from_folder(fpath, folder_files) + + run_module(m) + assert len(m.results) == 12 + assert len(m.timeline) == 16 + assert len(m.detected) == 0 diff --git a/tests/android/test_dumpsys_parser.py b/tests/android/test_dumpsys_parser.py new file mode 100644 index 0000000..b4f14dc --- /dev/null +++ b/tests/android/test_dumpsys_parser.py @@ -0,0 +1,29 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 The MVT Project Authors. +# Use of this software is governed by the MVT License 1.1 that can be found at +# https://license.mvt.re/1.1/ + +import hashlib +import logging + +from mvt.android.parsers.dumpsys import parse_dumpsys_appops + +from ..utils import get_artifact + + +class TestDumpsysParsing: + def test_appops_parsing(self): + file = get_artifact("android_data/dumpsys_appops.txt") + with open(file) as f: + data = f.read() + res = parse_dumpsys_appops(data) + + assert len(res) == 12 + assert res[0]["package_id"] == "com.android.phone" + assert res[0]["uid"] == "0" + assert len(res[0]["permissions"]) == 1 + assert res[0]["permissions"][0]["name"] == "MANAGE_IPSEC_TUNNELS" + assert res[0]["permissions"][0]["access"] == "allow" + assert res[6]["package_id"] == "com.sec.factory.camera" + assert len(res[6]["permissions"][1]["entries"]) == 1 + assert len(res[11]["permissions"]) == 4 diff --git a/tests/artifacts/android_data/bugreport/dumpstate.txt b/tests/artifacts/android_data/bugreport/dumpstate.txt new file mode 100644 index 0000000..aace052 --- /dev/null +++ b/tests/artifacts/android_data/bugreport/dumpstate.txt @@ -0,0 +1,126 @@ +Currently running services: + AAS + AODManagerService + CodecSolution + CustomFrequencyManagerService + DeviceRootKeyService + DirEncryptService + DisplaySolution + DockObserver + EngineeringModeService + FMPlayer + HcmManagerService + HermesService + HqmManagerService +------------------------------------------------------------------------------- +DUMP OF SERVICE AAS: +--------- 0.002s was the duration of dumpsys AAS, ending at: 2022-03-29 23:14:26 +------------------------------------------------------------------------------- +DUMP OF SERVICE appops: +Current AppOps Service state: + Settings: + top_state_settle_time=+30s0ms + fg_service_state_settle_time=+10s0ms + bg_state_settle_time=+1s0ms + + Op mode watchers: + Op COARSE_LOCATION: + #0: ModeCallback{b8f1a14 watchinguid=-1 flags=0x1 from uid=1000 pid=4098} + #1: ModeCallback{e9062d4 watchinguid=-1 flags=0x1 from uid=u0a12 pid=13172} + Op READ_CALL_LOG: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + Op WRITE_CALL_LOG: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + Op READ_SMS: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + Op RECEIVE_SMS: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + Op RECEIVE_MMS: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + + Uid 0: + state=cch + Package com.android.phone: + MANAGE_IPSEC_TUNNELS (allow): + Package com.sec.epdg: + MANAGE_IPSEC_TUNNELS (deny): + Uid 1000: + state=pers + LEGACY_STORAGE: mode=allow + Package com.samsung.android.provider.filterprovider: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Package com.samsung.android.smartswitchassistant: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Package com.samsung.clipboardsaveservice: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + RUN_IN_BACKGROUND (allow): + Package com.skms.android.agent: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Package com.sec.factory.camera: + RECORD_AUDIO (allow): + RUN_IN_BACKGROUND (allow): + Access: [pers-s] 2022-03-29 18:37:30.315 (-4h50m23s772ms) + Uid u0a103: + state=cch + COARSE_LOCATION: mode=ignore + LEGACY_STORAGE: mode=allow + Package com.facebook.katana: + READ_CONTACTS (allow): + Access: [bg-tpd] 2022-03-07 18:05:34.325 (-22d4h22m19s762ms) + WRITE_SMS (ignore): + Reject: [fg-s]2021-05-19 22:02:52.054 (-314d1h25m2s33ms) + Reject: [bg-s]2022-03-10 19:35:06.426 (-19d2h52m47s661ms) + Reject: [cch-s]2022-03-29 18:48:02.923 (-4h39m51s164ms) + WAKE_LOCK (allow): + Access: [fg-s] 2021-05-19 22:02:49.186 (-314d1h25m4s901ms) + Access: [bg-s] 2022-03-29 23:03:03.763 (-24m50s324ms) duration=+33ms + Access: [cch-s] 2022-03-07 14:57:11.635 (-22d7h30m42s452ms) + TOAST_WINDOW (allow): + READ_PHONE_STATE (allow): + Access: [fg-s] 2021-05-19 22:02:53.336 (-314d1h25m0s751ms) + Access: [bg-s] 2022-03-24 21:06:52.731 (-5d1h21m1s356ms) + Access: [cch-s] 2022-03-29 18:57:58.524 (-4h29m55s563ms) + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + READ_DEVICE_IDENTIFIERS (deny): + Reject: [fg-s]2021-05-19 22:02:53.434 (-314d1h25m0s653ms) + Reject: [bg-s]2022-03-24 21:06:56.538 (-5d1h20m57s549ms) + Reject: [cch-s]2022-03-29 18:57:58.644 (-4h29m55s443ms) + Uid u0a104: + state=cch + COARSE_LOCATION: mode=ignore + LEGACY_STORAGE: mode=ignore + Package org.mozilla.firefox: + REQUEST_INSTALL_PACKAGES (allow): + Uid u0a105: + state=cch + Package com.android.carrierdefaultapp: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Uid u0a106: + state=cch + LEGACY_STORAGE: mode=allow + Package com.samsung.safetyinformation: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Uid u0a107: + state=cch + LEGACY_STORAGE: mode=allow + Package com.sec.android.app.clockpackage: + WAKE_LOCK (allow): + Access: [bg-s] 2022-03-29 18:38:31.440 (-4h49m22s647ms) duration=+126ms + Access: [cch-s] 2021-06-07 12:47:06.642 (-295d10h40m47s445ms) + TOAST_WINDOW (allow): + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + +--------- 0.053s was the duration of dumpsys appops, ending at: 2022-03-29 23:14:27 +------------------------------------------------------------------------------- +DUMP OF SERVICE appwidget: +Providers: + [0] provider ProviderId{user:0, app:10107, cmp:ComponentInfo{com.sec.android.app.clockpackage/com.sec.android.app.clockpackage.alarmwidget.ClockAlarmWidgetProvider}} + diff --git a/tests/artifacts/android_data/bugreport/main_entry.txt b/tests/artifacts/android_data/bugreport/main_entry.txt new file mode 100644 index 0000000..2198d7a --- /dev/null +++ b/tests/artifacts/android_data/bugreport/main_entry.txt @@ -0,0 +1 @@ +dumpstate.txt diff --git a/tests/artifacts/android_data/dumpsys_appops.txt b/tests/artifacts/android_data/dumpsys_appops.txt new file mode 100644 index 0000000..a23036a --- /dev/null +++ b/tests/artifacts/android_data/dumpsys_appops.txt @@ -0,0 +1,100 @@ +Current AppOps Service state: + Settings: + top_state_settle_time=+30s0ms + fg_service_state_settle_time=+10s0ms + bg_state_settle_time=+1s0ms + + Op mode watchers: + Op COARSE_LOCATION: + #0: ModeCallback{b8f1a14 watchinguid=-1 flags=0x1 from uid=1000 pid=4098} + #1: ModeCallback{e9062d4 watchinguid=-1 flags=0x1 from uid=u0a12 pid=13172} + Op READ_CALL_LOG: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + Op WRITE_CALL_LOG: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + Op READ_SMS: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + Op RECEIVE_SMS: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + Op RECEIVE_MMS: + #0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098} + + Uid 0: + state=cch + Package com.android.phone: + MANAGE_IPSEC_TUNNELS (allow): + Package com.sec.epdg: + MANAGE_IPSEC_TUNNELS (deny): + Uid 1000: + state=pers + LEGACY_STORAGE: mode=allow + Package com.samsung.android.provider.filterprovider: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Package com.samsung.android.smartswitchassistant: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Package com.samsung.clipboardsaveservice: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + RUN_IN_BACKGROUND (allow): + Package com.skms.android.agent: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Package com.sec.factory.camera: + RECORD_AUDIO (allow): + RUN_IN_BACKGROUND (allow): + Access: [pers-s] 2022-03-29 18:37:30.315 (-4h50m23s772ms) + Uid u0a103: + state=cch + COARSE_LOCATION: mode=ignore + LEGACY_STORAGE: mode=allow + Package com.facebook.katana: + READ_CONTACTS (allow): + Access: [bg-tpd] 2022-03-07 18:05:34.325 (-22d4h22m19s762ms) + WRITE_SMS (ignore): + Reject: [fg-s]2021-05-19 22:02:52.054 (-314d1h25m2s33ms) + Reject: [bg-s]2022-03-10 19:35:06.426 (-19d2h52m47s661ms) + Reject: [cch-s]2022-03-29 18:48:02.923 (-4h39m51s164ms) + WAKE_LOCK (allow): + Access: [fg-s] 2021-05-19 22:02:49.186 (-314d1h25m4s901ms) + Access: [bg-s] 2022-03-29 23:03:03.763 (-24m50s324ms) duration=+33ms + Access: [cch-s] 2022-03-07 14:57:11.635 (-22d7h30m42s452ms) + TOAST_WINDOW (allow): + READ_PHONE_STATE (allow): + Access: [fg-s] 2021-05-19 22:02:53.336 (-314d1h25m0s751ms) + Access: [bg-s] 2022-03-24 21:06:52.731 (-5d1h21m1s356ms) + Access: [cch-s] 2022-03-29 18:57:58.524 (-4h29m55s563ms) + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + READ_DEVICE_IDENTIFIERS (deny): + Reject: [fg-s]2021-05-19 22:02:53.434 (-314d1h25m0s653ms) + Reject: [bg-s]2022-03-24 21:06:56.538 (-5d1h20m57s549ms) + Reject: [cch-s]2022-03-29 18:57:58.644 (-4h29m55s443ms) + Uid u0a104: + state=cch + COARSE_LOCATION: mode=ignore + LEGACY_STORAGE: mode=ignore + Package org.mozilla.firefox: + REQUEST_INSTALL_PACKAGES (allow): + Uid u0a105: + state=cch + Package com.android.carrierdefaultapp: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Uid u0a106: + state=cch + LEGACY_STORAGE: mode=allow + Package com.samsung.safetyinformation: + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): + Uid u0a107: + state=cch + LEGACY_STORAGE: mode=allow + Package com.sec.android.app.clockpackage: + WAKE_LOCK (allow): + Access: [bg-s] 2022-03-29 18:38:31.440 (-4h49m22s647ms) duration=+126ms + Access: [cch-s] 2021-06-07 12:47:06.642 (-295d10h40m47s445ms) + TOAST_WINDOW (allow): + READ_EXTERNAL_STORAGE (allow): + WRITE_EXTERNAL_STORAGE (allow): diff --git a/tests/test_check_bugreport.py b/tests/test_check_bugreport.py new file mode 100644 index 0000000..225532a --- /dev/null +++ b/tests/test_check_bugreport.py @@ -0,0 +1,20 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 The MVT Project Authors. +# Use of this software is governed by the MVT License 1.1 that can be found at +# https://license.mvt.re/1.1/ + +import os +from click.testing import CliRunner + +from mvt.android.cli import check_bugreport + +from .utils import get_artifact_folder + +class TestCheckBugreportCommand: + def test_check(self): + runner = CliRunner() + path = os.path.join(get_artifact_folder(), "android_data/bugreport/") + result = runner.invoke(check_bugreport, [path]) + assert result.exit_code == 0 + +