mirror of
https://github.com/mvt-project/mvt.git
synced 2026-09-03 08:30:51 +02:00
* Share the check-iocs module lists between the CLI and the code check-iocs re-checks the results a previous run stored, so its module list is every module of the platform that could have written one. Each platform's CLI composed that list inline, concatenating the families by hand, so the list existed only inside the click callback: anything else needing to know what check-iocs runs had to build its own copy, and the two could drift apart without a test noticing. Give each platform a command_modules.py holding the one list, and have its CLI assign it. The modules check-iocs runs are unchanged, and a test pins each list to the families it is composed of. * Pin that check-iocs re-checks the results of custom modules check-iocs takes its custom modules from load_custom_modules() like every check-* command and matches result files to modules by slug, so a plugin module's stored results are re-checked whenever it declares the check-iocs pair of its platform; nothing asserted it.
140 lines
4.2 KiB
Python
140 lines
4.2 KiB
Python
# Mobile Verification Toolkit (MVT)
|
|
# Copyright (c) 2021-2026 The MVT 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 json
|
|
import logging
|
|
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
|
|
from mvt.android.cli import cli as android_cli
|
|
from mvt.android.command_modules import ANDROID_CHECK_IOCS_MODULES
|
|
from mvt.common.cmd_check_iocs import CmdCheckIOCS
|
|
from mvt.common.module import MVTModule
|
|
from mvt.ios.cli import cli as ios_cli
|
|
from mvt.ios.command_modules import IOS_CHECK_IOCS_MODULES
|
|
|
|
# Keep the banner of the group callback from checking for updates online.
|
|
OFFLINE = ["--disable-update-check", "--disable-indicator-update-check"]
|
|
|
|
|
|
class CustomResultsModule(MVTModule):
|
|
"""A custom module which declares the check-iocs pair of both platforms."""
|
|
|
|
slug = "custom_results"
|
|
supported_commands = (
|
|
("ios", "check-backup"),
|
|
("ios", "check-iocs"),
|
|
("android", "check-iocs"),
|
|
)
|
|
|
|
checked: list = []
|
|
|
|
def run(self) -> None:
|
|
pass
|
|
|
|
def check_indicators(self) -> None:
|
|
self.checked.append(list(self.results))
|
|
|
|
|
|
class BackupOnlyModule(MVTModule):
|
|
"""A custom module which does not declare check-iocs."""
|
|
|
|
slug = "backup_only"
|
|
supported_commands = (("ios", "check-backup"),)
|
|
|
|
def check_indicators(self) -> None:
|
|
raise AssertionError("must not be re-checked")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"platform, builtin_modules",
|
|
[("ios", IOS_CHECK_IOCS_MODULES), ("android", ANDROID_CHECK_IOCS_MODULES)],
|
|
)
|
|
def test_check_iocs_rechecks_the_stored_results_of_custom_modules(
|
|
platform, builtin_modules, tmp_path, caplog
|
|
):
|
|
# check-iocs matches every <slug>.json in the results folder to the module
|
|
# with that slug, custom modules included, and runs its check_indicators()
|
|
# again over the stored results.
|
|
results = [{"domain": "example.org"}]
|
|
(tmp_path / "custom_results.json").write_text(json.dumps(results))
|
|
(tmp_path / "backup_only.json").write_text(json.dumps(results))
|
|
CustomResultsModule.checked.clear()
|
|
|
|
cmd = CmdCheckIOCS(
|
|
target_path=str(tmp_path),
|
|
custom_modules=[CustomResultsModule, BackupOnlyModule],
|
|
platform=platform,
|
|
)
|
|
cmd.modules = builtin_modules
|
|
|
|
with caplog.at_level(logging.INFO):
|
|
cmd.run()
|
|
|
|
assert CustomResultsModule.checked == [results]
|
|
assert (
|
|
'Loading results from "custom_results.json" with module CustomResultsModule'
|
|
in caplog.text
|
|
)
|
|
# A module declaring only check-backup is not part of check-iocs.
|
|
assert "backup_only.json" not in caplog.text
|
|
|
|
|
|
def test_check_iocs_lists_custom_modules_declaring_the_command(caplog):
|
|
cmd = CmdCheckIOCS(
|
|
custom_modules=[CustomResultsModule, BackupOnlyModule],
|
|
platform="ios",
|
|
)
|
|
cmd.modules = IOS_CHECK_IOCS_MODULES
|
|
|
|
with caplog.at_level(logging.INFO):
|
|
cmd.list_modules()
|
|
|
|
assert "CustomResultsModule" in caplog.text
|
|
assert "BackupOnlyModule" not in caplog.text
|
|
|
|
|
|
LOADED_MODULE = '''
|
|
from mvt.common.module import MVTModule
|
|
|
|
|
|
class LoadedResultsModule(MVTModule):
|
|
"""A module loaded from a file with --load-module."""
|
|
|
|
slug = "loaded_results"
|
|
supported_commands = (("ios", "check-iocs"), ("android", "check-iocs"))
|
|
|
|
def run(self) -> None:
|
|
pass
|
|
|
|
def check_indicators(self) -> None:
|
|
self.log.warning("loaded module checked %d results", len(self.results))
|
|
'''
|
|
|
|
|
|
@pytest.mark.parametrize("cli", [ios_cli, android_cli], ids=["mvt-ios", "mvt-android"])
|
|
def test_check_iocs_loads_custom_modules_from_a_file_on_each_cli(cli, tmp_path, caplog):
|
|
module_path = tmp_path / "loaded_module.py"
|
|
module_path.write_text(LOADED_MODULE)
|
|
results_folder = tmp_path / "results"
|
|
results_folder.mkdir()
|
|
(results_folder / "loaded_results.json").write_text(json.dumps([{"a": 1}]))
|
|
|
|
with caplog.at_level(logging.INFO):
|
|
result = CliRunner().invoke(
|
|
cli,
|
|
[
|
|
*OFFLINE,
|
|
"check-iocs",
|
|
"--load-module",
|
|
str(module_path),
|
|
str(results_folder),
|
|
],
|
|
)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert "loaded module checked 1 results" in caplog.text
|