diff --git a/checklist.txt b/checklist.txt new file mode 100644 index 00000000..7f31c817 --- /dev/null +++ b/checklist.txt @@ -0,0 +1,6 @@ +# You can add a comma seperated list of checks like this: +check11,check12 +extra72 # You can also use newlines for each check +check13 # This way allows you to add inline comments +# Both of these can be combined if you have a standard list and want to add +# inline comments for other checks. diff --git a/lib/check/__init__.py b/lib/check/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/check.py b/lib/check/check.py similarity index 82% rename from lib/check.py rename to lib/check/check.py index 2819e8d2..481ea5cd 100644 --- a/lib/check.py +++ b/lib/check/check.py @@ -8,7 +8,24 @@ from lib.logger import logger from lib.outputs import report -def load_checks_to_execute(check_list, provider): +# Parse checks from file +def parse_checks_from_file(checks_file): + checks_to_execute = set() + with open(checks_file) as f: + for line in f: + # Remove comments from file + line = line.partition("#")[0].strip() + # If file contains several checks comma-separated + if "," in line: + for check in line.split(","): + checks_to_execute.add(check.strip()) + # If line is not empty + elif len(line): + checks_to_execute.add(line) + return checks_to_execute + + +def load_checks_to_execute(checks_file, check_list, provider): checks_to_execute = set() # LOADER # Handle if there are checks passed using -c/--checks @@ -16,6 +33,16 @@ def load_checks_to_execute(check_list, provider): for check_name in check_list: checks_to_execute.add(check_name) + # Handle if there are checks passed using -C/--checks-file + elif checks_file: + # check if file exists or path + # check permissions to read + try: + checks_to_execute = parse_checks_from_file(checks_file) + + except Exception as e: + logger.error(f"{checks_file}: {e.__class__.__name__}") + # If there are no checks passed as argument else: # Get all check modules to run with the specific provider @@ -25,7 +52,7 @@ def load_checks_to_execute(check_list, provider): # Format: "providers.{provider}.services.{service}.{check_name}.{check_name}" check_name = check_module.split(".")[-1] checks_to_execute.add(check_name) - + print(checks_to_execute) return checks_to_execute diff --git a/lib/check/check_test.py b/lib/check/check_test.py new file mode 100644 index 00000000..9bafe921 --- /dev/null +++ b/lib/check/check_test.py @@ -0,0 +1,39 @@ +import os + +from lib.check.check import parse_checks_from_file + + +class Test_Check: + # def test_import_check(self): + # test_cases = [ + # { + # "name": "Test valid check path", + # "input": "providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials", + # "expected": "providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials", + # } + # ] + # for test in test_cases: + # assert importlib.import_module(test["input"]).__name__ == test["expected"] + + def test_parse_checks_from_file(checks_file): + test_cases = [ + { + "name": "Test valid check path", + "input": f"{os.path.dirname(os.path.realpath(__file__))}/fixtures/checklistA.txt", + "expected": {"check12", "check11", "extra72", "check13"}, + }, + { + "name": "Test valid check path", + "input": f"{os.path.dirname(os.path.realpath(__file__))}/fixtures/checklistB.txt", + "expected": { + "extra72", + "check13", + "check11", + "check12", + "check56", + "check2423", + }, + }, + ] + for test in test_cases: + assert parse_checks_from_file(test["input"]) == test["expected"] diff --git a/lib/check/fixtures/checklistA.txt b/lib/check/fixtures/checklistA.txt new file mode 100644 index 00000000..7f31c817 --- /dev/null +++ b/lib/check/fixtures/checklistA.txt @@ -0,0 +1,6 @@ +# You can add a comma seperated list of checks like this: +check11,check12 +extra72 # You can also use newlines for each check +check13 # This way allows you to add inline comments +# Both of these can be combined if you have a standard list and want to add +# inline comments for other checks. diff --git a/lib/check/fixtures/checklistB.txt b/lib/check/fixtures/checklistB.txt new file mode 100644 index 00000000..13bc350f --- /dev/null +++ b/lib/check/fixtures/checklistB.txt @@ -0,0 +1,11 @@ +# You can add a comma seperated list of checks like this: +check11,check12 +extra72 # You can also use newlines for each check +check13 # This way allows you to add inline comments +# Both of these can be combined if you have a standard list and want to add +# inline comments for other checks. +# +# +# +# check11,check12 +check2423,check56 diff --git a/lib/check_test.py b/lib/check_test.py deleted file mode 100644 index 9aee19ad..00000000 --- a/lib/check_test.py +++ /dev/null @@ -1,14 +0,0 @@ -import importlib - - -class Test_Check: - def test_import_check(self): - test_cases = [ - { - "name": "Test valid check path", - "input": "providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials", - "expected": "providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials", - } - ] - for test in test_cases: - assert importlib.import_module(test["input"]).__name__ == test["expected"] diff --git a/prowler.py b/prowler.py index d8c3eb79..1d41ae56 100644 --- a/prowler.py +++ b/prowler.py @@ -4,7 +4,7 @@ import argparse from lib.banner import print_banner, print_version -from lib.check import import_check, load_checks_to_execute, run_check +from lib.check.check import import_check, load_checks_to_execute, run_check from lib.logger import logger, logging_levels from providers.aws.aws_provider import Input_Data, provider_set_session @@ -12,7 +12,13 @@ if __name__ == "__main__": # CLI Arguments parser = argparse.ArgumentParser() parser.add_argument("provider", choices=["aws"], help="Specify Provider") - parser.add_argument("-c", "--checks", nargs="+", help="List of checks") + + # Arguments to set checks to run + # -c can't be used with -C + group = parser.add_mutually_exclusive_group() + group.add_argument("-c", "--checks", nargs="+", help="List of checks") + group.add_argument("-C", "--checks-file", nargs="?", help="List of checks") + parser.add_argument( "-b", "--no-banner", action="store_false", help="Hide Prowler Banner" ) @@ -66,7 +72,8 @@ if __name__ == "__main__": provider = args.provider checks = args.checks - + checks_file = args.checks_file + # Role assumption input options tests if args.role or args.account: if not args.account: @@ -94,7 +101,7 @@ if __name__ == "__main__": session_duration=args.session_duration, external_id=args.external_id, ) - + # Set Logger logger.setLevel(logging_levels.get(args.log_level)) @@ -110,7 +117,7 @@ if __name__ == "__main__": # Load checks to execute logger.debug("Loading checks") - checks_to_execute = load_checks_to_execute(checks, provider) + checks_to_execute = load_checks_to_execute(checks_file, checks, provider) # Execute checks for check_name in checks_to_execute: