diff --git a/groups.json b/groups.json index f2cf8f1b..8df7411a 100644 --- a/groups.json +++ b/groups.json @@ -1,12 +1,18 @@ { "aws": { - "gdpr": [ - "check11", - "check12" - ], - "iam": [ - "iam_disable_30_days_credentials", - "iam_disable_90_days_credentials" - ] + "gdpr": { + "checks": [ + "check11", + "check12" + ], + "description": "GDPR Readiness" + }, + "iam": { + "checks": [ + "iam_disable_30_days_credentials", + "iam_disable_90_days_credentials" + ], + "description": "Identity and Access Management" + } } } diff --git a/lib/check/check.py b/lib/check/check.py index de5ab52f..7bb05271 100644 --- a/lib/check/check.py +++ b/lib/check/check.py @@ -3,6 +3,7 @@ import pkgutil from abc import ABC, abstractmethod from dataclasses import dataclass from types import ModuleType +from typing import Any from colorama import Fore, Style @@ -24,8 +25,10 @@ def exclude_groups_to_run( checks_to_execute: set, excluded_groups: list, provider: str ) -> set: # Recover checks from the input groups - - checks_from_groups = parse_groups_from_file(groups_file, excluded_groups, provider) + available_groups = parse_groups_from_file(groups_file) + checks_from_groups = load_checks_to_execute_from_groups( + available_groups, excluded_groups, provider + ) for check_name in checks_from_groups: checks_to_execute.discard(check_name) return checks_to_execute @@ -61,15 +64,32 @@ def parse_checks_from_file(input_file: str, provider: str) -> set: return checks_to_execute -# Load checks from groups.json -def parse_groups_from_file(group_file: str, group_list: list, provider: str) -> set: - checks_to_execute = set() +# List available groups +def list_groups(provider: str) -> list: + groups = parse_groups_from_file(groups_file) + print(f"Available Groups:") + + for group, value in groups[provider].items(): + group_description = value["description"] + print(f"\t - {group_description} -- [{group}] ") + + +# Parse groups from groups.json +def parse_groups_from_file(group_file: str) -> Any: f = open_file(group_file) available_groups = parse_json_file(f) + return available_groups + + +# Parse checks from groups to execute +def load_checks_to_execute_from_groups( + available_groups: Any, group_list: list, provider: str +) -> set: + checks_to_execute = set() for group in group_list: if group in available_groups[provider]: - for check_name in available_groups[provider][group]: + for check_name in available_groups[provider][group]["checks"]: checks_to_execute.add(check_name) else: logger.error( @@ -120,8 +140,9 @@ def load_checks_to_execute( # Handle if there are groups passed using -g/--groups elif group_list: try: - checks_to_execute = parse_groups_from_file( - groups_file, group_list, provider + available_groups = parse_groups_from_file(groups_file) + checks_to_execute = load_checks_to_execute_from_groups( + available_groups, group_list, provider ) except Exception as e: logger.error(f"{e.__class__.__name__} -- {e}") diff --git a/lib/check/check_test.py b/lib/check/check_test.py index cfb35308..856b7f42 100644 --- a/lib/check/check_test.py +++ b/lib/check/check_test.py @@ -4,6 +4,7 @@ from lib.check.check import ( exclude_checks_to_run, exclude_groups_to_run, exclude_services_to_run, + load_checks_to_execute_from_groups, parse_checks_from_file, parse_groups_from_file, ) @@ -19,7 +20,35 @@ class Test_Check: # } # ] # for test in test_cases: - # assert importlib.import_module(test["input"]).__name__ == test["expected"] + # assert importlib.import_module(test["input"]).__name__ == test["expected" + + def test_parse_groups_from_file(self): + test_cases = [ + { + "input": { + "path": f"{os.path.dirname(os.path.realpath(__file__))}/fixtures/groupsA.json", + "provider": "aws", + }, + "expected": { + "aws": { + "gdpr": { + "description": "GDPR Readiness", + "checks": ["check11", "check12"], + }, + "iam": { + "description": "Identity and Access Management", + "checks": [ + "iam_disable_30_days_credentials", + "iam_disable_90_days_credentials", + ], + }, + } + }, + } + ] + for test in test_cases: + check_file = test["input"]["path"] + assert parse_groups_from_file(check_file) == test["expected"] def test_parse_checks_from_file(self): test_cases = [ @@ -36,23 +65,40 @@ class Test_Check: provider = test["input"]["provider"] assert parse_checks_from_file(check_file, provider) == test["expected"] - def test_parse_groups_from_file(self): + def test_load_checks_to_execute_from_groups(self): test_cases = [ { "input": { - "groups": ["gdpr"], + "groups_json": { + "aws": { + "gdpr": { + "description": "GDPR Readiness", + "checks": ["check11", "check12"], + }, + "iam": { + "description": "Identity and Access Management", + "checks": [ + "iam_disable_30_days_credentials", + "iam_disable_90_days_credentials", + ], + }, + } + }, "provider": "aws", - "group_file": f"{os.path.dirname(os.path.realpath(__name__))}/groups.json", + "groups": ["gdpr"], }, "expected": {"check11", "check12"}, } ] + for test in test_cases: provider = test["input"]["provider"] groups = test["input"]["groups"] - group_file = test["input"]["group_file"] + group_file = test["input"]["groups_json"] + assert ( - parse_groups_from_file(group_file, groups, provider) == test["expected"] + load_checks_to_execute_from_groups(group_file, groups, provider) + == test["expected"] ) def test_exclude_checks_to_run(self): diff --git a/lib/check/fixtures/groupsA.json b/lib/check/fixtures/groupsA.json index 80ac60fb..8df7411a 100644 --- a/lib/check/fixtures/groupsA.json +++ b/lib/check/fixtures/groupsA.json @@ -1,8 +1,18 @@ { "aws": { - "gdpr": [ - "check11", - "check12" - ] + "gdpr": { + "checks": [ + "check11", + "check12" + ], + "description": "GDPR Readiness" + }, + "iam": { + "checks": [ + "iam_disable_30_days_credentials", + "iam_disable_90_days_credentials" + ], + "description": "Identity and Access Management" + } } } diff --git a/prowler.py b/prowler.py index 69f50715..a5278653 100644 --- a/prowler.py +++ b/prowler.py @@ -9,6 +9,7 @@ from lib.check.check import ( exclude_groups_to_run, exclude_services_to_run, import_check, + list_groups, load_checks_to_execute, run_check, set_output_options, @@ -28,7 +29,7 @@ if __name__ == "__main__": group.add_argument("-C", "--checks-file", nargs="?", help="List of checks") group.add_argument("-s", "--services", nargs="+", help="List of services") group.add_argument("-g", "--groups", nargs="+", help="List of groups") - + group.add_argument("-L", "--list-groups", action="store_true", help="List groups") parser.add_argument("-e", "--excluded-checks", nargs="+", help="Checks to exclude") parser.add_argument("-E", "--excluded-groups", nargs="+", help="Groups to exclude") parser.add_argument( @@ -123,6 +124,10 @@ if __name__ == "__main__": if args.no_banner: print_banner() + if args.list_groups: + list_groups(provider) + quit() + # Setting output options set_output_options(args.quiet)