From f4bf3f362ba31e1bc58b53514e15008483f45d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Cearbhaill?= Date: Thu, 17 Oct 2024 12:28:42 +0200 Subject: [PATCH] Refactor CLI help messages to make the CLI code more readable and maintainable. (#554) * - modified help message string storage and referencing for consistency - grammar correction to docs/android/download_apks.md - changed ios backup help message from a format string that would reference and explicitly print the environment variable, to printing the name of the environment variable itself * Fix formatting for help message refactor --------- Co-authored-by: jazzy0verflow Co-authored-by: kh0rvus <50286871+kh0rvus@users.noreply.github.com> --- docs/android/download_apks.md | 2 +- src/mvt/android/cli.py | 82 ++++++++++++++--------------------- src/mvt/common/help.py | 39 +++++++++++++++-- src/mvt/ios/cli.py | 82 +++++++++++++---------------------- 4 files changed, 100 insertions(+), 105 deletions(-) diff --git a/docs/android/download_apks.md b/docs/android/download_apks.md index 8351146..b7ea886 100644 --- a/docs/android/download_apks.md +++ b/docs/android/download_apks.md @@ -1,6 +1,6 @@ # Downloading APKs from an Android phone -MVT allows to attempt to download all available installed packages (APKs) in order to further inspect them and potentially identify any which might be malicious in nature. +MVT allows you to attempt to download all available installed packages (APKs) from a device in order to further inspect them and potentially identify any which might be malicious in nature. You can do so by launching the following command: diff --git a/src/mvt/android/cli.py b/src/mvt/android/cli.py index b2fb829..8e9086f 100644 --- a/src/mvt/android/cli.py +++ b/src/mvt/android/cli.py @@ -9,16 +9,28 @@ import click from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.help import ( - HELP_MSG_ANDROID_BACKUP_PASSWORD, - HELP_MSG_FAST, - HELP_MSG_HASHES, + HELP_MSG_VERSION, + HELP_MSG_OUTPUT, + HELP_MSG_SERIAL, + HELP_MSG_DOWNLOAD_APKS, + HELP_MSG_DOWNLOAD_ALL_APKS, + HELP_MSG_VIRUS_TOTAL, + HELP_MSG_APK_OUTPUT, + HELP_MSG_APKS_FROM_FILE, + HELP_MSG_VERBOSE, + HELP_MSG_CHECK_ADB, HELP_MSG_IOC, + HELP_MSG_FAST, HELP_MSG_LIST_MODULES, HELP_MSG_MODULE, HELP_MSG_NONINTERACTIVE, - HELP_MSG_OUTPUT, - HELP_MSG_SERIAL, - HELP_MSG_VERBOSE, + HELP_MSG_ANDROID_BACKUP_PASSWORD, + HELP_MSG_CHECK_BUGREPORT, + HELP_MSG_CHECK_ANDROID_BACKUP, + HELP_MSG_CHECK_ANDROIDQF, + HELP_MSG_HASHES, + HELP_MSG_CHECK_IOCS, + HELP_MSG_STIX2, ) from mvt.common.logo import logo from mvt.common.updates import IndicatorsUpdates @@ -52,7 +64,7 @@ def cli(): # ============================================================================== # Command: version # ============================================================================== -@cli.command("version", help="Show the currently installed version of MVT") +@cli.command("version", help=HELP_MSG_VERSION) def version(): return @@ -61,30 +73,14 @@ def version(): # Command: download-apks # ============================================================================== @cli.command( - "download-apks", - help="Download all or only non-system installed APKs", - context_settings=CONTEXT_SETTINGS, + "download-apks", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_DOWNLOAD_APKS ) @click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL) +@click.option("--all-apks", "-a", is_flag=True, help=HELP_MSG_DOWNLOAD_ALL_APKS) +@click.option("--virustotal", "-V", is_flag=True, help=HELP_MSG_VIRUS_TOTAL) +@click.option("--output", "-o", type=click.Path(exists=False), help=HELP_MSG_APK_OUTPUT) @click.option( - "--all-apks", - "-a", - is_flag=True, - help="Extract all packages installed on the phone, including system packages", -) -@click.option("--virustotal", "-V", is_flag=True, help="Check packages on VirusTotal") -@click.option( - "--output", - "-o", - type=click.Path(exists=False), - help="Specify a path to a folder where you want to store the APKs", -) -@click.option( - "--from-file", - "-f", - type=click.Path(exists=True), - help="Instead of acquiring from phone, load an existing packages.json file for " - "lookups (mainly for debug purposes)", + "--from-file", "-f", type=click.Path(exists=True), help=HELP_MSG_APKS_FROM_FILE ) @click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.pass_context @@ -127,11 +123,7 @@ def download_apks(ctx, all_apks, virustotal, output, from_file, serial, verbose) # ============================================================================== # Command: check-adb # ============================================================================== -@cli.command( - "check-adb", - help="Check an Android device over ADB", - context_settings=CONTEXT_SETTINGS, -) +@cli.command("check-adb", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_ADB) @click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL) @click.option( "--iocs", @@ -195,9 +187,7 @@ def check_adb( # Command: check-bugreport # ============================================================================== @cli.command( - "check-bugreport", - help="Check an Android Bug Report", - context_settings=CONTEXT_SETTINGS, + "check-bugreport", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_BUGREPORT ) @click.option( "--iocs", @@ -243,7 +233,9 @@ def check_bugreport(ctx, iocs, output, list_modules, module, verbose, bugreport_ # Command: check-backup # ============================================================================== @cli.command( - "check-backup", help="Check an Android Backup", context_settings=CONTEXT_SETTINGS + "check-backup", + context_settings=CONTEXT_SETTINGS, + help=HELP_MSG_CHECK_ANDROID_BACKUP, ) @click.option( "--iocs", @@ -303,9 +295,7 @@ def check_backup( # Command: check-androidqf # ============================================================================== @cli.command( - "check-androidqf", - help="Check data collected with AndroidQF", - context_settings=CONTEXT_SETTINGS, + "check-androidqf", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_ANDROIDQF ) @click.option( "--iocs", @@ -368,11 +358,7 @@ def check_androidqf( # ============================================================================== # Command: check-iocs # ============================================================================== -@cli.command( - "check-iocs", - help="Compare stored JSON results to provided indicators", - context_settings=CONTEXT_SETTINGS, -) +@cli.command("check-iocs", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_IOCS) @click.option( "--iocs", "-i", @@ -399,11 +385,7 @@ def check_iocs(ctx, iocs, list_modules, module, folder): # ============================================================================== # Command: download-iocs # ============================================================================== -@cli.command( - "download-iocs", - help="Download public STIX2 indicators", - context_settings=CONTEXT_SETTINGS, -) +@cli.command("download-iocs", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_STIX2) def download_indicators(): ioc_updates = IndicatorsUpdates() ioc_updates.update() diff --git a/src/mvt/common/help.py b/src/mvt/common/help.py index 32986e0..0cca7ab 100644 --- a/src/mvt/common/help.py +++ b/src/mvt/common/help.py @@ -3,16 +3,49 @@ # Use of this software is governed by the MVT License 1.1 that can be found at # https://license.mvt.re/1.1/ -# Help messages of repeating options. +# Help messages of generic options. +HELP_MSG_VERSION = "Show the currently installed version of MVT" HELP_MSG_OUTPUT = "Specify a path to a folder where you want to store JSON results" HELP_MSG_IOC = "Path to indicators file (can be invoked multiple time)" HELP_MSG_FAST = "Avoid running time/resource consuming features" HELP_MSG_LIST_MODULES = "Print list of available modules and exit" HELP_MSG_MODULE = "Name of a single module you would like to run instead of all" HELP_MSG_NONINTERACTIVE = "Don't ask interactive questions during processing" -HELP_MSG_ANDROID_BACKUP_PASSWORD = "The backup password to use for an Android backup" HELP_MSG_HASHES = "Generate hashes of all the files analyzed" HELP_MSG_VERBOSE = "Verbose mode" +HELP_MSG_CHECK_IOCS = "Compare stored JSON results to provided indicators" +HELP_MSG_STIX2 = "Download public STIX2 indicators" -# Android-specific. +# IOS Specific +HELP_MSG_DECRYPT_BACKUP = "Decrypt an encrypted iTunes backup" +HELP_MSG_BACKUP_DESTINATION = ( + "Path to the folder where the decrypted backup should be stored" +) +HELP_MSG_IOS_BACKUP_PASSWORD = ( + "Password to use to decrypt the backup (or, set the {MVT_IOS_BACKUP_PASSWORD} " + "environment variable)" +) +HELP_MSG_BACKUP_KEYFILE = ( + "File containing raw encryption key to use to decrypt the backup" +) +HELP_MSG_EXTRACT_KEY = "Extract decryption key from an iTunes backup" +HELP_MSG_CHECK_IOS_BACKUP = "Extract artifacts from an iTunes backup" +HELP_MSG_CHECK_FS = "Extract artifacts from a full filesystem dump" + +# Android Specific HELP_MSG_SERIAL = "Specify a device serial number or HOST:PORT connection string" +HELP_MSG_DOWNLOAD_APKS = "Download all or only non-system installed APKs" +HELP_MSG_ANDROID_BACKUP_PASSWORD = "The backup password to use for an Android backup" +HELP_MSG_DOWNLOAD_ALL_APKS = ( + "Extract all packages installed on the phone, including system packages" +) +HELP_MSG_VIRUS_TOTAL = "Check packages on VirusTotal" +HELP_MSG_APK_OUTPUT = "Specify a path to a folder where you want to store the APKs" +HELP_MSG_APKS_FROM_FILE = ( + "Instead of acquiring APKs from a phone, load an existing packages.json file for " + "lookups (mainly for debug purposes)" +) +HELP_MSG_CHECK_ADB = "Check an Android device over ADB" +HELP_MSG_CHECK_BUGREPORT = "Check an Android Bug Report" +HELP_MSG_CHECK_ANDROID_BACKUP = "Check an Android Backup" +HELP_MSG_CHECK_ANDROIDQF = "Check data collected with AndroidQF" diff --git a/src/mvt/ios/cli.py b/src/mvt/ios/cli.py index e38c0bc..a74eb32 100644 --- a/src/mvt/ios/cli.py +++ b/src/mvt/ios/cli.py @@ -11,15 +11,6 @@ import click from rich.prompt import Prompt from mvt.common.cmd_check_iocs import CmdCheckIOCS -from mvt.common.help import ( - HELP_MSG_FAST, - HELP_MSG_HASHES, - HELP_MSG_IOC, - HELP_MSG_LIST_MODULES, - HELP_MSG_MODULE, - HELP_MSG_OUTPUT, - HELP_MSG_VERBOSE, -) from mvt.common.logo import logo from mvt.common.options import MutuallyExclusiveOption from mvt.common.updates import IndicatorsUpdates @@ -28,7 +19,25 @@ from mvt.common.utils import ( init_logging, set_verbose_logging, ) - +from mvt.common.help import ( + HELP_MSG_VERSION, + HELP_MSG_DECRYPT_BACKUP, + HELP_MSG_BACKUP_DESTINATION, + HELP_MSG_IOS_BACKUP_PASSWORD, + HELP_MSG_BACKUP_KEYFILE, + HELP_MSG_HASHES, + HELP_MSG_EXTRACT_KEY, + HELP_MSG_IOC, + HELP_MSG_OUTPUT, + HELP_MSG_FAST, + HELP_MSG_LIST_MODULES, + HELP_MSG_MODULE, + HELP_MSG_VERBOSE, + HELP_MSG_CHECK_FS, + HELP_MSG_CHECK_IOCS, + HELP_MSG_STIX2, + HELP_MSG_CHECK_IOS_BACKUP, +) from .cmd_check_backup import CmdIOSCheckBackup from .cmd_check_fs import CmdIOSCheckFS from .decrypt import DecryptBackup @@ -55,7 +64,7 @@ def cli(): # ============================================================================== # Command: version # ============================================================================== -@cli.command("version", help="Show the currently installed version of MVT") +@cli.command("version", help=HELP_MSG_VERSION) def version(): return @@ -64,31 +73,23 @@ def version(): # Command: decrypt-backup # ============================================================================== @cli.command( - "decrypt-backup", - help="Decrypt an encrypted iTunes backup", - context_settings=CONTEXT_SETTINGS, -) -@click.option( - "--destination", - "-d", - required=True, - help="Path to the folder where to store the decrypted backup", + "decrypt-backup", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_DECRYPT_BACKUP ) +@click.option("--destination", "-d", required=True, help=HELP_MSG_BACKUP_DESTINATION) @click.option( "--password", "-p", cls=MutuallyExclusiveOption, - help="Password to use to decrypt the backup (or, set " - f"{MVT_IOS_BACKUP_PASSWORD} environment variable)", mutually_exclusive=["key_file"], + help=HELP_MSG_IOS_BACKUP_PASSWORD, ) @click.option( "--key-file", "-k", cls=MutuallyExclusiveOption, type=click.Path(exists=True), - help="File containing raw encryption key to use to decrypt " "the backup", mutually_exclusive=["password"], + help=HELP_MSG_BACKUP_KEYFILE, ) @click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES) @click.argument("BACKUP_PATH", type=click.Path(exists=True)) @@ -145,22 +146,15 @@ def decrypt_backup(ctx, destination, password, key_file, hashes, backup_path): # Command: extract-key # ============================================================================== @cli.command( - "extract-key", - help="Extract decryption key from an iTunes backup", - context_settings=CONTEXT_SETTINGS, -) -@click.option( - "--password", - "-p", - help="Password to use to decrypt the backup (or, set " - f"{MVT_IOS_BACKUP_PASSWORD} environment variable)", + "extract-key", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_EXTRACT_KEY ) +@click.option("--password", "-p", help=HELP_MSG_IOS_BACKUP_PASSWORD) @click.option( "--key-file", "-k", - help="Key file to be written (if unset, will print to STDOUT)", required=False, type=click.Path(exists=False, file_okay=True, dir_okay=False, writable=True), + help=HELP_MSG_BACKUP_KEYFILE, ) @click.argument("BACKUP_PATH", type=click.Path(exists=True)) def extract_key(password, key_file, backup_path): @@ -195,9 +189,7 @@ def extract_key(password, key_file, backup_path): # Command: check-backup # ============================================================================== @cli.command( - "check-backup", - help="Extract artifacts from an iTunes backup", - context_settings=CONTEXT_SETTINGS, + "check-backup", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_IOS_BACKUP ) @click.option( "--iocs", @@ -247,11 +239,7 @@ def check_backup( # ============================================================================== # Command: check-fs # ============================================================================== -@cli.command( - "check-fs", - help="Extract artifacts from a full filesystem dump", - context_settings=CONTEXT_SETTINGS, -) +@cli.command("check-fs", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_FS) @click.option( "--iocs", "-i", @@ -299,11 +287,7 @@ def check_fs(ctx, iocs, output, fast, list_modules, module, hashes, verbose, dum # ============================================================================== # Command: check-iocs # ============================================================================== -@cli.command( - "check-iocs", - help="Compare stored JSON results to provided indicators", - context_settings=CONTEXT_SETTINGS, -) +@cli.command("check-iocs", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_IOCS) @click.option( "--iocs", "-i", @@ -330,11 +314,7 @@ def check_iocs(ctx, iocs, list_modules, module, folder): # ============================================================================== # Command: download-iocs # ============================================================================== -@cli.command( - "download-iocs", - help="Download public STIX2 indicators", - context_settings=CONTEXT_SETTINGS, -) +@cli.command("download-iocs", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_STIX2) def download_iocs(): ioc_updates = IndicatorsUpdates() ioc_updates.update()