feat(display): add progress bar and summary table (#1512)

Co-authored-by: sergargar <sergio@verica.io>
This commit is contained in:
Sergio Garcia
2022-11-22 11:18:43 +01:00
committed by GitHub
parent af1d85ae75
commit 9204142eaf
16 changed files with 320 additions and 106 deletions

View File

@@ -11,6 +11,8 @@ botocore = "1.27.8"
pydantic = "1.9.1"
shodan = "1.28.0"
detect-secrets = "1.4.0"
alive-progress = "2.4.1"
tabulate = "0.9.0"
[dev-packages]
black = "22.10.0"

91
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "881edb3306efd59b84c75bd2bff5acbc29397eb7f12321203c62a013f0491e2e"
"sha256": "2205b1ac0f570e1be2641ea17a1d07a046d4ae547f49622cf993c8f23ee550ae"
},
"pipfile-spec": 6,
"requires": {
@@ -16,6 +16,21 @@
]
},
"default": {
"about-time": {
"hashes": [
"sha256:586b329450c9387d1ae8c42d2db4f5b4c57a54508d0f1b7bb00322ffd5ce9f9b",
"sha256:96841beb3f9b5de1cbb323d2bdb0fa9abdecbc46f2d546b9b3c2483d23daa17a"
],
"version": "==3.1.1"
},
"alive-progress": {
"hashes": [
"sha256:089757c8197f27ad972ba27e1060f6db92368d83c736884e159034fd74865323",
"sha256:5503ffca0a0607d5f0d24d3b10a718fe50e375470fa07602b246333eb7ec88ee"
],
"index": "pypi",
"version": "==2.4.1"
},
"arnparse": {
"hashes": [
"sha256:b0906734e4b8f19e39b1e32944c6cd6274b6da90c066a83882ac7a11d27553e0",
@@ -26,19 +41,19 @@
},
"boto3": {
"hashes": [
"sha256:180b4413db1211836c622adfc16dd40a7b99b86cac894bc6e17ddea9d282ab14",
"sha256:73da24667fe1351cef0f402ee9cd4589a0a9d97b617caca3c25b5cdc38f9d62c"
"sha256:853cf4b2136c4deec4e01a17b89126377bfca30223535795d879ca65af4c4a69",
"sha256:a8ad13a23745b6d4a56d5bdde53a7a80cd7b40016cd411b9a94e6bbfb2ca5dd2"
],
"index": "pypi",
"version": "==1.26.12"
"version": "==1.26.13"
},
"botocore": {
"hashes": [
"sha256:3149b102e3c26c935acef6c330d0f46c717820d118886e983b6e81c306f31405",
"sha256:fdae73306a41a30697be300bdecb1e0d560d453c6d748891856beb87e9f6573f"
"sha256:9c73a180fad9a7da7797530ced3b5069872bff915b1ae9fa11fc1ed79b584c8e",
"sha256:9d39db398f472c0aa97098870c8c4cf12636b2667a18e694fea5fae046af907e"
],
"index": "pypi",
"version": "==1.29.12"
"version": "==1.29.13"
},
"certifi": {
"hashes": [
@@ -53,7 +68,7 @@
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==2.1.1"
},
"click": {
@@ -87,6 +102,12 @@
"index": "pypi",
"version": "==1.4.0"
},
"grapheme": {
"hashes": [
"sha256:44c2b9f21bbe77cfb05835fec230bd435954275267fea1858013b102f8603cca"
],
"version": "==0.6.0"
},
"idna": {
"hashes": [
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
@@ -230,6 +251,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"tabulate": {
"hashes": [
"sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c",
"sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"
],
"index": "pypi",
"version": "==0.9.0"
},
"typing-extensions": {
"hashes": [
"sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa",
@@ -258,11 +287,11 @@
"develop": {
"astroid": {
"hashes": [
"sha256:1c00a14f5a3ed0339d38d2e2e5b74ea2591df5861c0936bb292b84ccf3a78d83",
"sha256:72702205200b2a638358369d90c222d74ebc376787af8fb2f7f2a86f7b5cc85f"
"sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907",
"sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7"
],
"markers": "python_full_version >= '3.7.2'",
"version": "==2.12.12"
"version": "==2.12.13"
},
"attrs": {
"hashes": [
@@ -309,19 +338,19 @@
},
"boto3": {
"hashes": [
"sha256:180b4413db1211836c622adfc16dd40a7b99b86cac894bc6e17ddea9d282ab14",
"sha256:73da24667fe1351cef0f402ee9cd4589a0a9d97b617caca3c25b5cdc38f9d62c"
"sha256:853cf4b2136c4deec4e01a17b89126377bfca30223535795d879ca65af4c4a69",
"sha256:a8ad13a23745b6d4a56d5bdde53a7a80cd7b40016cd411b9a94e6bbfb2ca5dd2"
],
"index": "pypi",
"version": "==1.26.12"
"version": "==1.26.13"
},
"botocore": {
"hashes": [
"sha256:3149b102e3c26c935acef6c330d0f46c717820d118886e983b6e81c306f31405",
"sha256:fdae73306a41a30697be300bdecb1e0d560d453c6d748891856beb87e9f6573f"
"sha256:9c73a180fad9a7da7797530ced3b5069872bff915b1ae9fa11fc1ed79b584c8e",
"sha256:9d39db398f472c0aa97098870c8c4cf12636b2667a18e694fea5fae046af907e"
],
"index": "pypi",
"version": "==1.29.12"
"version": "==1.29.13"
},
"certifi": {
"hashes": [
@@ -405,7 +434,7 @@
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==2.1.1"
},
"click": {
@@ -604,7 +633,7 @@
"sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7",
"sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"
],
"markers": "python_version < '4' and python_full_version >= '3.6.1'",
"markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
"version": "==5.10.1"
},
"jinja2": {
@@ -636,7 +665,7 @@
"sha256:1e525177574c23ae0f55cd62382632a083a0339928f0ca846a975a4da9851cec",
"sha256:780a22d517cdc857d9714a80d8349c546945063f20853ea32ba7f85bc643ec7d"
],
"markers": "python_version >= '3.7' and python_version < '4'",
"markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'",
"version": "==0.1.2"
},
"lazy-object-proxy": {
@@ -728,11 +757,11 @@
},
"moto": {
"hashes": [
"sha256:2fb909d2ea1b732f89604e4268e2c2207c253e590a635a410c3c2aaebb34e113",
"sha256:ba03b638cf3b1cec64cbe9ac0d184ca898b69020c8e3c5b9b4961c1670629010"
"sha256:356bf792b439228891c910e2a0fafd4264334cf9000b508c732ff43d8694fb6a",
"sha256:9ba96d04a472d5682493cad7fee33337da34ebef18b397af1ea6dfb41efbe148"
],
"index": "pypi",
"version": "==4.0.9"
"version": "==4.0.10"
},
"mypy-extensions": {
"hashes": [
@@ -746,7 +775,7 @@
"sha256:34fbd14b7501abe25e64d7b4624a9db02cde1a578d285b3da6f34b290cdf0b3a",
"sha256:7cf27585dd7970b7257cefe48e1a3a10d4e34421831bdb472d96967433bc27bd"
],
"markers": "python_version >= '3.7' and python_version < '4'",
"markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'",
"version": "==0.3.4"
},
"openapi-spec-validator": {
@@ -770,7 +799,7 @@
"sha256:5c869d315be50776cc8a993f3af43e0c60dc01506b399643f919034ebf4cdcab",
"sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14"
],
"markers": "python_version >= '3.7' and python_version < '4'",
"markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'",
"version": "==0.4.3"
},
"pathspec": {
@@ -830,11 +859,11 @@
},
"pylint": {
"hashes": [
"sha256:3b120505e5af1d06a5ad76b55d8660d44bf0f2fc3c59c2bdd94e39188ee3a4df",
"sha256:c2108037eb074334d9e874dc3c783752cc03d0796c88c9a9af282d0f161a1004"
"sha256:15060cc22ed6830a4049cf40bc24977744df2e554d38da1b2657591de5bcd052",
"sha256:25b13ddcf5af7d112cf96935e21806c1da60e676f952efb650130f2a4483421c"
],
"index": "pypi",
"version": "==2.15.5"
"version": "==2.15.6"
},
"pyparsing": {
"hashes": [
@@ -1030,11 +1059,11 @@
},
"setuptools": {
"hashes": [
"sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31",
"sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f"
"sha256:6211d2f5eddad8757bd0484923ca7c0a6302ebc4ab32ea5e94357176e0ca0840",
"sha256:d1eebf881c6114e51df1664bc2c9133d022f78d12d5f4f665b9191f084e2862d"
],
"markers": "python_version >= '3.7'",
"version": "==65.5.1"
"version": "==65.6.0"
},
"six": {
"hashes": [

View File

@@ -7,7 +7,10 @@ from lib.logger import logger
timestamp = datetime.today()
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc)
prowler_version = "3.0-beta-08Aug2022"
prowler_version = "3.0-beta-21Nov2022"
orange_color = "\033[38;5;208m"
banner_color = "\033[1;92m"
# Groups
groups_file = "groups.json"

View File

@@ -1,14 +1,14 @@
from colorama import Fore, Style
from config.config import prowler_version, timestamp
from config.config import banner_color, orange_color, prowler_version, timestamp
def print_version():
print(f"Prowler {prowler_version}")
def print_banner():
banner = f"""{Fore.CYAN} _
def print_banner(args):
banner = f"""{banner_color} _
_ __ _ __ _____ _| | ___ _ __
| '_ \| '__/ _ \ \ /\ / / |/ _ \ '__|
| |_) | | | (_) \ V V /| | __/ |
@@ -16,11 +16,16 @@ def print_banner():
|_|{Fore.BLUE} the handy cloud security tool
{Fore.YELLOW}Date: {timestamp.strftime("%Y-%m-%d %H:%M:%S")}{Style.RESET_ALL}
Color code for results:
- {Fore.YELLOW}INFO (Information){Style.RESET_ALL}
- {Fore.GREEN}PASS (Recommended value){Style.RESET_ALL}
- {Fore.YELLOW}WARNING (Ignored by allowlist){Style.RESET_ALL}
- {Fore.RED}FAIL (Fix required){Style.RESET_ALL}
"""
print(banner)
if args.verbose or args.quiet:
print(
f"""
Color code for results:
- {Fore.YELLOW}INFO (Information){Style.RESET_ALL}
- {Fore.GREEN}PASS (Recommended value){Style.RESET_ALL}
- {orange_color}WARNING (Ignored by allowlist){Style.RESET_ALL}
- {Fore.RED}FAIL (Fix required){Style.RESET_ALL}
"""
)

View File

@@ -3,13 +3,15 @@ from pkgutil import walk_packages
from types import ModuleType
from typing import Any
from alive_progress import alive_bar
from colorama import Fore, Style
from config.config import groups_file
from lib.check.models import Output_From_Options, load_check_metadata
from config.config import groups_file, orange_color
from lib.check.models import Check, Output_From_Options, load_check_metadata
from lib.logger import logger
from lib.outputs.outputs import report
from lib.utils.utils import open_file, parse_json_file
from providers.aws.lib.audit_info.models import AWS_Audit_Info
# Load all checks metadata
@@ -189,6 +191,7 @@ def set_output_options(
security_hub_enabled: bool,
output_filename: str,
allowlist_file: str,
verbose: bool,
):
global output_options
output_options = Output_From_Options(
@@ -198,15 +201,18 @@ def set_output_options(
security_hub_enabled=security_hub_enabled,
output_filename=output_filename,
allowlist_file=allowlist_file,
verbose=verbose,
# set input options here
)
return output_options
def run_check(check, audit_info, output_options):
print(
f"\nCheck ID: {check.checkID} - {Fore.MAGENTA}{check.serviceName}{Fore.YELLOW} [{check.severity}]{Style.RESET_ALL}"
)
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}"
)
logger.debug(f"Executing check: {check.checkID}")
try:
findings = check.execute()
@@ -215,7 +221,54 @@ def run_check(check, audit_info, output_options):
logger.error(
f"{check.checkID} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
else:
report(findings, output_options, audit_info)
finally:
pass
return findings
def execute_checks(
checks_to_execute: list,
provider: str,
audit_info: AWS_Audit_Info,
audit_output_options: Output_From_Options,
) -> list:
all_findings = []
print(
f"{Style.BRIGHT}Executing {len(checks_to_execute)} checks, please wait...{Style.RESET_ALL}\n"
)
with alive_bar(
total=len(checks_to_execute),
ctrl_c=False,
bar="blocks",
spinner="classic",
stats=False,
enrich_print=False,
) as bar:
for check_name in checks_to_execute:
# Recover service from check name
service = check_name.split("_")[0]
bar.title = f"-> Scanning {orange_color}{service}{Style.RESET_ALL} service"
try:
# Import check module
check_module_path = (
f"providers.{provider}.services.{service}.{check_name}.{check_name}"
)
lib = import_check(check_module_path)
# Recover functions from check
check_to_execute = getattr(lib, check_name)
c = check_to_execute()
# Run check
check_findings = run_check(c, audit_output_options)
all_findings.extend(check_findings)
report(check_findings, audit_output_options, audit_info)
bar()
# If check does not exists in the provider or is from another provider
except ModuleNotFoundError:
logger.error(
f"Check '{check_name}' was not found for the {provider.upper()} provider"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return all_findings

View File

@@ -16,6 +16,7 @@ class Output_From_Options:
security_hub_enabled: bool
output_filename: str
allowlist_file: str
verbose: str
# Testing Pending

View File

@@ -4,11 +4,13 @@ import sys
from csv import DictWriter
from colorama import Fore, Style
from tabulate import tabulate
from config.config import (
csv_file_suffix,
json_asff_file_suffix,
json_file_suffix,
orange_color,
prowler_version,
timestamp_iso,
timestamp_utc,
@@ -25,6 +27,7 @@ from lib.outputs.models import (
)
from lib.utils.utils import file_exists, hash_sha512, open_file
from providers.aws.lib.allowlist.allowlist import is_allowlisted
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.lib.security_hub.security_hub import send_to_security_hub
@@ -62,7 +65,7 @@ def report(check_findings, output_options, audit_info):
print(
f"\t{color}{finding.status}{Style.RESET_ALL} {finding.region}: {finding.status_extended}"
)
elif not output_options.is_quiet:
elif not output_options.is_quiet and output_options.verbose:
print(
f"\t{color}{finding.status}{Style.RESET_ALL} {finding.region}: {finding.status_extended}"
)
@@ -103,8 +106,8 @@ def report(check_findings, output_options, audit_info):
finding.region, finding_output, audit_info.audit_session
)
else: # No service resources in the whole account
color = set_report_color("WARNING")
if not output_options.is_quiet:
color = set_report_color("INFO")
if not output_options.is_quiet and output_options.verbose:
print(f"\t{color}INFO{Style.RESET_ALL} There are no resources")
if file_descriptors:
@@ -180,6 +183,8 @@ def set_report_color(status):
elif status == "ERROR":
color = Fore.BLACK
elif status == "WARNING":
color = orange_color
elif status == "INFO":
color = Fore.YELLOW
else:
raise Exception("Invalid Report Status. Must be PASS, FAIL, ERROR or WARNING")
@@ -289,3 +294,111 @@ def send_to_s3_bucket(
except Exception as error:
logger.critical(f"{error.__class__.__name__} -- {error}")
sys.exit()
def display_summary_table(
findings: list,
audit_info: AWS_Audit_Info,
output_filename: str,
output_directory: str,
):
try:
if findings:
current = {
"Service": "",
"Provider": "",
"Critical": 0,
"High": 0,
"Medium": 0,
"Low": 0,
}
findings_table = {
"Provider": [],
"Service": [],
"Status": [],
"Critical": [],
"High": [],
"Medium": [],
"Low": [],
}
pass_count = fail_count = 0
for finding in findings:
# If new service and not first, add previous row
if (
current["Service"] != finding.check_metadata.ServiceName
and current["Service"]
):
add_service_to_table(findings_table, current)
current["Critical"] = current["High"] = current["Medium"] = current[
"Low"
] = 0
current["Service"] = finding.check_metadata.ServiceName
current["Provider"] = finding.check_metadata.Provider
if finding.status == "PASS":
pass_count += 1
elif finding.status == "FAIL":
fail_count += 1
if finding.check_metadata.Severity == "critical":
current["Critical"] += 1
elif finding.check_metadata.Severity == "high":
current["High"] += 1
elif finding.check_metadata.Severity == "medium":
current["Medium"] += 1
elif finding.check_metadata.Severity == "low":
current["Low"] += 1
# Add final service
add_service_to_table(findings_table, current)
print("\nOverview Results:")
overview_table = [
[
f"{Fore.RED}{round(fail_count/len(findings)*100, 2)}% ({fail_count}) Failed{Style.RESET_ALL}",
f"{Fore.GREEN}{round(pass_count/len(findings)*100, 2)}% ({pass_count}) Passed{Style.RESET_ALL}",
]
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
print(
f"\nAccount {Fore.YELLOW}{audit_info.audited_account}{Style.RESET_ALL} Scan Results (severity columns are for fails only):"
)
print(tabulate(findings_table, headers="keys", tablefmt="rounded_grid"))
print(
f"{Style.BRIGHT}* You only see here those services that contains resources.{Style.RESET_ALL}"
)
print("\nDetailed results are in:")
print(f" - CSV: {output_directory}/{output_filename}.csv")
print(f" - JSON: {output_directory}/{output_filename}.json\n")
except Exception as error:
logger.critical(
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
)
sys.exit()
def add_service_to_table(findings_table, current):
if (
current["Critical"] > 0
or current["High"] > 0
or current["Medium"] > 0
or current["Low"] > 0
):
current["Status"] = f"{Fore.RED}FAIL{Style.RESET_ALL}"
else:
current["Status"] = f"{Fore.GREEN}PASS{Style.RESET_ALL}"
findings_table["Provider"].append(current["Provider"])
findings_table["Service"].append(current["Service"])
findings_table["Status"].append(current["Status"])
findings_table["Critical"].append(
f"{Fore.LIGHTRED_EX}{current['Critical']}{Style.RESET_ALL}"
)
findings_table["High"].append(f"{Fore.RED}{current['High']}{Style.RESET_ALL}")
findings_table["Medium"].append(
f"{Fore.YELLOW}{current['Medium']}{Style.RESET_ALL}"
)
findings_table["Low"].append(f"{Fore.BLUE}{current['Low']}{Style.RESET_ALL}")

View File

@@ -13,6 +13,7 @@ from config.config import (
prowler_version,
timestamp_iso,
timestamp_utc,
orange_color,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
@@ -109,7 +110,7 @@ class Test_Outputs:
def test_set_report_color(self):
test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]
test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, orange_color]
for status in test_status:
assert set_report_color(status) in test_colors

View File

@@ -1,13 +1,14 @@
from colorama import Fore, Style
from lib.check.models import Check
from lib.check.models import Check, Check_Report
from providers.aws.services.account.account_client import account_client
# This check has no findings since it is manual
class account_maintain_current_contact_details(Check):
def execute(self):
print(
f"\t{Fore.YELLOW}INFO{Style.RESET_ALL} Manual check: Login to the AWS Console. Choose your account name on the top right of the window -> My Account -> Contact Information."
)
return []
report = Check_Report(self.metadata)
report.region = account_client.region
report.resource_id = account_client.audited_account
report.status = "INFO"
report.status_extended = "Manual check: Login to the AWS Console. Choose your account name on the top right of the window -> My Account -> Contact Information."
return [report]

View File

@@ -1,13 +1,14 @@
from colorama import Fore, Style
from lib.check.models import Check
from lib.check.models import Check, Check_Report
from providers.aws.services.account.account_client import account_client
# This check has no findings since it is manual
class account_security_contact_information_is_registered(Check):
def execute(self):
print(
f"\t{Fore.YELLOW}INFO{Style.RESET_ALL} Manual check: Login to the AWS Console. Choose your account name on the top right of the window -> My Account -> Alternate Contacts -> Security Section."
)
return []
report = Check_Report(self.metadata)
report.region = account_client.region
report.resource_id = account_client.audited_account
report.status = "INFO"
report.status_extended = "Manual check: Login to the AWS Console. Choose your account name on the top right of the window -> My Account -> Alternate Contacts -> Security Section."
return [report]

View File

@@ -1,13 +1,14 @@
from colorama import Fore, Style
from lib.check.models import Check
from lib.check.models import Check, Check_Report
from providers.aws.services.account.account_client import account_client
# This check has no findings since it is manual
class account_security_questions_are_registered_in_the_aws_account(Check):
def execute(self):
print(
f"\t{Fore.YELLOW}INFO{Style.RESET_ALL} Manual check: Login to the AWS Console as root. Choose your account name on the top right of the window -> My Account -> Configure Security Challenge Questions."
)
return []
report = Check_Report(self.metadata)
report.region = account_client.region
report.resource_id = account_client.audited_account
report.status = "INFO"
report.status_extended = "Manual check: Login to the AWS Console as root. Choose your account name on the top right of the window -> My Account -> Configure Security Challenge Questions."
return [report]

View File

@@ -1,9 +1,13 @@
################## Account
from providers.aws.aws_provider import get_region_global_service
class Account:
def __init__(self, audit_info):
self.service = "account"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.region = get_region_global_service(audit_info)
def __get_session__(self):
return self.session

View File

@@ -2,8 +2,10 @@
"Provider": "aws",
"CheckID": "cloudtrail_s3_dataevents_read_enabled",
"CheckTitle": "Check if S3 buckets have Object-level logging for read events is enabled in CloudTrail.",
"CheckType": ["Logging and Monitoring"],
"ServiceName": "s3",
"CheckType": [
"Logging and Monitoring"
],
"ServiceName": "cloudtrail",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "low",

View File

@@ -2,8 +2,10 @@
"Provider": "aws",
"CheckID": "cloudtrail_s3_dataevents_write_enabled",
"CheckTitle": "Check if S3 buckets have Object-level logging for write events is enabled in CloudTrail.",
"CheckType": ["Logging and Monitoring"],
"ServiceName": "s3",
"CheckType": [
"Logging and Monitoring"
],
"ServiceName": "cloudtrail",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "low",

View File

@@ -35,14 +35,13 @@ def check_security_group(
@param any_address: If True, only 0.0.0.0/0 will be public and do not search for public addresses. (Default: False)
"""
# Check for all traffic ingress rules regardless of the protocol
if ingress_rule["IpProtocol"] == "-1":
for ip_ingress_rule in ingress_rule["IpRanges"]:
if _is_cidr_public(ip_ingress_rule["CidrIp"], any_address):
return True
for ip_ingress_rule in ingress_rule["Ipv6Ranges"]:
if _is_cidr_public(ip_ingress_rule["CidrIp"], any_address):
if _is_cidr_public(ip_ingress_rule["CidrIpv6"], any_address):
return True
# Check for specific ports in ingress rules
@@ -76,7 +75,7 @@ def check_security_group(
# IPv6
for ip_ingress_rule in ingress_rule["Ipv6Ranges"]:
if _is_cidr_public(ip_ingress_rule["CidrIp"]):
if _is_cidr_public(ip_ingress_rule["CidrIpv6"]):
# If there are input ports to check
if ports:
for port in ports:

45
prowler
View File

@@ -17,17 +17,16 @@ from lib.check.check import (
exclude_checks_to_run,
exclude_groups_to_run,
exclude_services_to_run,
import_check,
execute_checks,
list_groups,
list_services,
print_checks,
print_services,
run_check,
set_output_options,
)
from lib.check.checks_loader import load_checks_to_execute
from lib.logger import logger, set_logging_config
from lib.outputs.outputs import close_json, send_to_s3_bucket
from lib.outputs.outputs import close_json, display_summary_table, send_to_s3_bucket
from providers.aws.aws_provider import provider_set_session
from providers.aws.lib.allowlist.allowlist import parse_allowlist_file
from providers.aws.lib.security_hub.security_hub import (
@@ -137,6 +136,7 @@ if __name__ == "__main__":
"--output-modes",
nargs="+",
help="Output mode, by default csv",
default=["csv", "json"],
choices=["csv", "json", "json-asff"],
)
parser.add_argument(
@@ -194,6 +194,11 @@ if __name__ == "__main__":
default=None,
help="Path for allowlist yaml file, by default is 'providers/aws/allowlist.yaml'. See default yaml for reference and format.",
)
parser.add_argument(
"--verbose",
action="store_true",
help="Display detailed information about findings.",
)
# Parse Arguments
args = parser.parse_args()
@@ -227,7 +232,7 @@ if __name__ == "__main__":
sys.exit()
if args.no_banner:
print_banner()
print_banner(args)
if args.list_groups:
list_groups(provider)
@@ -321,30 +326,14 @@ if __name__ == "__main__":
args.security_hub,
output_filename,
allowlist_file,
args.verbose,
)
# Execute checks
if len(checks_to_execute):
for check_name in checks_to_execute:
# Recover service from check name
service = check_name.split("_")[0]
try:
# Import check module
check_module_path = (
f"providers.{provider}.services.{service}.{check_name}.{check_name}"
)
lib = import_check(check_module_path)
# Recover functions from check
check_to_execute = getattr(lib, check_name)
c = check_to_execute()
# Run check
run_check(c, audit_info, audit_output_options)
# If check does not exists in the provider or is from another provider
except ModuleNotFoundError:
logger.error(
f"Check '{check_name}' was not found for the {provider.upper()} provider"
)
findings = execute_checks(
checks_to_execute, provider, audit_info, audit_output_options
)
else:
logger.error(
"There are no checks to execute. Please, check your input arguments"
@@ -374,3 +363,11 @@ if __name__ == "__main__":
# Resolve previous fails of Security Hub
if args.security_hub:
resolve_security_hub_previous_findings(output_directory, audit_info)
# Display summary table
display_summary_table(
findings,
audit_info,
output_filename,
output_directory,
)