Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand Recipe testing to perform arg validation #898

Merged
merged 9 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion data/recipes/aws_forensics.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "aws_forensics",
"short_description": "Copies a volume from an AWS account to an analysis VM.",
"description": "Copies a volume from an AWS account, creates an analysis VM in AWS (with a startup script containing installation instructions for basic forensics tooling), and attaches the copied volume to it.",
"test_params": "default us-east-1 incident_id --instance_id i-01234567 --volume_ids vol-01234567",
"modules": [{
"wants": [],
"name": "AWSCollector",
Expand All @@ -26,7 +27,7 @@
["--instance_id", "Instance ID of the instance to analyze.", null, {"format": "regex", "comma_separated": false, "regex": "^i-[0-9a-f]{8,17}$"}],
["--volume_ids", "Comma-separated list of volume IDs to copy.", null, {"format": "regex", "comma_separated": true, "regex": "^vol-[0-9a-f]{8,17}$"}],
["--all_volumes", "Copy all volumes in the designated instance. Overrides volume_ids if specified.", false],
["--boot_volume_size", "The size of the analysis VM boot volume (in GB).", 50, {"format": "regex", "regex": "^\\d+$"}],
["--boot_volume_size", "The size of the analysis VM boot volume (in GB).", "50", {"format": "regex", "regex": "^\\d+$"}],
ramo-j marked this conversation as resolved.
Show resolved Hide resolved
["--analysis_zone", "The AWS zone in which to create the VM.", null, {"format": "aws_region"}],
["--analysis_profile_name", "Name of the AWS profile to use when creating the analysis VM.", null]
]
Expand Down
7 changes: 6 additions & 1 deletion dftimewolf/lib/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""Various dfTimewolf resource objects."""

import dataclasses
from typing import Any, Dict, Sequence
from typing import Any, Dict, Sequence, Optional


@dataclasses.dataclass
Expand Down Expand Up @@ -62,3 +62,8 @@ def GetHelpString(self) -> str:
short_description = self.contents.get(
'short_description', 'No description')
return ' {0:<35s}{1:s}\n'.format(self.name, short_description)

def GetTestParams(self) -> Optional[list[str]]:
if self.contents.get('test_params', None):
return self.contents.get('test_params', '').split(' ')
return None
16 changes: 15 additions & 1 deletion tests/cli/main_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def _EnumerateRecipeNames():
tool = _CreateToolObject()
# pylint: disable=protected-access
for recipe in tool._recipes_manager.GetRecipes():
yield (recipe.name, recipe.name)
yield (f'_{recipe.name}', recipe.name)


class MainToolTest(parameterized.TestCase):
Expand Down Expand Up @@ -104,6 +104,9 @@ def testToolWithArbitraryRecipe(self):

@parameterized.named_parameters(_EnumerateRecipeNames())
def testRecipeSetupArgs(self, recipe_name):
self._testRecipeSetupArgs(recipe_name)

def _testRecipeSetupArgs(self, recipe_name):
"""Checks that all recipes pass the correct arguments to their modules."""
# We want to access the tool's state object to load recipes and go through
# modules.
Expand All @@ -129,11 +132,19 @@ def testRecipeSetupArgs(self, recipe_name):

@parameterized.named_parameters(_EnumerateRecipeNames())
def testRecipeValidators(self, recipe_name):
self._testRecipeValidators(recipe_name)

def _testRecipeValidators(self, recipe_name):
"""Tests that recipes do not specify invalid validators."""
# pylint: disable=protected-access
self.tool._state = dftw_state.DFTimewolfState(config.Config)
recipe = self.tool._recipes_manager.Recipes()[recipe_name]

test_params = recipe.GetTestParams()
if test_params:
recipe_args = [recipe_name] + test_params
self.tool.ParseArguments(recipe_args)

self.tool._state.LoadRecipe(recipe.contents, dftimewolf_recipes.MODULES)
for arg in recipe.args:
if arg.validation_params:
Expand All @@ -142,6 +153,9 @@ def testRecipeValidators(self, recipe_name):
validators_manager.ValidatorsManager.ListValidators(),
f'Error in {recipe.name}:{arg.switch} - '
f'Invalid validator {arg.validation_params["format"]}.')

if test_params:
self.tool.ValidateArguments()

def testRecipeWithNestedArgs(self):
"""Tests that a recipe with args referenced in other args is populated."""
Expand Down
Loading