feat(compliance): Loader and Execute (#1465)

This commit is contained in:
Pepe Fagoaga
2022-11-23 15:53:53 +01:00
committed by GitHub
parent 1a70a45805
commit b3e57ca3e5
515 changed files with 6018 additions and 5614 deletions

View File

@@ -1,4 +1,7 @@
import functools
import importlib
import os
import sys
from pkgutil import walk_packages
from types import ModuleType
from typing import Any
@@ -6,7 +9,8 @@ from typing import Any
from alive_progress import alive_bar
from colorama import Fore, Style
from config.config import groups_file, orange_color
from config.config import compliance_specification_dir, groups_file, orange_color
from lib.check.compliance_models import load_compliance_framework
from lib.check.models import Check, Output_From_Options, load_check_metadata
from lib.logger import logger
from lib.outputs.outputs import report
@@ -31,6 +35,29 @@ def bulk_load_checks_metadata(provider: str) -> dict:
return bulk_check_metadata
# Bulk load all compliance frameworks specification
def bulk_load_compliance_frameworks(provider: str) -> dict:
"""Bulk load all compliance frameworks specification into a dict"""
bulk_compliance_frameworks = {}
compliance_specification_dir_path = f"{compliance_specification_dir}/{provider}"
try:
for filename in os.listdir(compliance_specification_dir_path):
file_path = os.path.join(compliance_specification_dir_path, filename)
# Check if it is a file
if os.path.isfile(file_path):
# Open Compliance file in JSON
# cis_v1.4_aws.json --> cis_v1.4_aws
compliance_framework_name = filename.split(".json")[0]
# Store the compliance info
bulk_compliance_frameworks[
compliance_framework_name
] = load_compliance_framework(file_path)
except Exception as e:
logger.error(f"{e.__class__.__name__} -- {e}")
return bulk_compliance_frameworks
# Exclude checks to run
def exclude_checks_to_run(checks_to_execute: set, excluded_checks: list) -> set:
for check in excluded_checks:
@@ -101,16 +128,43 @@ def print_services(service_list: set):
print(f"- {service}")
def print_checks(provider: str, check_list: set, bulk_checks_metadata: dict):
def print_compliance_frameworks(bulk_compliance_frameworks: dict):
print(
f"There are {Fore.YELLOW}{len(bulk_compliance_frameworks.keys())}{Style.RESET_ALL} available Compliance Frameworks: \n"
)
for framework in bulk_compliance_frameworks.keys():
print(f"\t- {Fore.YELLOW}{framework}{Style.RESET_ALL}")
def print_compliance_requirements(bulk_compliance_frameworks: dict):
if bulk_compliance_frameworks and "ens_rd2022_aws" in bulk_compliance_frameworks:
print("Listing ENS RD2022 AWS Compliance Requirements:\n")
for compliance in bulk_compliance_frameworks.values():
for requirement in compliance.Requirements:
checks = ""
for check in requirement.Checks:
checks += f" {Fore.YELLOW}\t\t{check}\n{Style.RESET_ALL}"
print(
f"Requirement Id: {Fore.MAGENTA}{requirement.Id}{Style.RESET_ALL}\n\t- Description: {requirement.Description}\n\t- Checks:\n{checks}"
)
def print_checks(
provider: str,
check_list: set,
bulk_checks_metadata: dict,
):
for check in check_list:
try:
print(
f"[{bulk_checks_metadata[check].CheckID}] {bulk_checks_metadata[check].CheckTitle} - {Fore.MAGENTA}{bulk_checks_metadata[check].ServiceName} {Fore.YELLOW}[{bulk_checks_metadata[check].Severity}]{Style.RESET_ALL}"
)
except KeyError as error:
logger.error(
logger.critical(
f"Check {error} was not found for the {provider.upper()} provider"
)
sys.exit()
print(
f"\nThere are {Fore.YELLOW}{len(check_list)}{Style.RESET_ALL} available checks.\n"
)
@@ -150,21 +204,51 @@ def load_checks_to_execute_from_groups(
return checks_to_execute
# Parse checks from compliance frameworks specification
def parse_checks_from_compliance_framework(
compliance_frameworks: list, bulk_compliance_frameworks: dict
) -> list:
"""Parse checks from compliance frameworks specification"""
checks_to_execute = set()
try:
for framework in compliance_frameworks:
# compliance_framework_json["Requirements"][*]["Checks"]
compliance_framework_checks_list = [
requirement.Checks
for requirement in bulk_compliance_frameworks[framework].Requirements
]
# Reduce nested list into a list
# Pythonic functional magic
compliance_framework_checks = functools.reduce(
lambda x, y: x + y, compliance_framework_checks_list
)
# Then union this list of checks with the initial one
checks_to_execute = checks_to_execute.union(compliance_framework_checks)
except Exception as e:
logger.error(f"{e.__class__.__name__}[{e.__traceback__.tb_lineno}] -- {e}")
return checks_to_execute
# Recover all checks from the selected provider and service
def recover_checks_from_provider(provider: str, service: str = None) -> list:
checks = []
modules = list_modules(provider, service)
for module_name in modules:
# Format: "providers.{provider}.services.{service}.{check_name}.{check_name}"
check_name = module_name.name
# We need to exclude common shared libraries in services
if (
check_name.count(".") == 5
and "lib" not in check_name
and "test" not in check_name
):
checks.append(check_name)
return checks
try:
checks = []
modules = list_modules(provider, service)
for module_name in modules:
# Format: "providers.{provider}.services.{service}.{check_name}.{check_name}"
check_name = module_name.name
# We need to exclude common shared libraries in services
if (
check_name.count(".") == 5
and "lib" not in check_name
and "test" not in check_name
):
checks.append(check_name)
return checks
except Exception as e:
logger.critical(f"{e.__class__.__name__}[{e.__traceback__.tb_lineno}]: {e}")
sys.exit()
# List all available modules in the selected provider and service
@@ -184,6 +268,7 @@ def import_check(check_path: str) -> ModuleType:
return lib
# Sets the Output_From_Options to be used in the output modes
def set_output_options(
quiet: bool,
output_modes: list,
@@ -191,8 +276,10 @@ def set_output_options(
security_hub_enabled: bool,
output_filename: str,
allowlist_file: str,
bulk_checks_metadata: dict,
verbose: bool,
):
"""Sets the Output_From_Options to be used in the output modes"""
global output_options
output_options = Output_From_Options(
is_quiet=quiet,
@@ -201,6 +288,7 @@ def set_output_options(
security_hub_enabled=security_hub_enabled,
output_filename=output_filename,
allowlist_file=allowlist_file,
bulk_checks_metadata=bulk_checks_metadata,
verbose=verbose,
# set input options here
)
@@ -211,15 +299,15 @@ def run_check(check: Check, output_options: Output_From_Options) -> list:
findings = []
if output_options.verbose or output_options.is_quiet:
print(
f"\nCheck ID: {check.checkID} - {Fore.MAGENTA}{check.serviceName}{Fore.YELLOW} [{check.severity}]{Style.RESET_ALL}"
f"\nCheck ID: {check.CheckID} - {Fore.MAGENTA}{check.ServiceName}{Fore.YELLOW} [{check.Severity}]{Style.RESET_ALL}"
)
logger.debug(f"Executing check: {check.checkID}")
logger.debug(f"Executing check: {check.CheckID}")
try:
findings = check.execute()
except Exception as error:
print(f"Something went wrong in {check.checkID}, please use --log-level ERROR")
print(f"Something went wrong in {check.CheckID}, please use --log-level ERROR")
logger.error(
f"{check.checkID} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
f"{check.CheckID} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
finally:
return findings
@@ -264,13 +352,14 @@ def execute_checks(
# If check does not exists in the provider or is from another provider
except ModuleNotFoundError:
logger.error(
logger.critical(
f"Check '{check_name}' was not found for the {provider.upper()} provider"
)
bar.title = f"-> {Fore.RED}Scan was aborted!{Style.RESET_ALL}"
sys.exit()
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
bar.title = f"-> {Fore.GREEN}Scan is completed!"
print(Style.RESET_ALL)
bar.title = f"-> {Fore.GREEN}Scan is completed!{Style.RESET_ALL}"
return all_findings