From 3a2cc24e2af66ef1c4826f2fd41ed5f05aaa7504 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 4 Jan 2017 22:58:54 +0000 Subject: [PATCH] Add in plugin function capability (and make plugin validation centralized on plugin construction). --- volatility/framework/exceptions.py | 4 ++++ volatility/framework/interfaces/plugins.py | 24 ++++++++++++++++------ volatility/framework/plugins/__init__.py | 13 ++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/volatility/framework/exceptions.py b/volatility/framework/exceptions.py index 459321088..d62ae85a2 100644 --- a/volatility/framework/exceptions.py +++ b/volatility/framework/exceptions.py @@ -10,6 +10,10 @@ class VolatilityException(Exception): """Class to allow filtering of all VolatilityExceptions""" +class PluginRequirementException(VolatilityException): + """Class to allow plugins to indicate that a requirement has not been fulfilled""" + + class SymbolError(VolatilityException): """Thrown when a symbol lookup has failed""" diff --git a/volatility/framework/interfaces/plugins.py b/volatility/framework/interfaces/plugins.py index 3879a32e3..c3243a164 100644 --- a/volatility/framework/interfaces/plugins.py +++ b/volatility/framework/interfaces/plugins.py @@ -4,11 +4,15 @@ They are called and carry out some algorithms on data stored in layers using obj """ # Configuration interfaces must be imported separately, since we're part of interfaces and can't import ourselves +import logging from abc import ABCMeta, abstractmethod +from volatility.framework import exceptions from volatility.framework import validity from volatility.framework.interfaces import configuration as interfaces_configuration +vollog = logging.getLogger(__name__) + # # Plugins @@ -32,7 +36,11 @@ class PluginInterface(interfaces_configuration.ConfigurableInterface, validity.V def __init__(self, context, config_path): super().__init__(context, config_path) - # self.validate() + # Plugins self validate on construction, it makes it more difficult to work with them, but then + # the validation doesn't need to be repeated over and over again by externals + if not self.validate(context, config_path): + vollog.warning("Plugin failed validation") + raise exceptions.PluginRequirementException("The plugin configuration failed to validate") @classmethod def get_requirements(cls): @@ -40,20 +48,24 @@ class PluginInterface(interfaces_configuration.ConfigurableInterface, validity.V return [] @classmethod - def validate(self, context, config_path): + def validate(cls, context, config_path): """Ensures that the plugin's requirements have been met appropriately""" result_set = [(config_path + "." + requirement.name, requirement.validate(context, config_path)) for requirement - in self.get_requirements() if not requirement.optional] + in cls.get_requirements() if not requirement.optional] return all([r for _, r in result_set]) @abstractmethod def run(self): """Executes the functionality of the code + .. note:: This method expects `self.validate` to have been called to ensure all necessary options have been provided + :return: a TreeGrid object that can then be passed to a Renderer. :rtype: interfaces.renderers.TreeGrid """ -# TODO: Needs to say what it can/can't handle (validate context) -# TODO: Needs to offer available options' -# TODO: Figure out how to handle global config options + def __call__(self, **kwargs): + """Method to make a plugin callable. It must still have been instantiated with a context and a config_path""" + for k, v in kwargs: + self.config[k] = v + return self.run() diff --git a/volatility/framework/plugins/__init__.py b/volatility/framework/plugins/__init__.py index e69de29bb..c0bd58e71 100644 --- a/volatility/framework/plugins/__init__.py +++ b/volatility/framework/plugins/__init__.py @@ -0,0 +1,13 @@ +from volatility.framework import interfaces + + +def plugin_function(plugin, context, config_path, **kwargs): + """Run the plugin as a function""" + if not isinstance(plugin, interfaces.plugins.PluginInterface): + raise TypeError("Plugin must be a PluginInterface derived object") + if not isinstance(context, interfaces.context.ContextInterface): + raise TypeError("Context must be a ContextInterface derived object") + if not isinstance(config_path, str): + raise TypeError("Config_path must a be string") + constructed = plugin(context, config_path) + return constructed(**kwargs)