diff --git a/volatility/cli/__init__.py b/volatility/cli/__init__.py index 5017aa2cd..3e00d0f56 100644 --- a/volatility/cli/__init__.py +++ b/volatility/cli/__init__.py @@ -135,6 +135,10 @@ class CommandLine(object): for requirement in configurables_list[configurable].get_requirements(): value = vargs.get(requirement.name, None) if value is not None: + if isinstance(requirement, requirements.ListRequirement): + if not isinstance(value, list): + raise TypeError("Configuration for ListRequirement was not a list") + value = [requirement.element_type(x) for x in value] if not inspect.isclass(configurables_list[configurable]): config_path = configurables_list[configurable].config_path else: @@ -228,15 +232,8 @@ class CommandLine(object): if "type" in additional: del additional["type"] elif isinstance(requirement, volatility.framework.configuration.requirements.ListRequirement): - if requirement.min_elements != requirement.max_elements: - if requirement.min_elements > 0: - additional["nargs"] = "+" - additional["nargs"] = "*" - # We can't test for min_elements > 1 or going over max_elements but rather than warning here, - # we expect the plugin to fail validation instead - else: - additional["nargs"] = requirement.max_elements - additional["type"] = requirement.element_type + # This is a trick to generate a list of values + additional["type"] = lambda x: x.split(',') elif isinstance(requirement, volatility.framework.configuration.requirements.ChoiceRequirement): additional["type"] = str additional["choices"] = requirement.choices diff --git a/volatility/framework/configuration/requirements.py b/volatility/framework/configuration/requirements.py index 4e0870dc8..6e3c4da54 100644 --- a/volatility/framework/configuration/requirements.py +++ b/volatility/framework/configuration/requirements.py @@ -81,9 +81,15 @@ class ListRequirement(interfaces_configuration.RequirementInterface): def unsatisfied(self, context: interfaces.context.ContextInterface, config_path: str) -> typing.List[str]: """Check the types on each of the returned values and their number and then call the element type's check for each one""" - value = self.config_value(context, config_path) - if value is None: - raise ValueError("No value stored in the configuration") + default = None + value = self.config_value(context, config_path, default) + if not value and self.min_elements > 0: + vollog.log(constants.LOGLEVEL_V, "ListRequirement Unsatisfied - ListRequirement has non-zero min_elements") + return [interfaces_configuration.path_join(config_path, self.name)] + if value == default: + # We need to differentiate between no value and an empty list + vollog.log(constants.LOGLEVEL_V, "ListRequirement Unsatisfied - Value was not specified") + return [interfaces_configuration.path_join(config_path, self.name)] if not isinstance(value, list): # TODO: Check this is the correct response for an error raise ValueError("Unexpected config value found: {}".format(repr(value))) @@ -94,7 +100,8 @@ class ListRequirement(interfaces_configuration.RequirementInterface): vollog.log(constants.LOGLEVEL_V, "TypeError - Too many values provided to list option.") return [interfaces_configuration.path_join(config_path, self.name)] if not all([self._check_type(element, self.element_type) for element in value]): - vollog.log(constants.LOGLEVEL_V, "TypeError - At least one element in the list is not of the correct type.") + vollog.log(constants.LOGLEVEL_V, + "TypeError - At least one element in the list is not of the correct type.") return [interfaces_configuration.path_join(config_path, self.name)] return [] diff --git a/volatility/framework/interfaces/automagic.py b/volatility/framework/interfaces/automagic.py index 3d06f3627..4b9e99e5a 100644 --- a/volatility/framework/interfaces/automagic.py +++ b/volatility/framework/interfaces/automagic.py @@ -69,14 +69,16 @@ class AutomagicInterface(interfaces_configuration.ConfigurableInterface, metacla :param context: Context on which to operate :param config_path: Configuration path of the top-level requirement - :param requirement: Top-level requirement whose subrequirements will all be searched + :param requirement_root: Top-level requirement whose subrequirements will all be searched + :param requirement_type: Type of requirement to find + :param shortcut: Only returns requirements that live under unsatisfied requirements :return: A list of tuples containing the config_path, sub_config_path and requirement identifying the SymbolRequirements """ sub_config_path = interfaces_configuration.path_join(config_path, requirement_root.name) results = [] recurse = not shortcut if isinstance(requirement_root, requirement_type): - if not shortcut or requirement_root.unsatisfied(context, config_path): + if recurse or requirement_root.unsatisfied(context, config_path): results.append((config_path, sub_config_path, requirement_root)) else: recurse = True