mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-08 10:47:38 +02:00
Refactor the List and Choice Requirements
These are required by the Automagic interface, and so to prevent importing from outside the interfaces namespace, these two classes were moved into interfaces.
This commit is contained in:
@@ -198,7 +198,7 @@ class CommandLine(object):
|
||||
additional["type"] = requirement.instance_type
|
||||
if isinstance(requirement, requirements.BooleanRequirement):
|
||||
additional["action"] = "store_true"
|
||||
elif isinstance(requirement, requirements.ListRequirement):
|
||||
elif isinstance(requirement, interfaces.configuration.ListRequirement):
|
||||
if requirement.min_elements != requirement.max_elements:
|
||||
if requirement.min_elements > 0:
|
||||
additional["nargs"] = "+"
|
||||
@@ -208,7 +208,7 @@ class CommandLine(object):
|
||||
else:
|
||||
additional["nargs"] = requirement.max_elements
|
||||
additional["type"] = requirement.element_type.instance_type
|
||||
elif isinstance(requirement, requirements.ChoiceRequirement):
|
||||
elif isinstance(requirement, interfaces.configuration.ChoiceRequirement):
|
||||
additional["type"] = str
|
||||
additional["choices"] = requirement.choices
|
||||
else:
|
||||
|
||||
@@ -7,17 +7,17 @@ etc) as well as indicating what they expect to be in the context (such as partic
|
||||
|
||||
import logging
|
||||
|
||||
from volatility.framework import constants, interfaces
|
||||
from volatility.framework.interfaces import configuration as interfaces_configuration
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
# Allow these two to be imported directly from requirements
|
||||
# This helps prevent import loops since other interfaces need to be able to check instances of this
|
||||
TranslationLayerRequirement = interfaces.configuration.TranslationLayerRequirement
|
||||
SymbolRequirement = interfaces.configuration.SymbolRequirement
|
||||
TranslationLayerRequirement = interfaces_configuration.TranslationLayerRequirement
|
||||
SymbolRequirement = interfaces_configuration.SymbolRequirement
|
||||
|
||||
|
||||
class MultiRequirement(interfaces.configuration.RequirementInterface):
|
||||
class MultiRequirement(interfaces_configuration.RequirementInterface):
|
||||
"""Class to hold multiple requirements.
|
||||
|
||||
Technically the Interface could handle this, but it's an interface, so this is a concrete implementation.
|
||||
@@ -27,90 +27,22 @@ class MultiRequirement(interfaces.configuration.RequirementInterface):
|
||||
return self.unsatisfied_children(context, config_path)
|
||||
|
||||
|
||||
class BooleanRequirement(interfaces.configuration.InstanceRequirement):
|
||||
class BooleanRequirement(interfaces_configuration.InstanceRequirement):
|
||||
"""A requirement type that contains a boolean value"""
|
||||
# Note, this must be a separate class in order to differentiate between Booleans and other instance requirements
|
||||
|
||||
|
||||
class IntRequirement(interfaces.configuration.InstanceRequirement):
|
||||
class IntRequirement(interfaces_configuration.InstanceRequirement):
|
||||
"""A requirement type that contains a single integer"""
|
||||
instance_type = int
|
||||
|
||||
|
||||
class StringRequirement(interfaces.configuration.InstanceRequirement):
|
||||
class StringRequirement(interfaces_configuration.InstanceRequirement):
|
||||
"""A requirement type that contains a single unicode string"""
|
||||
# TODO: Maybe add string length limits?
|
||||
instance_type = str
|
||||
|
||||
|
||||
class BytesRequirement(interfaces.configuration.InstanceRequirement):
|
||||
class BytesRequirement(interfaces_configuration.InstanceRequirement):
|
||||
"""A requirement type that contains a byte string"""
|
||||
instance_type = bytes
|
||||
|
||||
|
||||
class ChoiceRequirement(interfaces.configuration.RequirementInterface):
|
||||
"""Allows one from a choice of strings"""
|
||||
|
||||
def __init__(self, choices, *args, **kwargs):
|
||||
"""Constructs the object
|
||||
|
||||
:param choices: A list of possible string options that can be chosen from
|
||||
:type choices: list of str
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not isinstance(choices, list) or any([not isinstance(choice, str) for choice in choices]):
|
||||
raise TypeError("ChoiceRequirement takes a list of strings as choices")
|
||||
self.choices = choices
|
||||
|
||||
def unsatisfied(self, context, config_path):
|
||||
"""Validates the provided value to ensure it is one of the available choices"""
|
||||
value = self.config_value(context, config_path)
|
||||
if value not in self.choices:
|
||||
vollog.log(constants.LOGLEVEL_V, "ValueError - Value is not within the set of available choices")
|
||||
return [interfaces.configuration.path_join(config_path, self.name)]
|
||||
return []
|
||||
|
||||
|
||||
class ListRequirement(interfaces.configuration.RequirementInterface):
|
||||
"""Allows for a list of a specific type of requirement (all of which must be met for this requirement to be met) to be specified
|
||||
|
||||
This roughly correlates to allowing a number of arguments to follow a command line parameter,
|
||||
such as a list of integers or a list of strings.
|
||||
|
||||
It is distinct from a multi-requirement which stores the subrequirements in a dictionary, not a list,
|
||||
and does not allow for a dynamic number of values.
|
||||
"""
|
||||
|
||||
def __init__(self, element_type, max_elements, min_elements, *args, **kwargs):
|
||||
"""Constructs the object
|
||||
|
||||
:param element_type: The (requirement) type of each element within the list
|
||||
:type element_type: InstanceRequirement
|
||||
:param max_elements; The maximum number of acceptable elements this list can contain
|
||||
:type max_elements: int
|
||||
:param min_elements: The minimum number of acceptable elements this list can contain
|
||||
:type min_elements: int
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not isinstance(element_type, interfaces.configuration.InstanceRequirement):
|
||||
raise TypeError("ListRequirements can only contain simple InstanceRequirements")
|
||||
self.element_type = element_type
|
||||
self.min_elements = min_elements
|
||||
self.max_elements = max_elements
|
||||
|
||||
def unsatisfied(self, context, config_path):
|
||||
"""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)
|
||||
self._check_type(value, list)
|
||||
if not (self.min_elements <= len(value) <= self.max_elements):
|
||||
vollog.log(constants.LOGLEVEL_V, "TypeError - List option provided more or less elements than allowed.")
|
||||
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.")
|
||||
return [interfaces.configuration.path_join(config_path, self.name)]
|
||||
result = []
|
||||
for element in value:
|
||||
subresult = self.element_type.unsatisfied(context, element)
|
||||
for subvalue in subresult:
|
||||
result.append(subvalue)
|
||||
return result
|
||||
|
||||
@@ -6,7 +6,6 @@ Automagic objects attempt to automatically fill configuration values that a user
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
from volatility.framework import validity
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.interfaces import configuration as interfaces_configuration
|
||||
|
||||
|
||||
@@ -38,8 +37,8 @@ class AutomagicInterface(interfaces_configuration.ConfigurableInterface, metacla
|
||||
super().__init__(context, config_path)
|
||||
for requirement in self.get_requirements():
|
||||
if not isinstance(requirement, (interfaces_configuration.InstanceRequirement,
|
||||
requirements.ChoiceRequirement,
|
||||
requirements.ListRequirement)):
|
||||
interfaces_configuration.ChoiceRequirement,
|
||||
interfaces_configuration.ListRequirement)):
|
||||
raise ValueError(
|
||||
"Automagic requirements must be an InstanceRequirement, ChoiceRequirement or ListRequirement")
|
||||
|
||||
|
||||
@@ -613,3 +613,71 @@ class SymbolRequirement(ConstructableRequirementInterface):
|
||||
return False
|
||||
context.symbol_space.append(obj)
|
||||
return True
|
||||
|
||||
|
||||
class ChoiceRequirement(RequirementInterface):
|
||||
"""Allows one from a choice of strings"""
|
||||
|
||||
def __init__(self, choices, *args, **kwargs):
|
||||
"""Constructs the object
|
||||
|
||||
:param choices: A list of possible string options that can be chosen from
|
||||
:type choices: list of str
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not isinstance(choices, list) or any([not isinstance(choice, str) for choice in choices]):
|
||||
raise TypeError("ChoiceRequirement takes a list of strings as choices")
|
||||
self.choices = choices
|
||||
|
||||
def unsatisfied(self, context, config_path):
|
||||
"""Validates the provided value to ensure it is one of the available choices"""
|
||||
value = self.config_value(context, config_path)
|
||||
if value not in self.choices:
|
||||
vollog.log(constants.LOGLEVEL_V, "ValueError - Value is not within the set of available choices")
|
||||
return [path_join(config_path, self.name)]
|
||||
return []
|
||||
|
||||
|
||||
class ListRequirement(RequirementInterface):
|
||||
"""Allows for a list of a specific type of requirement (all of which must be met for this requirement to be met) to be specified
|
||||
|
||||
This roughly correlates to allowing a number of arguments to follow a command line parameter,
|
||||
such as a list of integers or a list of strings.
|
||||
|
||||
It is distinct from a multi-requirement which stores the subrequirements in a dictionary, not a list,
|
||||
and does not allow for a dynamic number of values.
|
||||
"""
|
||||
|
||||
def __init__(self, element_type, max_elements, min_elements, *args, **kwargs):
|
||||
"""Constructs the object
|
||||
|
||||
:param element_type: The (requirement) type of each element within the list
|
||||
:type element_type: InstanceRequirement
|
||||
:param max_elements; The maximum number of acceptable elements this list can contain
|
||||
:type max_elements: int
|
||||
:param min_elements: The minimum number of acceptable elements this list can contain
|
||||
:type min_elements: int
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not isinstance(element_type, InstanceRequirement):
|
||||
raise TypeError("ListRequirements can only contain simple InstanceRequirements")
|
||||
self.element_type = element_type
|
||||
self.min_elements = min_elements
|
||||
self.max_elements = max_elements
|
||||
|
||||
def unsatisfied(self, context, config_path):
|
||||
"""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)
|
||||
self._check_type(value, list)
|
||||
if not (self.min_elements <= len(value) <= self.max_elements):
|
||||
vollog.log(constants.LOGLEVEL_V, "TypeError - List option provided more or less elements than allowed.")
|
||||
return [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.")
|
||||
return [path_join(config_path, self.name)]
|
||||
result = []
|
||||
for element in value:
|
||||
subresult = self.element_type.unsatisfied(context, element)
|
||||
for subvalue in subresult:
|
||||
result.append(subvalue)
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user