diff --git a/mythril/__init__.py b/mythril/__init__.py index 063d3b9b..d1d2caab 100644 --- a/mythril/__init__.py +++ b/mythril/__init__.py @@ -1,6 +1,14 @@ # We use RsT document formatting in docstring. For example :param to mark parameters. # See PEP 287 __docformat__ = "restructuredtext" +import logging # Accept mythril.VERSION to get mythril's current version number from .__version__ import __version__ as VERSION # NOQA +from mythril.plugin.loader import MythrilPluginLoader + +log = logging.getLogger(__name__) + +# Initialise core Mythril Components +log.info("Initializing core Mythril components") +_ = MythrilPluginLoader() diff --git a/mythril/plugin/discovery.py b/mythril/plugin/discovery.py index 783a3f0b..b157dec8 100644 --- a/mythril/plugin/discovery.py +++ b/mythril/plugin/discovery.py @@ -2,6 +2,8 @@ import pkg_resources from mythril.support.support_utils import Singleton from mythril.plugin.interface import MythrilPlugin +from typing import List + class PluginDiscovery(object, metaclass=Singleton): """PluginDiscovery class @@ -9,7 +11,7 @@ class PluginDiscovery(object, metaclass=Singleton): This plugin implements the logic to discover and build plugins in installed python packages """ - # Installed plugins structure. Retreives all modules that have an entry point for mythril.plugins + # Installed plugins structure. Retrieves all modules that have an entry point for mythril.plugins _installed_plugins = { entry_point.name: entry_point.load() for entry_point in pkg_resources.iter_entry_points("mythril.plugins") @@ -26,7 +28,22 @@ class PluginDiscovery(object, metaclass=Singleton): plugin = self._installed_plugins.get(plugin_name) - if plugin is None or not isinstance(plugin, MythrilPlugin): + if plugin is None or not issubclass(plugin, MythrilPlugin): raise ValueError(f"No valid plugin was found for {plugin_name}") - return plugin + return plugin() + + def get_plugins(self, default_enabled=None) -> List[str]: + """ Gets a list of installed mythril plugins + + :param default_enabled: Select plugins that are enabled by default + :return: List of plugin names + """ + if default_enabled is None: + return list(self._installed_plugins.keys()) + + return [ + plugin_name + for plugin_name, plugin_class in self._installed_plugins.items() + if plugin_class.plugin_default_enabled == default_enabled + ] diff --git a/mythril/plugin/interface.py b/mythril/plugin/interface.py index 34f3625f..ead1a5da 100644 --- a/mythril/plugin/interface.py +++ b/mythril/plugin/interface.py @@ -12,6 +12,7 @@ class MythrilPlugin: """ author = "Default Author" + name = "Plugin Name" plugin_license = "All rights reserved." plugin_type = "Mythril Plugin" plugin_version = "0.0.1 " diff --git a/mythril/plugin/loader.py b/mythril/plugin/loader.py index 6c75cf4d..45699d8a 100644 --- a/mythril/plugin/loader.py +++ b/mythril/plugin/loader.py @@ -1,8 +1,11 @@ from mythril.analysis.module import DetectionModule from mythril.plugin.interface import MythrilCLIPlugin, MythrilPlugin +from mythril.plugin.discovery import PluginDiscovery from mythril.support.support_utils import Singleton +from mythril.analysis.module.loader import ModuleLoader + import logging log = logging.getLogger(__name__) @@ -19,12 +22,15 @@ class MythrilPluginLoader(object, metaclass=Singleton): """ def __init__(self): + log.info("Initializing mythril plugin loader") self.loaded_plugins = [] + self._load_default_enabled() def load(self, plugin: MythrilPlugin): """Loads the passed plugin""" if not isinstance(plugin, MythrilPlugin): raise ValueError("Passed plugin is not of type MythrilPlugin") + logging.info(f"Loading plugin: {plugin.name}") log.info(f"Loading plugin: {str(plugin)}") @@ -34,6 +40,16 @@ class MythrilPluginLoader(object, metaclass=Singleton): raise UnsupportedPluginType("Passed plugin type is not yet supported") self.loaded_plugins.append(plugin) - - def _load_detection_module(self, plugin: DetectionModule): - pass + log.info(f"Finished loading plugin: {plugin.name}") + + @staticmethod + def _load_detection_module(plugin: DetectionModule): + """Loads the passed detection module""" + log.info(f"Loading detection module: {plugin.name}") + ModuleLoader().register_module(plugin) + + def _load_default_enabled(self): + log.info("Loading installed analysis modules that are enabled by default") + for plugin_name in PluginDiscovery().get_plugins(default_enabled=True): + plugin = PluginDiscovery().build_plugin(plugin_name) + self.load(plugin)