feat(checks): Select checks to run from provider using -C/--checks-file (#1200)

This commit is contained in:
Pepe Fagoaga
2022-06-16 12:49:55 +02:00
committed by GitHub
parent 162852634e
commit 9d5e43e6a2
8 changed files with 103 additions and 21 deletions

6
checklist.txt Normal file
View File

@@ -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.

0
lib/check/__init__.py Normal file
View File

View File

@@ -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

39
lib/check/check_test.py Normal file
View File

@@ -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"]

View File

@@ -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.

View File

@@ -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

View File

@@ -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"]

View File

@@ -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: