mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
fix(aws): not show findings when AccessDenieds (#3803)
This commit is contained in:
@@ -178,6 +178,8 @@ class <Service>(ServiceParentClass):
|
|||||||
f"{<item>.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{<item>.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
???+note
|
||||||
|
To avoid fake findings, when Prowler can't retrieve the items, because an Access Denied or similar error, we set that items value as `None`.
|
||||||
|
|
||||||
#### Service Models
|
#### Service Models
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -345,8 +345,10 @@ def prowler():
|
|||||||
global_provider,
|
global_provider,
|
||||||
global_provider.output_options,
|
global_provider.output_options,
|
||||||
)
|
)
|
||||||
# Only display compliance table if there are findings and it is a default execution
|
# Only display compliance table if there are findings (not all MANUAL) and it is a default execution
|
||||||
if findings and default_execution:
|
if (
|
||||||
|
findings and not all(finding.status == "MANUAL" for finding in findings)
|
||||||
|
) and default_execution:
|
||||||
compliance_overview = False
|
compliance_overview = False
|
||||||
if not compliance_framework:
|
if not compliance_framework:
|
||||||
compliance_framework = get_available_compliance_frameworks(provider)
|
compliance_framework = get_available_compliance_frameworks(provider)
|
||||||
|
|||||||
@@ -40,11 +40,13 @@ def display_summary_table(
|
|||||||
entity_type = "Context"
|
entity_type = "Context"
|
||||||
audited_entities = provider.identity.context
|
audited_entities = provider.identity.context
|
||||||
|
|
||||||
if findings:
|
# Check if there are findings and that they are not all MANUAL
|
||||||
|
if findings and not all(finding.status == "MANUAL" for finding in findings):
|
||||||
current = {
|
current = {
|
||||||
"Service": "",
|
"Service": "",
|
||||||
"Provider": "",
|
"Provider": "",
|
||||||
"Total": 0,
|
"Total": 0,
|
||||||
|
"Pass": 0,
|
||||||
"Critical": 0,
|
"Critical": 0,
|
||||||
"High": 0,
|
"High": 0,
|
||||||
"Medium": 0,
|
"Medium": 0,
|
||||||
@@ -70,9 +72,9 @@ def display_summary_table(
|
|||||||
):
|
):
|
||||||
add_service_to_table(findings_table, current)
|
add_service_to_table(findings_table, current)
|
||||||
|
|
||||||
current["Total"] = current["Muted"] = current["Critical"] = current[
|
current["Total"] = current["Pass"] = current["Muted"] = current[
|
||||||
"High"
|
"Critical"
|
||||||
] = current["Medium"] = current["Low"] = 0
|
] = current["High"] = current["Medium"] = current["Low"] = 0
|
||||||
|
|
||||||
current["Service"] = finding.check_metadata.ServiceName
|
current["Service"] = finding.check_metadata.ServiceName
|
||||||
current["Provider"] = finding.check_metadata.Provider
|
current["Provider"] = finding.check_metadata.Provider
|
||||||
@@ -83,6 +85,7 @@ def display_summary_table(
|
|||||||
current["Muted"] += 1
|
current["Muted"] += 1
|
||||||
if finding.status == "PASS":
|
if finding.status == "PASS":
|
||||||
pass_count += 1
|
pass_count += 1
|
||||||
|
current["Pass"] += 1
|
||||||
elif finding.status == "FAIL":
|
elif finding.status == "FAIL":
|
||||||
fail_count += 1
|
fail_count += 1
|
||||||
if finding.check_metadata.Severity == "critical":
|
if finding.check_metadata.Severity == "critical":
|
||||||
@@ -155,7 +158,7 @@ def add_service_to_table(findings_table, current):
|
|||||||
)
|
)
|
||||||
current["Status"] = f"{Fore.RED}FAIL ({total_fails}){Style.RESET_ALL}"
|
current["Status"] = f"{Fore.RED}FAIL ({total_fails}){Style.RESET_ALL}"
|
||||||
else:
|
else:
|
||||||
current["Status"] = f"{Fore.GREEN}PASS ({current['Total']}){Style.RESET_ALL}"
|
current["Status"] = f"{Fore.GREEN}PASS ({current['Pass']}){Style.RESET_ALL}"
|
||||||
|
|
||||||
findings_table["Provider"].append(current["Provider"])
|
findings_table["Provider"].append(current["Provider"])
|
||||||
findings_table["Service"].append(current["Service"])
|
findings_table["Service"].append(current["Service"])
|
||||||
|
|||||||
@@ -789,18 +789,25 @@ class AwsProvider(Provider):
|
|||||||
|
|
||||||
def get_aws_enabled_regions(self, current_session: Session) -> set:
|
def get_aws_enabled_regions(self, current_session: Session) -> set:
|
||||||
"""get_aws_enabled_regions returns a set of enabled AWS regions"""
|
"""get_aws_enabled_regions returns a set of enabled AWS regions"""
|
||||||
|
try:
|
||||||
|
# EC2 Client to check enabled regions
|
||||||
|
service = "ec2"
|
||||||
|
default_region = self.get_default_region(service)
|
||||||
|
ec2_client = current_session.client(service, region_name=default_region)
|
||||||
|
|
||||||
# EC2 Client to check enabled regions
|
enabled_regions = set()
|
||||||
service = "ec2"
|
# With AllRegions=False we only get the enabled regions for the account
|
||||||
default_region = self.get_default_region(service)
|
for region in ec2_client.describe_regions(AllRegions=False).get(
|
||||||
ec2_client = current_session.client(service, region_name=default_region)
|
"Regions", []
|
||||||
|
):
|
||||||
|
enabled_regions.add(region.get("RegionName"))
|
||||||
|
|
||||||
enabled_regions = set()
|
return enabled_regions
|
||||||
# With AllRegions=False we only get the enabled regions for the account
|
except Exception as error:
|
||||||
for region in ec2_client.describe_regions(AllRegions=False).get("Regions", []):
|
logger.error(
|
||||||
enabled_regions.add(region.get("RegionName"))
|
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
return enabled_regions
|
return set()
|
||||||
|
|
||||||
# TODO: review this function
|
# TODO: review this function
|
||||||
# Maybe this should be done within the AwsProvider and not in __main__.py
|
# Maybe this should be done within the AwsProvider and not in __main__.py
|
||||||
|
|||||||
+22
-18
@@ -6,22 +6,26 @@ class account_maintain_different_contact_details_to_security_billing_and_operati
|
|||||||
Check
|
Check
|
||||||
):
|
):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
report = Check_Report_AWS(self.metadata())
|
findings = []
|
||||||
report.region = account_client.region
|
if account_client.contact_base:
|
||||||
report.resource_id = account_client.audited_account
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_arn = account_client.audited_account_arn
|
report.region = account_client.region
|
||||||
|
report.resource_id = account_client.audited_account
|
||||||
|
report.resource_arn = account_client.audited_account_arn
|
||||||
|
|
||||||
if (
|
if (
|
||||||
len(account_client.contact_phone_numbers)
|
len(account_client.contact_phone_numbers)
|
||||||
== account_client.number_of_contacts
|
== account_client.number_of_contacts
|
||||||
and len(account_client.contact_names) == account_client.number_of_contacts
|
and len(account_client.contact_names)
|
||||||
# This is because the primary contact has no email field
|
== account_client.number_of_contacts
|
||||||
and len(account_client.contact_emails)
|
# This is because the primary contact has no email field
|
||||||
== account_client.number_of_contacts - 1
|
and len(account_client.contact_emails)
|
||||||
):
|
== account_client.number_of_contacts - 1
|
||||||
report.status = "PASS"
|
):
|
||||||
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts found and they are different between each other and between ROOT contact."
|
report.status = "PASS"
|
||||||
else:
|
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts found and they are different between each other and between ROOT contact."
|
||||||
report.status = "FAIL"
|
else:
|
||||||
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts not found or they are not different between each other and between ROOT contact."
|
report.status = "FAIL"
|
||||||
return [report]
|
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts not found or they are not different between each other and between ROOT contact."
|
||||||
|
findings.append(report)
|
||||||
|
return findings
|
||||||
|
|||||||
@@ -18,28 +18,29 @@ class Account(AWSService):
|
|||||||
self.contacts_security = self.__get_alternate_contact__("SECURITY")
|
self.contacts_security = self.__get_alternate_contact__("SECURITY")
|
||||||
self.contacts_operations = self.__get_alternate_contact__("OPERATIONS")
|
self.contacts_operations = self.__get_alternate_contact__("OPERATIONS")
|
||||||
|
|
||||||
# Set of contact phone numbers
|
if self.contact_base:
|
||||||
self.contact_phone_numbers = {
|
# Set of contact phone numbers
|
||||||
self.contact_base.phone_number,
|
self.contact_phone_numbers = {
|
||||||
self.contacts_billing.phone_number,
|
self.contact_base.phone_number,
|
||||||
self.contacts_security.phone_number,
|
self.contacts_billing.phone_number,
|
||||||
self.contacts_operations.phone_number,
|
self.contacts_security.phone_number,
|
||||||
}
|
self.contacts_operations.phone_number,
|
||||||
|
}
|
||||||
|
|
||||||
# Set of contact names
|
# Set of contact names
|
||||||
self.contact_names = {
|
self.contact_names = {
|
||||||
self.contact_base.name,
|
self.contact_base.name,
|
||||||
self.contacts_billing.name,
|
self.contacts_billing.name,
|
||||||
self.contacts_security.name,
|
self.contacts_security.name,
|
||||||
self.contacts_operations.name,
|
self.contacts_operations.name,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set of contact emails
|
# Set of contact emails
|
||||||
self.contact_emails = {
|
self.contact_emails = {
|
||||||
self.contacts_billing.email,
|
self.contacts_billing.email,
|
||||||
self.contacts_security.email,
|
self.contacts_security.email,
|
||||||
self.contacts_operations.email,
|
self.contacts_operations.email,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __get_contact_information__(self):
|
def __get_contact_information__(self):
|
||||||
try:
|
try:
|
||||||
@@ -53,10 +54,16 @@ class Account(AWSService):
|
|||||||
phone_number=primary_account_contact.get("PhoneNumber"),
|
phone_number=primary_account_contact.get("PhoneNumber"),
|
||||||
)
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
logger.error(
|
||||||
)
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
return Contact(type="PRIMARY")
|
)
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
return Contact(type="PRIMARY")
|
||||||
|
|
||||||
def __get_alternate_contact__(self, contact_type: str):
|
def __get_alternate_contact__(self, contact_type: str):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from botocore.client import ClientError
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from prowler.lib.logger import logger
|
from prowler.lib.logger import logger
|
||||||
@@ -37,6 +38,8 @@ class Backup(AWSService):
|
|||||||
self.audit_resources,
|
self.audit_resources,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
|
if self.backup_vaults is None:
|
||||||
|
self.backup_vaults = []
|
||||||
self.backup_vaults.append(
|
self.backup_vaults.append(
|
||||||
BackupVault(
|
BackupVault(
|
||||||
arn=configuration.get("BackupVaultArn"),
|
arn=configuration.get("BackupVaultArn"),
|
||||||
@@ -55,7 +58,13 @@ class Backup(AWSService):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except ClientError as error:
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||||
|
if not self.backup_vaults:
|
||||||
|
self.backup_vaults = None
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
|||||||
+17
-17
@@ -5,24 +5,24 @@ from prowler.providers.aws.services.backup.backup_client import backup_client
|
|||||||
class backup_vaults_encrypted(Check):
|
class backup_vaults_encrypted(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
|
if backup_client.backup_vaults:
|
||||||
for backup_vault in backup_client.backup_vaults:
|
for backup_vault in backup_client.backup_vaults:
|
||||||
# By default we assume that the result is fail
|
# By default we assume that the result is fail
|
||||||
report = Check_Report_AWS(self.metadata())
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status = "FAIL"
|
report.status = "FAIL"
|
||||||
report.status_extended = (
|
|
||||||
f"Backup Vault {backup_vault.name} is not encrypted."
|
|
||||||
)
|
|
||||||
report.resource_arn = backup_vault.arn
|
|
||||||
report.resource_id = backup_vault.name
|
|
||||||
report.region = backup_vault.region
|
|
||||||
# if it is encrypted we only change the status and the status extended
|
|
||||||
if backup_vault.encryption:
|
|
||||||
report.status = "PASS"
|
|
||||||
report.status_extended = (
|
report.status_extended = (
|
||||||
f"Backup Vault {backup_vault.name} is encrypted."
|
f"Backup Vault {backup_vault.name} is not encrypted."
|
||||||
)
|
)
|
||||||
# then we store the finding
|
report.resource_arn = backup_vault.arn
|
||||||
findings.append(report)
|
report.resource_id = backup_vault.name
|
||||||
|
report.region = backup_vault.region
|
||||||
|
# if it is encrypted we only change the status and the status extended
|
||||||
|
if backup_vault.encryption:
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = (
|
||||||
|
f"Backup Vault {backup_vault.name} is encrypted."
|
||||||
|
)
|
||||||
|
# then we store the finding
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
@@ -5,18 +5,19 @@ from prowler.providers.aws.services.backup.backup_client import backup_client
|
|||||||
class backup_vaults_exist(Check):
|
class backup_vaults_exist(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if backup_client.backup_vaults is not None:
|
||||||
report.status = "FAIL"
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status_extended = "No Backup Vault exist."
|
report.status = "FAIL"
|
||||||
report.resource_arn = backup_client.backup_vault_arn_template
|
report.status_extended = "No Backup Vault exist."
|
||||||
report.resource_id = backup_client.audited_account
|
report.resource_arn = backup_client.backup_vault_arn_template
|
||||||
report.region = backup_client.region
|
report.resource_id = backup_client.audited_account
|
||||||
if backup_client.backup_vaults:
|
report.region = backup_client.region
|
||||||
report.status = "PASS"
|
if backup_client.backup_vaults:
|
||||||
report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}."
|
report.status = "PASS"
|
||||||
report.resource_arn = backup_client.backup_vaults[0].arn
|
report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}."
|
||||||
report.resource_id = backup_client.backup_vaults[0].name
|
report.resource_arn = backup_client.backup_vaults[0].arn
|
||||||
report.region = backup_client.backup_vaults[0].region
|
report.resource_id = backup_client.backup_vaults[0].name
|
||||||
|
report.region = backup_client.backup_vaults[0].region
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+23
-22
@@ -8,28 +8,29 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
|||||||
class cloudtrail_bucket_requires_mfa_delete(Check):
|
class cloudtrail_bucket_requires_mfa_delete(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
if trail.is_logging:
|
for trail in cloudtrail_client.trails.values():
|
||||||
trail_bucket_is_in_account = False
|
if trail.is_logging:
|
||||||
trail_bucket = trail.s3_bucket
|
trail_bucket_is_in_account = False
|
||||||
report = Check_Report_AWS(self.metadata())
|
trail_bucket = trail.s3_bucket
|
||||||
report.region = trail.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = trail.name
|
report.region = trail.region
|
||||||
report.resource_arn = trail.arn
|
report.resource_id = trail.name
|
||||||
report.resource_tags = trail.tags
|
report.resource_arn = trail.arn
|
||||||
report.status = "FAIL"
|
report.resource_tags = trail.tags
|
||||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled."
|
report.status = "FAIL"
|
||||||
for bucket in s3_client.buckets:
|
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled."
|
||||||
if trail_bucket == bucket.name:
|
for bucket in s3_client.buckets:
|
||||||
trail_bucket_is_in_account = True
|
if trail_bucket == bucket.name:
|
||||||
if bucket.mfa_delete:
|
trail_bucket_is_in_account = True
|
||||||
report.status = "PASS"
|
if bucket.mfa_delete:
|
||||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) has MFA delete enabled."
|
report.status = "PASS"
|
||||||
# check if trail bucket is a cross account bucket
|
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) has MFA delete enabled."
|
||||||
if not trail_bucket_is_in_account:
|
# check if trail bucket is a cross account bucket
|
||||||
report.status = "MANUAL"
|
if not trail_bucket_is_in_account:
|
||||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually."
|
report.status = "MANUAL"
|
||||||
|
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually."
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+30
-29
@@ -11,37 +11,38 @@ maximum_time_without_logging = 1
|
|||||||
class cloudtrail_cloudwatch_logging_enabled(Check):
|
class cloudtrail_cloudwatch_logging_enabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
if trail.name:
|
for trail in cloudtrail_client.trails.values():
|
||||||
report = Check_Report_AWS(self.metadata())
|
if trail.name:
|
||||||
report.region = trail.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = trail.name
|
report.region = trail.region
|
||||||
report.resource_arn = trail.arn
|
report.resource_id = trail.name
|
||||||
report.resource_tags = trail.tags
|
report.resource_arn = trail.arn
|
||||||
report.status = "PASS"
|
report.resource_tags = trail.tags
|
||||||
if trail.is_multiregion:
|
report.status = "PASS"
|
||||||
report.status_extended = (
|
if trail.is_multiregion:
|
||||||
f"Multiregion trail {trail.name} has been logging the last 24h."
|
report.status_extended = f"Multiregion trail {trail.name} has been logging the last 24h."
|
||||||
)
|
else:
|
||||||
else:
|
report.status_extended = f"Single region trail {trail.name} has been logging the last 24h."
|
||||||
report.status_extended = f"Single region trail {trail.name} has been logging the last 24h."
|
if trail.latest_cloudwatch_delivery_time:
|
||||||
if trail.latest_cloudwatch_delivery_time:
|
last_log_delivery = (
|
||||||
last_log_delivery = (
|
datetime.now().replace(tzinfo=timezone.utc)
|
||||||
datetime.now().replace(tzinfo=timezone.utc)
|
- trail.latest_cloudwatch_delivery_time
|
||||||
- trail.latest_cloudwatch_delivery_time
|
)
|
||||||
)
|
if last_log_delivery > timedelta(
|
||||||
if last_log_delivery > timedelta(days=maximum_time_without_logging):
|
days=maximum_time_without_logging
|
||||||
|
):
|
||||||
|
report.status = "FAIL"
|
||||||
|
if trail.is_multiregion:
|
||||||
|
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h."
|
||||||
|
else:
|
||||||
|
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h."
|
||||||
|
else:
|
||||||
report.status = "FAIL"
|
report.status = "FAIL"
|
||||||
if trail.is_multiregion:
|
if trail.is_multiregion:
|
||||||
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h."
|
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
|
||||||
else:
|
else:
|
||||||
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h."
|
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
|
||||||
else:
|
findings.append(report)
|
||||||
report.status = "FAIL"
|
|
||||||
if trail.is_multiregion:
|
|
||||||
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
|
|
||||||
else:
|
|
||||||
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
|
|
||||||
findings.append(report)
|
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+14
-15
@@ -7,19 +7,18 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
|||||||
class cloudtrail_insights_exist(Check):
|
class cloudtrail_insights_exist(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
if trail.is_logging:
|
for trail in cloudtrail_client.trails.values():
|
||||||
report = Check_Report_AWS(self.metadata())
|
if trail.is_logging:
|
||||||
report.region = trail.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = trail.name
|
report.region = trail.region
|
||||||
report.resource_arn = trail.arn
|
report.resource_id = trail.name
|
||||||
report.resource_tags = trail.tags
|
report.resource_arn = trail.arn
|
||||||
report.status = "FAIL"
|
report.resource_tags = trail.tags
|
||||||
report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging."
|
report.status = "FAIL"
|
||||||
if trail.has_insight_selectors:
|
report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging."
|
||||||
report.status = "PASS"
|
if trail.has_insight_selectors:
|
||||||
report.status_extended = (
|
report.status = "PASS"
|
||||||
f"Trail {trail.name} has insight selectors and it is logging."
|
report.status_extended = f"Trail {trail.name} has insight selectors and it is logging."
|
||||||
)
|
findings.append(report)
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+18
-21
@@ -7,32 +7,29 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
|||||||
class cloudtrail_kms_encryption_enabled(Check):
|
class cloudtrail_kms_encryption_enabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
if trail.name:
|
for trail in cloudtrail_client.trails.values():
|
||||||
report = Check_Report_AWS(self.metadata())
|
if trail.name:
|
||||||
report.region = trail.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = trail.name
|
report.region = trail.region
|
||||||
report.resource_arn = trail.arn
|
report.resource_id = trail.name
|
||||||
report.resource_tags = trail.tags
|
report.resource_arn = trail.arn
|
||||||
report.status = "FAIL"
|
report.resource_tags = trail.tags
|
||||||
if trail.is_multiregion:
|
report.status = "FAIL"
|
||||||
report.status_extended = (
|
|
||||||
f"Multiregion trail {trail.name} has encryption disabled."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
report.status_extended = (
|
|
||||||
f"Single region trail {trail.name} has encryption disabled."
|
|
||||||
)
|
|
||||||
if trail.kms_key:
|
|
||||||
report.status = "PASS"
|
|
||||||
if trail.is_multiregion:
|
if trail.is_multiregion:
|
||||||
report.status_extended = (
|
report.status_extended = (
|
||||||
f"Multiregion trail {trail.name} has encryption enabled."
|
f"Multiregion trail {trail.name} has encryption disabled."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
report.status_extended = (
|
report.status_extended = (
|
||||||
f"Single region trail {trail.name} has encryption enabled."
|
f"Single region trail {trail.name} has encryption disabled."
|
||||||
)
|
)
|
||||||
findings.append(report)
|
if trail.kms_key:
|
||||||
|
report.status = "PASS"
|
||||||
|
if trail.is_multiregion:
|
||||||
|
report.status_extended = f"Multiregion trail {trail.name} has encryption enabled."
|
||||||
|
else:
|
||||||
|
report.status_extended = f"Single region trail {trail.name} has encryption enabled."
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+18
-19
@@ -7,26 +7,25 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
|||||||
class cloudtrail_log_file_validation_enabled(Check):
|
class cloudtrail_log_file_validation_enabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
if trail.name:
|
for trail in cloudtrail_client.trails.values():
|
||||||
report = Check_Report_AWS(self.metadata())
|
if trail.name:
|
||||||
report.region = trail.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = trail.name
|
report.region = trail.region
|
||||||
report.resource_arn = trail.arn
|
report.resource_id = trail.name
|
||||||
report.resource_tags = trail.tags
|
report.resource_arn = trail.arn
|
||||||
report.status = "FAIL"
|
report.resource_tags = trail.tags
|
||||||
if trail.is_multiregion:
|
report.status = "FAIL"
|
||||||
report.status_extended = (
|
|
||||||
f"Multiregion trail {trail.name} log file validation disabled."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
report.status_extended = f"Single region trail {trail.name} log file validation disabled."
|
|
||||||
if trail.log_file_validation_enabled:
|
|
||||||
report.status = "PASS"
|
|
||||||
if trail.is_multiregion:
|
if trail.is_multiregion:
|
||||||
report.status_extended = f"Multiregion trail {trail.name} log file validation enabled."
|
report.status_extended = f"Multiregion trail {trail.name} log file validation disabled."
|
||||||
else:
|
else:
|
||||||
report.status_extended = f"Single region trail {trail.name} log file validation enabled."
|
report.status_extended = f"Single region trail {trail.name} log file validation disabled."
|
||||||
findings.append(report)
|
if trail.log_file_validation_enabled:
|
||||||
|
report.status = "PASS"
|
||||||
|
if trail.is_multiregion:
|
||||||
|
report.status_extended = f"Multiregion trail {trail.name} log file validation enabled."
|
||||||
|
else:
|
||||||
|
report.status_extended = f"Single region trail {trail.name} log file validation enabled."
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+30
-29
@@ -8,35 +8,36 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
|||||||
class cloudtrail_logs_s3_bucket_access_logging_enabled(Check):
|
class cloudtrail_logs_s3_bucket_access_logging_enabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
if trail.name:
|
for trail in cloudtrail_client.trails.values():
|
||||||
trail_bucket_is_in_account = False
|
if trail.name:
|
||||||
trail_bucket = trail.s3_bucket
|
trail_bucket_is_in_account = False
|
||||||
report = Check_Report_AWS(self.metadata())
|
trail_bucket = trail.s3_bucket
|
||||||
report.region = trail.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = trail.name
|
report.region = trail.region
|
||||||
report.resource_arn = trail.arn
|
report.resource_id = trail.name
|
||||||
report.resource_tags = trail.tags
|
report.resource_arn = trail.arn
|
||||||
report.status = "FAIL"
|
report.resource_tags = trail.tags
|
||||||
if trail.is_multiregion:
|
report.status = "FAIL"
|
||||||
report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
|
if trail.is_multiregion:
|
||||||
else:
|
report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
|
||||||
report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
|
else:
|
||||||
for bucket in s3_client.buckets:
|
report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
|
||||||
if trail_bucket == bucket.name:
|
for bucket in s3_client.buckets:
|
||||||
trail_bucket_is_in_account = True
|
if trail_bucket == bucket.name:
|
||||||
if bucket.logging:
|
trail_bucket_is_in_account = True
|
||||||
report.status = "PASS"
|
if bucket.logging:
|
||||||
if trail.is_multiregion:
|
report.status = "PASS"
|
||||||
report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
|
if trail.is_multiregion:
|
||||||
else:
|
report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
|
||||||
report.status_extended = f"Single region trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
|
else:
|
||||||
break
|
report.status_extended = f"Single region trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
|
||||||
|
break
|
||||||
|
|
||||||
# check if trail is delivering logs in a cross account bucket
|
# check if trail is delivering logs in a cross account bucket
|
||||||
if not trail_bucket_is_in_account:
|
if not trail_bucket_is_in_account:
|
||||||
report.status = "MANUAL"
|
report.status = "MANUAL"
|
||||||
report.status_extended = f"Trail {trail.name} is delivering logs in a cross-account bucket {trail_bucket} in another account out of Prowler's permissions scope, please check it manually."
|
report.status_extended = f"Trail {trail.name} is delivering logs in a cross-account bucket {trail_bucket} in another account out of Prowler's permissions scope, please check it manually."
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+37
-36
@@ -8,41 +8,42 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
|||||||
class cloudtrail_logs_s3_bucket_is_not_publicly_accessible(Check):
|
class cloudtrail_logs_s3_bucket_is_not_publicly_accessible(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
if trail.name:
|
for trail in cloudtrail_client.trails.values():
|
||||||
trail_bucket_is_in_account = False
|
if trail.name:
|
||||||
trail_bucket = trail.s3_bucket
|
trail_bucket_is_in_account = False
|
||||||
report = Check_Report_AWS(self.metadata())
|
trail_bucket = trail.s3_bucket
|
||||||
report.region = trail.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = trail.name
|
report.region = trail.region
|
||||||
report.resource_arn = trail.arn
|
report.resource_id = trail.name
|
||||||
report.resource_tags = trail.tags
|
report.resource_arn = trail.arn
|
||||||
report.status = "PASS"
|
report.resource_tags = trail.tags
|
||||||
if trail.is_multiregion:
|
report.status = "PASS"
|
||||||
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible."
|
if trail.is_multiregion:
|
||||||
else:
|
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible."
|
||||||
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible."
|
else:
|
||||||
for bucket in s3_client.buckets:
|
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible."
|
||||||
# Here we need to ensure that acl_grantee is filled since if we don't have permissions to query the api for a concrete region
|
for bucket in s3_client.buckets:
|
||||||
# (for example due to a SCP) we are going to try access an attribute from a None type
|
# Here we need to ensure that acl_grantee is filled since if we don't have permissions to query the api for a concrete region
|
||||||
if trail_bucket == bucket.name:
|
# (for example due to a SCP) we are going to try access an attribute from a None type
|
||||||
trail_bucket_is_in_account = True
|
if trail_bucket == bucket.name:
|
||||||
if bucket.acl_grantees:
|
trail_bucket_is_in_account = True
|
||||||
for grant in bucket.acl_grantees:
|
if bucket.acl_grantees:
|
||||||
if (
|
for grant in bucket.acl_grantees:
|
||||||
grant.URI
|
if (
|
||||||
== "http://acs.amazonaws.com/groups/global/AllUsers"
|
grant.URI
|
||||||
):
|
== "http://acs.amazonaws.com/groups/global/AllUsers"
|
||||||
report.status = "FAIL"
|
):
|
||||||
if trail.is_multiregion:
|
report.status = "FAIL"
|
||||||
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible."
|
if trail.is_multiregion:
|
||||||
else:
|
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible."
|
||||||
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible."
|
else:
|
||||||
break
|
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible."
|
||||||
# check if trail bucket is a cross account bucket
|
break
|
||||||
if not trail_bucket_is_in_account:
|
# check if trail bucket is a cross account bucket
|
||||||
report.status = "MANUAL"
|
if not trail_bucket_is_in_account:
|
||||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually."
|
report.status = "MANUAL"
|
||||||
findings.append(report)
|
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually."
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+30
-31
@@ -7,36 +7,35 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
|||||||
class cloudtrail_multi_region_enabled(Check):
|
class cloudtrail_multi_region_enabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for region in cloudtrail_client.regional_clients.keys():
|
if cloudtrail_client.trails is not None:
|
||||||
report = Check_Report_AWS(self.metadata())
|
for region in cloudtrail_client.regional_clients.keys():
|
||||||
report.region = region
|
report = Check_Report_AWS(self.metadata())
|
||||||
for trail in cloudtrail_client.trails.values():
|
report.region = region
|
||||||
if trail.region == region or trail.is_multiregion:
|
for trail in cloudtrail_client.trails.values():
|
||||||
if trail.is_logging:
|
if trail.region == region or trail.is_multiregion:
|
||||||
report.status = "PASS"
|
if trail.is_logging:
|
||||||
report.resource_id = trail.name
|
report.status = "PASS"
|
||||||
report.resource_arn = trail.arn
|
report.resource_id = trail.name
|
||||||
report.resource_tags = trail.tags
|
report.resource_arn = trail.arn
|
||||||
if trail.is_multiregion:
|
report.resource_tags = trail.tags
|
||||||
report.status_extended = (
|
if trail.is_multiregion:
|
||||||
f"Trail {trail.name} is multiregion and it is logging."
|
report.status_extended = f"Trail {trail.name} is multiregion and it is logging."
|
||||||
)
|
else:
|
||||||
|
report.status_extended = f"Trail {trail.name} is not multiregion and it is logging."
|
||||||
|
# Since there exists a logging trail in that region there is no point in checking the remaining trails
|
||||||
|
# Store the finding and exit the loop
|
||||||
|
findings.append(report)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
report.status_extended = f"Trail {trail.name} is not multiregion and it is logging."
|
report.status = "FAIL"
|
||||||
# Since there exists a logging trail in that region there is no point in checking the remaining trails
|
report.status_extended = (
|
||||||
# Store the finding and exit the loop
|
"No CloudTrail trails enabled and logging were found."
|
||||||
findings.append(report)
|
)
|
||||||
break
|
report.resource_arn = (
|
||||||
else:
|
cloudtrail_client.__get_trail_arn_template__(region)
|
||||||
report.status = "FAIL"
|
)
|
||||||
report.status_extended = (
|
report.resource_id = cloudtrail_client.audited_account
|
||||||
"No CloudTrail trails enabled and logging were found."
|
# If there are no trails logging it is needed to store the FAIL once all the trails have been checked
|
||||||
)
|
if report.status == "FAIL":
|
||||||
report.resource_arn = (
|
findings.append(report)
|
||||||
cloudtrail_client.__get_trail_arn_template__(region)
|
|
||||||
)
|
|
||||||
report.resource_id = cloudtrail_client.audited_account
|
|
||||||
# If there are no trails logging it is needed to store the FAIL once all the trails have been checked
|
|
||||||
if report.status == "FAIL":
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+43
-42
@@ -7,48 +7,49 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
|||||||
class cloudtrail_multi_region_enabled_logging_management_events(Check):
|
class cloudtrail_multi_region_enabled_logging_management_events(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if cloudtrail_client.trails is not None:
|
||||||
report.status = "FAIL"
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status_extended = (
|
report.status = "FAIL"
|
||||||
"No trail found with multi-region enabled and logging management events."
|
report.status_extended = "No trail found with multi-region enabled and logging management events."
|
||||||
)
|
report.region = cloudtrail_client.region
|
||||||
report.region = cloudtrail_client.region
|
report.resource_id = cloudtrail_client.audited_account
|
||||||
report.resource_id = cloudtrail_client.audited_account
|
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
|
||||||
|
|
||||||
for trail in cloudtrail_client.trails.values():
|
for trail in cloudtrail_client.trails.values():
|
||||||
if trail.is_logging:
|
if trail.is_logging:
|
||||||
if trail.is_multiregion:
|
if trail.is_multiregion:
|
||||||
for event in trail.data_events:
|
for event in trail.data_events:
|
||||||
# Classic event selectors
|
# Classic event selectors
|
||||||
if not event.is_advanced:
|
if not event.is_advanced:
|
||||||
# Check if trail has IncludeManagementEvents and ReadWriteType is All
|
# Check if trail has IncludeManagementEvents and ReadWriteType is All
|
||||||
if (
|
if (
|
||||||
event.event_selector["ReadWriteType"] == "All"
|
event.event_selector["ReadWriteType"] == "All"
|
||||||
and event.event_selector["IncludeManagementEvents"]
|
and event.event_selector["IncludeManagementEvents"]
|
||||||
):
|
):
|
||||||
report.region = trail.region
|
report.region = trail.region
|
||||||
report.resource_id = trail.name
|
report.resource_id = trail.name
|
||||||
report.resource_arn = trail.arn
|
report.resource_arn = trail.arn
|
||||||
report.resource_tags = trail.tags
|
report.resource_tags = trail.tags
|
||||||
report.status = "PASS"
|
report.status = "PASS"
|
||||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled."
|
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled."
|
||||||
|
|
||||||
# Advanced event selectors
|
# Advanced event selectors
|
||||||
elif event.is_advanced:
|
elif event.is_advanced:
|
||||||
if event.event_selector.get(
|
if event.event_selector.get(
|
||||||
"Name"
|
"Name"
|
||||||
) == "Management events selector" and all(
|
) == "Management events selector" and all(
|
||||||
[
|
[
|
||||||
field["Field"] != "readOnly"
|
field["Field"] != "readOnly"
|
||||||
for field in event.event_selector["FieldSelectors"]
|
for field in event.event_selector[
|
||||||
]
|
"FieldSelectors"
|
||||||
):
|
]
|
||||||
report.region = trail.region
|
]
|
||||||
report.resource_id = trail.name
|
):
|
||||||
report.resource_arn = trail.arn
|
report.region = trail.region
|
||||||
report.resource_tags = trail.tags
|
report.resource_id = trail.name
|
||||||
report.status = "PASS"
|
report.resource_arn = trail.arn
|
||||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled."
|
report.resource_tags = trail.tags
|
||||||
findings.append(report)
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled."
|
||||||
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+46
-43
@@ -8,23 +8,41 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
|||||||
class cloudtrail_s3_dataevents_read_enabled(Check):
|
class cloudtrail_s3_dataevents_read_enabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
for data_event in trail.data_events:
|
for trail in cloudtrail_client.trails.values():
|
||||||
# classic event selectors
|
for data_event in trail.data_events:
|
||||||
if not data_event.is_advanced:
|
# classic event selectors
|
||||||
# Check if trail has a data event for all S3 Buckets for read
|
if not data_event.is_advanced:
|
||||||
if (
|
# Check if trail has a data event for all S3 Buckets for read
|
||||||
data_event.event_selector["ReadWriteType"] == "ReadOnly"
|
if (
|
||||||
or data_event.event_selector["ReadWriteType"] == "All"
|
data_event.event_selector["ReadWriteType"] == "ReadOnly"
|
||||||
):
|
or data_event.event_selector["ReadWriteType"] == "All"
|
||||||
for resource in data_event.event_selector["DataResources"]:
|
):
|
||||||
if "AWS::S3::Object" == resource["Type"] and (
|
for resource in data_event.event_selector["DataResources"]:
|
||||||
f"arn:{cloudtrail_client.audited_partition}:s3"
|
if "AWS::S3::Object" == resource["Type"] and (
|
||||||
in resource["Values"]
|
f"arn:{cloudtrail_client.audited_partition}:s3"
|
||||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
|
in resource["Values"]
|
||||||
in resource["Values"]
|
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
|
||||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
|
in resource["Values"]
|
||||||
in resource["Values"]
|
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
|
||||||
|
in resource["Values"]
|
||||||
|
):
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = trail.region
|
||||||
|
report.resource_id = trail.name
|
||||||
|
report.resource_arn = trail.arn
|
||||||
|
report.resource_tags = trail.tags
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
|
||||||
|
findings.append(report)
|
||||||
|
# advanced event selectors
|
||||||
|
elif data_event.is_advanced:
|
||||||
|
for field_selector in data_event.event_selector[
|
||||||
|
"FieldSelectors"
|
||||||
|
]:
|
||||||
|
if (
|
||||||
|
field_selector["Field"] == "resources.type"
|
||||||
|
and field_selector["Equals"][0] == "AWS::S3::Object"
|
||||||
):
|
):
|
||||||
report = Check_Report_AWS(self.metadata())
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.region = trail.region
|
report.region = trail.region
|
||||||
@@ -32,31 +50,16 @@ class cloudtrail_s3_dataevents_read_enabled(Check):
|
|||||||
report.resource_arn = trail.arn
|
report.resource_arn = trail.arn
|
||||||
report.resource_tags = trail.tags
|
report.resource_tags = trail.tags
|
||||||
report.status = "PASS"
|
report.status = "PASS"
|
||||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
|
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
# advanced event selectors
|
if not findings and (
|
||||||
elif data_event.is_advanced:
|
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
||||||
for field_selector in data_event.event_selector["FieldSelectors"]:
|
):
|
||||||
if (
|
report = Check_Report_AWS(self.metadata())
|
||||||
field_selector["Field"] == "resources.type"
|
report.region = cloudtrail_client.region
|
||||||
and field_selector["Equals"][0] == "AWS::S3::Object"
|
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||||
):
|
report.resource_id = cloudtrail_client.audited_account
|
||||||
report = Check_Report_AWS(self.metadata())
|
report.status = "FAIL"
|
||||||
report.region = trail.region
|
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
|
||||||
report.resource_id = trail.name
|
findings.append(report)
|
||||||
report.resource_arn = trail.arn
|
|
||||||
report.resource_tags = trail.tags
|
|
||||||
report.status = "PASS"
|
|
||||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
|
|
||||||
findings.append(report)
|
|
||||||
if not findings and (
|
|
||||||
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
|
||||||
):
|
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = cloudtrail_client.region
|
|
||||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
|
||||||
report.resource_id = cloudtrail_client.audited_account
|
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+46
-43
@@ -8,23 +8,41 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
|||||||
class cloudtrail_s3_dataevents_write_enabled(Check):
|
class cloudtrail_s3_dataevents_write_enabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for trail in cloudtrail_client.trails.values():
|
if cloudtrail_client.trails is not None:
|
||||||
for data_event in trail.data_events:
|
for trail in cloudtrail_client.trails.values():
|
||||||
# Classic event selectors
|
for data_event in trail.data_events:
|
||||||
if not data_event.is_advanced:
|
# Classic event selectors
|
||||||
# Check if trail has a data event for all S3 Buckets for write
|
if not data_event.is_advanced:
|
||||||
if (
|
# Check if trail has a data event for all S3 Buckets for write
|
||||||
data_event.event_selector["ReadWriteType"] == "All"
|
if (
|
||||||
or data_event.event_selector["ReadWriteType"] == "WriteOnly"
|
data_event.event_selector["ReadWriteType"] == "All"
|
||||||
):
|
or data_event.event_selector["ReadWriteType"] == "WriteOnly"
|
||||||
for resource in data_event.event_selector["DataResources"]:
|
):
|
||||||
if "AWS::S3::Object" == resource["Type"] and (
|
for resource in data_event.event_selector["DataResources"]:
|
||||||
f"arn:{cloudtrail_client.audited_partition}:s3"
|
if "AWS::S3::Object" == resource["Type"] and (
|
||||||
in resource["Values"]
|
f"arn:{cloudtrail_client.audited_partition}:s3"
|
||||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
|
in resource["Values"]
|
||||||
in resource["Values"]
|
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
|
||||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
|
in resource["Values"]
|
||||||
in resource["Values"]
|
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
|
||||||
|
in resource["Values"]
|
||||||
|
):
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = trail.region
|
||||||
|
report.resource_id = trail.name
|
||||||
|
report.resource_arn = trail.arn
|
||||||
|
report.resource_tags = trail.tags
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
|
||||||
|
findings.append(report)
|
||||||
|
# Advanced event selectors
|
||||||
|
elif data_event.is_advanced:
|
||||||
|
for field_selector in data_event.event_selector[
|
||||||
|
"FieldSelectors"
|
||||||
|
]:
|
||||||
|
if (
|
||||||
|
field_selector["Field"] == "resources.type"
|
||||||
|
and field_selector["Equals"][0] == "AWS::S3::Object"
|
||||||
):
|
):
|
||||||
report = Check_Report_AWS(self.metadata())
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.region = trail.region
|
report.region = trail.region
|
||||||
@@ -32,31 +50,16 @@ class cloudtrail_s3_dataevents_write_enabled(Check):
|
|||||||
report.resource_arn = trail.arn
|
report.resource_arn = trail.arn
|
||||||
report.resource_tags = trail.tags
|
report.resource_tags = trail.tags
|
||||||
report.status = "PASS"
|
report.status = "PASS"
|
||||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
|
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
# Advanced event selectors
|
if not findings and (
|
||||||
elif data_event.is_advanced:
|
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
||||||
for field_selector in data_event.event_selector["FieldSelectors"]:
|
):
|
||||||
if (
|
report = Check_Report_AWS(self.metadata())
|
||||||
field_selector["Field"] == "resources.type"
|
report.region = cloudtrail_client.region
|
||||||
and field_selector["Equals"][0] == "AWS::S3::Object"
|
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||||
):
|
report.resource_id = cloudtrail_client.audited_account
|
||||||
report = Check_Report_AWS(self.metadata())
|
report.status = "FAIL"
|
||||||
report.region = trail.region
|
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
|
||||||
report.resource_id = trail.name
|
findings.append(report)
|
||||||
report.resource_arn = trail.arn
|
|
||||||
report.resource_tags = trail.tags
|
|
||||||
report.status = "PASS"
|
|
||||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
|
|
||||||
findings.append(report)
|
|
||||||
if not findings and (
|
|
||||||
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
|
||||||
):
|
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = cloudtrail_client.region
|
|
||||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
|
||||||
report.resource_id = cloudtrail_client.audited_account
|
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ class Cloudtrail(AWSService):
|
|||||||
self.trail_arn_template = f"arn:{self.audited_partition}:cloudtrail:{self.region}:{self.audited_account}:trail"
|
self.trail_arn_template = f"arn:{self.audited_partition}:cloudtrail:{self.region}:{self.audited_account}:trail"
|
||||||
self.trails = {}
|
self.trails = {}
|
||||||
self.__threading_call__(self.__get_trails__)
|
self.__threading_call__(self.__get_trails__)
|
||||||
self.__get_trail_status__()
|
if self.trails:
|
||||||
self.__get_insight_selectors__()
|
self.__get_trail_status__()
|
||||||
self.__get_event_selectors__()
|
self.__get_insight_selectors__()
|
||||||
self.__list_tags_for_resource__()
|
self.__get_event_selectors__()
|
||||||
|
self.__list_tags_for_resource__()
|
||||||
|
|
||||||
def __get_trail_arn_template__(self, region):
|
def __get_trail_arn_template__(self, region):
|
||||||
return (
|
return (
|
||||||
@@ -45,6 +46,8 @@ class Cloudtrail(AWSService):
|
|||||||
kms_key_id = trail["KmsKeyId"]
|
kms_key_id = trail["KmsKeyId"]
|
||||||
if "CloudWatchLogsLogGroupArn" in trail:
|
if "CloudWatchLogsLogGroupArn" in trail:
|
||||||
log_group_arn = trail["CloudWatchLogsLogGroupArn"]
|
log_group_arn = trail["CloudWatchLogsLogGroupArn"]
|
||||||
|
if self.trails is None:
|
||||||
|
self.trails = {}
|
||||||
self.trails[trail["TrailARN"]] = Trail(
|
self.trails[trail["TrailARN"]] = Trail(
|
||||||
name=trail["Name"],
|
name=trail["Name"],
|
||||||
is_multiregion=trail["IsMultiRegionTrail"],
|
is_multiregion=trail["IsMultiRegionTrail"],
|
||||||
@@ -61,12 +64,24 @@ class Cloudtrail(AWSService):
|
|||||||
has_insight_selectors=trail.get("HasInsightSelectors"),
|
has_insight_selectors=trail.get("HasInsightSelectors"),
|
||||||
)
|
)
|
||||||
if trails_count == 0:
|
if trails_count == 0:
|
||||||
|
if self.trails is None:
|
||||||
|
self.trails = {}
|
||||||
self.trails[self.__get_trail_arn_template__(regional_client.region)] = (
|
self.trails[self.__get_trail_arn_template__(regional_client.region)] = (
|
||||||
Trail(
|
Trail(
|
||||||
region=regional_client.region,
|
region=regional_client.region,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
if not self.trails:
|
||||||
|
self.trails = None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_acls_alarm_configured(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventName\s*=\s*.?CreateNetworkAcl.+\$\.eventName\s*=\s*.?CreateNetworkAclEntry.+\$\.eventName\s*=\s*.?DeleteNetworkAcl.+\$\.eventName\s*=\s*.?DeleteNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclAssociation.?"
|
pattern = r"\$\.eventName\s*=\s*.?CreateNetworkAcl.+\$\.eventName\s*=\s*.?CreateNetworkAclEntry.+\$\.eventName\s*=\s*.?DeleteNetworkAcl.+\$\.eventName\s*=\s*.?DeleteNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclAssociation.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_gateways_alarm_configured(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventName\s*=\s*.?CreateCustomerGateway.+\$\.eventName\s*=\s*.?DeleteCustomerGateway.+\$\.eventName\s*=\s*.?AttachInternetGateway.+\$\.eventName\s*=\s*.?CreateInternetGateway.+\$\.eventName\s*=\s*.?DeleteInternetGateway.+\$\.eventName\s*=\s*.?DetachInternetGateway.?"
|
pattern = r"\$\.eventName\s*=\s*.?CreateCustomerGateway.+\$\.eventName\s*=\s*.?DeleteCustomerGateway.+\$\.eventName\s*=\s*.?AttachInternetGateway.+\$\.eventName\s*=\s*.?CreateInternetGateway.+\$\.eventName\s*=\s*.?DeleteInternetGateway.+\$\.eventName\s*=\s*.?DetachInternetGateway.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_route_tables_alarm_configured(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventSource\s*=\s*.?ec2.amazonaws.com.+\$\.eventName\s*=\s*.?CreateRoute.+\$\.eventName\s*=\s*.?CreateRouteTable.+\$\.eventName\s*=\s*.?ReplaceRoute.+\$\.eventName\s*=\s*.?ReplaceRouteTableAssociation.+\$\.eventName\s*=\s*.?DeleteRouteTable.+\$\.eventName\s*=\s*.?DeleteRoute.+\$\.eventName\s*=\s*.?DisassociateRouteTable.?"
|
pattern = r"\$\.eventSource\s*=\s*.?ec2.amazonaws.com.+\$\.eventName\s*=\s*.?CreateRoute.+\$\.eventName\s*=\s*.?CreateRouteTable.+\$\.eventName\s*=\s*.?ReplaceRoute.+\$\.eventName\s*=\s*.?ReplaceRouteTableAssociation.+\$\.eventName\s*=\s*.?DeleteRouteTable.+\$\.eventName\s*=\s*.?DeleteRoute.+\$\.eventName\s*=\s*.?DisassociateRouteTable.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_vpcs_alarm_configured(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventName\s*=\s*.?CreateVpc.+\$\.eventName\s*=\s*.?DeleteVpc.+\$\.eventName\s*=\s*.?ModifyVpcAttribute.+\$\.eventName\s*=\s*.?AcceptVpcPeeringConnection.+\$\.eventName\s*=\s*.?CreateVpcPeeringConnection.+\$\.eventName\s*=\s*.?DeleteVpcPeeringConnection.+\$\.eventName\s*=\s*.?RejectVpcPeeringConnection.+\$\.eventName\s*=\s*.?AttachClassicLinkVpc.+\$\.eventName\s*=\s*.?DetachClassicLinkVpc.+\$\.eventName\s*=\s*.?DisableVpcClassicLink.+\$\.eventName\s*=\s*.?EnableVpcClassicLink.?"
|
pattern = r"\$\.eventName\s*=\s*.?CreateVpc.+\$\.eventName\s*=\s*.?DeleteVpc.+\$\.eventName\s*=\s*.?ModifyVpcAttribute.+\$\.eventName\s*=\s*.?AcceptVpcPeeringConnection.+\$\.eventName\s*=\s*.?CreateVpcPeeringConnection.+\$\.eventName\s*=\s*.?DeleteVpcPeeringConnection.+\$\.eventName\s*=\s*.?RejectVpcPeeringConnection.+\$\.eventName\s*=\s*.?AttachClassicLinkVpc.+\$\.eventName\s*=\s*.?DetachClassicLinkVpc.+\$\.eventName\s*=\s*.?DisableVpcClassicLink.+\$\.eventName\s*=\s*.?EnableVpcClassicLink.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+16
-13
@@ -5,17 +5,20 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class cloudwatch_cross_account_sharing_disabled(Check):
|
class cloudwatch_cross_account_sharing_disabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if iam_client.roles is not None:
|
||||||
report.status = "PASS"
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status_extended = "CloudWatch doesn't allow cross-account sharing."
|
report.status = "PASS"
|
||||||
report.resource_arn = iam_client.role_arn_template
|
report.status_extended = "CloudWatch doesn't allow cross-account sharing."
|
||||||
report.resource_id = iam_client.audited_account
|
report.resource_arn = iam_client.role_arn_template
|
||||||
report.region = iam_client.region
|
report.resource_id = iam_client.audited_account
|
||||||
for role in iam_client.roles:
|
report.region = iam_client.region
|
||||||
if role.name == "CloudWatch-CrossAccountSharingRole":
|
for role in iam_client.roles:
|
||||||
report.resource_arn = role.arn
|
if role.name == "CloudWatch-CrossAccountSharingRole":
|
||||||
report.resource_id = role.name
|
report.resource_arn = role.arn
|
||||||
report.status = "FAIL"
|
report.resource_id = role.name
|
||||||
report.status_extended = "CloudWatch has allowed cross-account sharing."
|
report.status = "FAIL"
|
||||||
findings.append(report)
|
report.status_extended = (
|
||||||
|
"CloudWatch has allowed cross-account sharing."
|
||||||
|
)
|
||||||
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+14
-15
@@ -5,19 +5,18 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client
|
|||||||
class cloudwatch_log_group_kms_encryption_enabled(Check):
|
class cloudwatch_log_group_kms_encryption_enabled(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for log_group in logs_client.log_groups:
|
if logs_client.log_groups:
|
||||||
report = Check_Report_AWS(self.metadata())
|
for log_group in logs_client.log_groups:
|
||||||
report.region = log_group.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = log_group.name
|
report.region = log_group.region
|
||||||
report.resource_arn = log_group.arn
|
report.resource_id = log_group.name
|
||||||
report.resource_tags = log_group.tags
|
report.resource_arn = log_group.arn
|
||||||
if log_group.kms_id:
|
report.resource_tags = log_group.tags
|
||||||
report.status = "PASS"
|
if log_group.kms_id:
|
||||||
report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated."
|
report.status = "PASS"
|
||||||
else:
|
report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated."
|
||||||
report.status = "FAIL"
|
else:
|
||||||
report.status_extended = (
|
report.status = "FAIL"
|
||||||
f"Log Group {log_group.name} does not have AWS KMS keys associated."
|
report.status_extended = f"Log Group {log_group.name} does not have AWS KMS keys associated."
|
||||||
)
|
findings.append(report)
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+77
-69
@@ -11,78 +11,86 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client
|
|||||||
class cloudwatch_log_group_no_secrets_in_logs(Check):
|
class cloudwatch_log_group_no_secrets_in_logs(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
for log_group in logs_client.log_groups:
|
if logs_client.log_groups:
|
||||||
report = Check_Report_AWS(self.metadata())
|
for log_group in logs_client.log_groups:
|
||||||
report.status = "PASS"
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status_extended = f"No secrets found in {log_group.name} log group."
|
report.status = "PASS"
|
||||||
report.region = log_group.region
|
report.status_extended = (
|
||||||
report.resource_id = log_group.name
|
f"No secrets found in {log_group.name} log group."
|
||||||
report.resource_arn = log_group.arn
|
)
|
||||||
log_group_secrets = []
|
report.region = log_group.region
|
||||||
if log_group.log_streams:
|
report.resource_id = log_group.name
|
||||||
for log_stream_name in log_group.log_streams:
|
report.resource_arn = log_group.arn
|
||||||
log_stream_secrets = {}
|
log_group_secrets = []
|
||||||
log_stream_data = "\n".join(
|
if log_group.log_streams:
|
||||||
[
|
for log_stream_name in log_group.log_streams:
|
||||||
dumps(event["message"])
|
log_stream_secrets = {}
|
||||||
for event in log_group.log_streams[log_stream_name]
|
log_stream_data = "\n".join(
|
||||||
]
|
|
||||||
)
|
|
||||||
log_stream_secrets_output = detect_secrets_scan(log_stream_data)
|
|
||||||
|
|
||||||
if log_stream_secrets_output:
|
|
||||||
for secret in log_stream_secrets_output:
|
|
||||||
flagged_event = log_group.log_streams[log_stream_name][
|
|
||||||
secret["line_number"] - 1
|
|
||||||
]
|
|
||||||
cloudwatch_timestamp = (
|
|
||||||
convert_to_cloudwatch_timestamp_format(
|
|
||||||
flagged_event["timestamp"]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if cloudwatch_timestamp not in log_stream_secrets.keys():
|
|
||||||
log_stream_secrets[cloudwatch_timestamp] = SecretsDict()
|
|
||||||
|
|
||||||
try:
|
|
||||||
log_event_data = dumps(
|
|
||||||
loads(flagged_event["message"]), indent=2
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
log_event_data = dumps(
|
|
||||||
flagged_event["message"], indent=2
|
|
||||||
)
|
|
||||||
if len(log_event_data.split("\n")) > 1:
|
|
||||||
# Can get more informative output if there is more than 1 line.
|
|
||||||
# Will rescan just this event to get the type of secret and the line number
|
|
||||||
event_detect_secrets_output = detect_secrets_scan(
|
|
||||||
log_event_data
|
|
||||||
)
|
|
||||||
if event_detect_secrets_output:
|
|
||||||
for secret in event_detect_secrets_output:
|
|
||||||
log_stream_secrets[
|
|
||||||
cloudwatch_timestamp
|
|
||||||
].add_secret(
|
|
||||||
secret["line_number"], secret["type"]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
log_stream_secrets[cloudwatch_timestamp].add_secret(
|
|
||||||
1, secret["type"]
|
|
||||||
)
|
|
||||||
if log_stream_secrets:
|
|
||||||
secrets_string = "; ".join(
|
|
||||||
[
|
[
|
||||||
f"at {timestamp} - {log_stream_secrets[timestamp].to_string()}"
|
dumps(event["message"])
|
||||||
for timestamp in log_stream_secrets
|
for event in log_group.log_streams[log_stream_name]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
log_group_secrets.append(
|
log_stream_secrets_output = detect_secrets_scan(log_stream_data)
|
||||||
f"in log stream {log_stream_name} {secrets_string}"
|
|
||||||
)
|
if log_stream_secrets_output:
|
||||||
if log_group_secrets:
|
for secret in log_stream_secrets_output:
|
||||||
secrets_string = "; ".join(log_group_secrets)
|
flagged_event = log_group.log_streams[log_stream_name][
|
||||||
report.status = "FAIL"
|
secret["line_number"] - 1
|
||||||
report.status_extended = f"Potential secrets found in log group {log_group.name} {secrets_string}."
|
]
|
||||||
findings.append(report)
|
cloudwatch_timestamp = (
|
||||||
|
convert_to_cloudwatch_timestamp_format(
|
||||||
|
flagged_event["timestamp"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
cloudwatch_timestamp
|
||||||
|
not in log_stream_secrets.keys()
|
||||||
|
):
|
||||||
|
log_stream_secrets[cloudwatch_timestamp] = (
|
||||||
|
SecretsDict()
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
log_event_data = dumps(
|
||||||
|
loads(flagged_event["message"]), indent=2
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
log_event_data = dumps(
|
||||||
|
flagged_event["message"], indent=2
|
||||||
|
)
|
||||||
|
if len(log_event_data.split("\n")) > 1:
|
||||||
|
# Can get more informative output if there is more than 1 line.
|
||||||
|
# Will rescan just this event to get the type of secret and the line number
|
||||||
|
event_detect_secrets_output = detect_secrets_scan(
|
||||||
|
log_event_data
|
||||||
|
)
|
||||||
|
if event_detect_secrets_output:
|
||||||
|
for secret in event_detect_secrets_output:
|
||||||
|
log_stream_secrets[
|
||||||
|
cloudwatch_timestamp
|
||||||
|
].add_secret(
|
||||||
|
secret["line_number"], secret["type"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
log_stream_secrets[cloudwatch_timestamp].add_secret(
|
||||||
|
1, secret["type"]
|
||||||
|
)
|
||||||
|
if log_stream_secrets:
|
||||||
|
secrets_string = "; ".join(
|
||||||
|
[
|
||||||
|
f"at {timestamp} - {log_stream_secrets[timestamp].to_string()}"
|
||||||
|
for timestamp in log_stream_secrets
|
||||||
|
]
|
||||||
|
)
|
||||||
|
log_group_secrets.append(
|
||||||
|
f"in log stream {log_stream_name} {secrets_string}"
|
||||||
|
)
|
||||||
|
if log_group_secrets:
|
||||||
|
secrets_string = "; ".join(log_group_secrets)
|
||||||
|
report.status = "FAIL"
|
||||||
|
report.status_extended = f"Potential secrets found in log group {log_group.name} {secrets_string}."
|
||||||
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+19
-18
@@ -10,23 +10,24 @@ class cloudwatch_log_group_retention_policy_specific_days_enabled(Check):
|
|||||||
specific_retention_days = logs_client.audit_config.get(
|
specific_retention_days = logs_client.audit_config.get(
|
||||||
"log_group_retention_days", 365
|
"log_group_retention_days", 365
|
||||||
)
|
)
|
||||||
for log_group in logs_client.log_groups:
|
if logs_client.log_groups:
|
||||||
report = Check_Report_AWS(self.metadata())
|
for log_group in logs_client.log_groups:
|
||||||
report.region = log_group.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = log_group.name
|
report.region = log_group.region
|
||||||
report.resource_arn = log_group.arn
|
report.resource_id = log_group.name
|
||||||
report.resource_tags = log_group.tags
|
report.resource_arn = log_group.arn
|
||||||
if (
|
report.resource_tags = log_group.tags
|
||||||
log_group.never_expire is False
|
if (
|
||||||
and log_group.retention_days < specific_retention_days
|
log_group.never_expire is False
|
||||||
):
|
and log_group.retention_days < specific_retention_days
|
||||||
report.status = "FAIL"
|
):
|
||||||
report.status_extended = f"Log Group {log_group.name} has less than {specific_retention_days} days retention period ({log_group.retention_days} days)."
|
report.status = "FAIL"
|
||||||
else:
|
report.status_extended = f"Log Group {log_group.name} has less than {specific_retention_days} days retention period ({log_group.retention_days} days)."
|
||||||
report.status = "PASS"
|
|
||||||
if log_group.never_expire is True:
|
|
||||||
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it never expires."
|
|
||||||
else:
|
else:
|
||||||
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it has {log_group.retention_days} days."
|
report.status = "PASS"
|
||||||
findings.append(report)
|
if log_group.never_expire is True:
|
||||||
|
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it never expires."
|
||||||
|
else:
|
||||||
|
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it has {log_group.retention_days} days."
|
||||||
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -17,21 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_change
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventSource\s*=\s*.?config.amazonaws.com.+\$\.eventName\s*=\s*.?StopConfigurationRecorder.+\$\.eventName\s*=\s*.?DeleteDeliveryChannel.+\$\.eventName\s*=\s*.?PutDeliveryChannel.+\$\.eventName\s*=\s*.?PutConfigurationRecorder.?"
|
pattern = r"\$\.eventSource\s*=\s*.?config.amazonaws.com.+\$\.eventName\s*=\s*.?StopConfigurationRecorder.+\$\.eventName\s*=\s*.?DeleteDeliveryChannel.+\$\.eventName\s*=\s*.?PutDeliveryChannel.+\$\.eventName\s*=\s*.?PutConfigurationRecorder.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -17,21 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_change
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventName\s*=\s*.?CreateTrail.+\$\.eventName\s*=\s*.?UpdateTrail.+\$\.eventName\s*=\s*.?DeleteTrail.+\$\.eventName\s*=\s*.?StartLogging.+\$\.eventName\s*=\s*.?StopLogging.?"
|
pattern = r"\$\.eventName\s*=\s*.?CreateTrail.+\$\.eventName\s*=\s*.?UpdateTrail.+\$\.eventName\s*=\s*.?DeleteTrail.+\$\.eventName\s*=\s*.?StartLogging.+\$\.eventName\s*=\s*.?StopLogging.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_authentication_failures(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.errorMessage\s*=\s*.?Failed authentication.?"
|
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.errorMessage\s*=\s*.?Failed authentication.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_aws_organizations_changes(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventSource\s*=\s*.?organizations\.amazonaws\.com.+\$\.eventName\s*=\s*.?AcceptHandshake.+\$\.eventName\s*=\s*.?AttachPolicy.+\$\.eventName\s*=\s*.?CancelHandshake.+\$\.eventName\s*=\s*.?CreateAccount.+\$\.eventName\s*=\s*.?CreateOrganization.+\$\.eventName\s*=\s*.?CreateOrganizationalUnit.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeclineHandshake.+\$\.eventName\s*=\s*.?DeleteOrganization.+\$\.eventName\s*=\s*.?DeleteOrganizationalUnit.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?EnableAllFeatures.+\$\.eventName\s*=\s*.?EnablePolicyType.+\$\.eventName\s*=\s*.?InviteAccountToOrganization.+\$\.eventName\s*=\s*.?LeaveOrganization.+\$\.eventName\s*=\s*.?DetachPolicy.+\$\.eventName\s*=\s*.?DisablePolicyType.+\$\.eventName\s*=\s*.?MoveAccount.+\$\.eventName\s*=\s*.?RemoveAccountFromOrganization.+\$\.eventName\s*=\s*.?UpdateOrganizationalUnit.+\$\.eventName\s*=\s*.?UpdatePolicy.?"
|
pattern = r"\$\.eventSource\s*=\s*.?organizations\.amazonaws\.com.+\$\.eventName\s*=\s*.?AcceptHandshake.+\$\.eventName\s*=\s*.?AttachPolicy.+\$\.eventName\s*=\s*.?CancelHandshake.+\$\.eventName\s*=\s*.?CreateAccount.+\$\.eventName\s*=\s*.?CreateOrganization.+\$\.eventName\s*=\s*.?CreateOrganizationalUnit.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeclineHandshake.+\$\.eventName\s*=\s*.?DeleteOrganization.+\$\.eventName\s*=\s*.?DeleteOrganizationalUnit.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?EnableAllFeatures.+\$\.eventName\s*=\s*.?EnablePolicyType.+\$\.eventName\s*=\s*.?InviteAccountToOrganization.+\$\.eventName\s*=\s*.?LeaveOrganization.+\$\.eventName\s*=\s*.?DetachPolicy.+\$\.eventName\s*=\s*.?DisablePolicyType.+\$\.eventName\s*=\s*.?MoveAccount.+\$\.eventName\s*=\s*.?RemoveAccountFromOrganization.+\$\.eventName\s*=\s*.?UpdateOrganizationalUnit.+\$\.eventName\s*=\s*.?UpdatePolicy.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk(Chec
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventSource\s*=\s*.?kms.amazonaws.com.+\$\.eventName\s*=\s*.?DisableKey.+\$\.eventName\s*=\s*.?ScheduleKeyDeletion.?"
|
pattern = r"\$\.eventSource\s*=\s*.?kms.amazonaws.com.+\$\.eventName\s*=\s*.?DisableKey.+\$\.eventName\s*=\s*.?ScheduleKeyDeletion.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,22 +15,25 @@ class cloudwatch_log_metric_filter_for_s3_bucket_policy_changes(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventSource\s*=\s*.?s3.amazonaws.com.+\$\.eventName\s*=\s*.?PutBucketAcl.+\$\.eventName\s*=\s*.?PutBucketPolicy.+\$\.eventName\s*=\s*.?PutBucketCors.+\$\.eventName\s*=\s*.?PutBucketLifecycle.+\$\.eventName\s*=\s*.?PutBucketReplication.+\$\.eventName\s*=\s*.?DeleteBucketPolicy.+\$\.eventName\s*=\s*.?DeleteBucketCors.+\$\.eventName\s*=\s*.?DeleteBucketLifecycle.+\$\.eventName\s*=\s*.?DeleteBucketReplication.?"
|
pattern = r"\$\.eventSource\s*=\s*.?s3.amazonaws.com.+\$\.eventName\s*=\s*.?PutBucketAcl.+\$\.eventName\s*=\s*.?PutBucketPolicy.+\$\.eventName\s*=\s*.?PutBucketCors.+\$\.eventName\s*=\s*.?PutBucketLifecycle.+\$\.eventName\s*=\s*.?PutBucketReplication.+\$\.eventName\s*=\s*.?DeleteBucketPolicy.+\$\.eventName\s*=\s*.?DeleteBucketCors.+\$\.eventName\s*=\s*.?DeleteBucketLifecycle.+\$\.eventName\s*=\s*.?DeleteBucketReplication.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
|
report.region = logs_client.region
|
||||||
|
report.resource_id = logs_client.audited_account
|
||||||
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
|
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report = check_cloudwatch_log_metric_filter(
|
||||||
pattern,
|
pattern,
|
||||||
cloudtrail_client.trails,
|
cloudtrail_client.trails,
|
||||||
logs_client.metric_filters,
|
logs_client.metric_filters,
|
||||||
cloudwatch_client.metric_alarms,
|
cloudwatch_client.metric_alarms,
|
||||||
report,
|
report,
|
||||||
)
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_policy_changes(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventName\s*=\s*.?DeleteGroupPolicy.+\$\.eventName\s*=\s*.?DeleteRolePolicy.+\$\.eventName\s*=\s*.?DeleteUserPolicy.+\$\.eventName\s*=\s*.?PutGroupPolicy.+\$\.eventName\s*=\s*.?PutRolePolicy.+\$\.eventName\s*=\s*.?PutUserPolicy.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?CreatePolicyVersion.+\$\.eventName\s*=\s*.?DeletePolicyVersion.+\$\.eventName\s*=\s*.?AttachRolePolicy.+\$\.eventName\s*=\s*.?DetachRolePolicy.+\$\.eventName\s*=\s*.?AttachUserPolicy.+\$\.eventName\s*=\s*.?DetachUserPolicy.+\$\.eventName\s*=\s*.?AttachGroupPolicy.+\$\.eventName\s*=\s*.?DetachGroupPolicy.?"
|
pattern = r"\$\.eventName\s*=\s*.?DeleteGroupPolicy.+\$\.eventName\s*=\s*.?DeleteRolePolicy.+\$\.eventName\s*=\s*.?DeleteUserPolicy.+\$\.eventName\s*=\s*.?PutGroupPolicy.+\$\.eventName\s*=\s*.?PutRolePolicy.+\$\.eventName\s*=\s*.?PutUserPolicy.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?CreatePolicyVersion.+\$\.eventName\s*=\s*.?DeletePolicyVersion.+\$\.eventName\s*=\s*.?AttachRolePolicy.+\$\.eventName\s*=\s*.?DetachRolePolicy.+\$\.eventName\s*=\s*.?AttachUserPolicy.+\$\.eventName\s*=\s*.?DetachUserPolicy.+\$\.eventName\s*=\s*.?AttachGroupPolicy.+\$\.eventName\s*=\s*.?DetachGroupPolicy.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_root_usage(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.userIdentity\.type\s*=\s*.?Root.+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*.?AwsServiceEvent.?"
|
pattern = r"\$\.userIdentity\.type\s*=\s*.?Root.+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*.?AwsServiceEvent.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_security_group_changes(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventName\s*=\s*.?AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*.?AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupIngress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupEgress.+\$\.eventName\s*=\s*.?CreateSecurityGroup.+\$\.eventName\s*=\s*.?DeleteSecurityGroup.?"
|
pattern = r"\$\.eventName\s*=\s*.?AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*.?AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupIngress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupEgress.+\$\.eventName\s*=\s*.?CreateSecurityGroup.+\$\.eventName\s*=\s*.?DeleteSecurityGroup.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_sign_in_without_mfa(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*.?Yes.?"
|
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*.?Yes.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_unauthorized_api_calls(Check):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
pattern = r"\$\.errorCode\s*=\s*.?\*UnauthorizedOperation.+\$\.errorCode\s*=\s*.?AccessDenied\*.?"
|
pattern = r"\$\.errorCode\s*=\s*.?\*UnauthorizedOperation.+\$\.errorCode\s*=\s*.?AccessDenied\*.?"
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if (
|
||||||
report.status = "FAIL"
|
cloudtrail_client.trails is not None
|
||||||
report.status_extended = (
|
and logs_client.metric_filters is not None
|
||||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
and cloudwatch_client.metric_alarms is not None
|
||||||
)
|
):
|
||||||
report.region = logs_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = logs_client.audited_account
|
report.status = "FAIL"
|
||||||
report.resource_arn = logs_client.log_group_arn_template
|
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||||
report = check_cloudwatch_log_metric_filter(
|
report.region = logs_client.region
|
||||||
pattern,
|
report.resource_id = logs_client.audited_account
|
||||||
cloudtrail_client.trails,
|
report.resource_arn = logs_client.log_group_arn_template
|
||||||
logs_client.metric_filters,
|
report = check_cloudwatch_log_metric_filter(
|
||||||
cloudwatch_client.metric_alarms,
|
pattern,
|
||||||
report,
|
cloudtrail_client.trails,
|
||||||
)
|
logs_client.metric_filters,
|
||||||
|
cloudwatch_client.metric_alarms,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ class CloudWatch(AWSService):
|
|||||||
super().__init__(__class__.__name__, provider)
|
super().__init__(__class__.__name__, provider)
|
||||||
self.metric_alarms = []
|
self.metric_alarms = []
|
||||||
self.__threading_call__(self.__describe_alarms__)
|
self.__threading_call__(self.__describe_alarms__)
|
||||||
self.__list_tags_for_resource__()
|
if self.metric_alarms:
|
||||||
|
self.__list_tags_for_resource__()
|
||||||
|
|
||||||
def __describe_alarms__(self, regional_client):
|
def __describe_alarms__(self, regional_client):
|
||||||
logger.info("CloudWatch - Describing alarms...")
|
logger.info("CloudWatch - Describing alarms...")
|
||||||
@@ -33,6 +34,8 @@ class CloudWatch(AWSService):
|
|||||||
namespace = None
|
namespace = None
|
||||||
if "Namespace" in alarm:
|
if "Namespace" in alarm:
|
||||||
namespace = alarm["Namespace"]
|
namespace = alarm["Namespace"]
|
||||||
|
if self.metric_alarms is None:
|
||||||
|
self.metric_alarms = []
|
||||||
self.metric_alarms.append(
|
self.metric_alarms.append(
|
||||||
MetricAlarm(
|
MetricAlarm(
|
||||||
arn=alarm["AlarmArn"],
|
arn=alarm["AlarmArn"],
|
||||||
@@ -42,6 +45,17 @@ class CloudWatch(AWSService):
|
|||||||
region=regional_client.region,
|
region=regional_client.region,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "AccessDenied":
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
if not self.metric_alarms:
|
||||||
|
self.metric_alarms = None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
@@ -72,15 +86,16 @@ class Logs(AWSService):
|
|||||||
self.log_groups = []
|
self.log_groups = []
|
||||||
self.__threading_call__(self.__describe_metric_filters__)
|
self.__threading_call__(self.__describe_metric_filters__)
|
||||||
self.__threading_call__(self.__describe_log_groups__)
|
self.__threading_call__(self.__describe_log_groups__)
|
||||||
if (
|
if self.log_groups:
|
||||||
"cloudwatch_log_group_no_secrets_in_logs"
|
if (
|
||||||
in provider.audit_metadata.expected_checks
|
"cloudwatch_log_group_no_secrets_in_logs"
|
||||||
):
|
in provider.audit_metadata.expected_checks
|
||||||
self.events_per_log_group_threshold = (
|
):
|
||||||
1000 # The threshold for number of events to return per log group.
|
self.events_per_log_group_threshold = (
|
||||||
)
|
1000 # The threshold for number of events to return per log group.
|
||||||
self.__threading_call__(self.__get_log_events__)
|
)
|
||||||
self.__list_tags_for_resource__()
|
self.__threading_call__(self.__get_log_events__)
|
||||||
|
self.__list_tags_for_resource__()
|
||||||
|
|
||||||
def __describe_metric_filters__(self, regional_client):
|
def __describe_metric_filters__(self, regional_client):
|
||||||
logger.info("CloudWatch Logs - Describing metric filters...")
|
logger.info("CloudWatch Logs - Describing metric filters...")
|
||||||
@@ -94,6 +109,8 @@ class Logs(AWSService):
|
|||||||
if not self.audit_resources or (
|
if not self.audit_resources or (
|
||||||
is_resource_filtered(arn, self.audit_resources)
|
is_resource_filtered(arn, self.audit_resources)
|
||||||
):
|
):
|
||||||
|
if self.metric_filters is None:
|
||||||
|
self.metric_filters = []
|
||||||
self.metric_filters.append(
|
self.metric_filters.append(
|
||||||
MetricFilter(
|
MetricFilter(
|
||||||
arn=arn,
|
arn=arn,
|
||||||
@@ -104,6 +121,17 @@ class Logs(AWSService):
|
|||||||
region=regional_client.region,
|
region=regional_client.region,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
if not self.metric_filters:
|
||||||
|
self.metric_filters = []
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
@@ -126,6 +154,8 @@ class Logs(AWSService):
|
|||||||
if not retention_days:
|
if not retention_days:
|
||||||
never_expire = True
|
never_expire = True
|
||||||
retention_days = 9999
|
retention_days = 9999
|
||||||
|
if self.log_groups is None:
|
||||||
|
self.log_groups = []
|
||||||
self.log_groups.append(
|
self.log_groups.append(
|
||||||
LogGroup(
|
LogGroup(
|
||||||
arn=log_group["arn"],
|
arn=log_group["arn"],
|
||||||
@@ -136,6 +166,17 @@ class Logs(AWSService):
|
|||||||
region=regional_client.region,
|
region=regional_client.region,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
if not self.log_groups:
|
||||||
|
self.log_groups = None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ def check_cloudwatch_log_metric_filter(
|
|||||||
):
|
):
|
||||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||||
log_groups = []
|
log_groups = []
|
||||||
for trail in trails.values():
|
if trails is not None:
|
||||||
if trail.log_group_arn:
|
for trail in trails.values():
|
||||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
if trail.log_group_arn:
|
||||||
|
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||||
# 2. Describe metric filters for previous log groups
|
# 2. Describe metric filters for previous log groups
|
||||||
for metric_filter in metric_filters:
|
for metric_filter in metric_filters:
|
||||||
if metric_filter.log_group in log_groups:
|
if metric_filter.log_group in log_groups:
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ class FMS(AWSService):
|
|||||||
):
|
):
|
||||||
# FMS is not enabled in this account
|
# FMS is not enabled in this account
|
||||||
self.fms_admin_account = False
|
self.fms_admin_account = False
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
|
||||||
|
)
|
||||||
|
self.fms_admin_account = None
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
|
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
|
||||||
|
|||||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_password_policy_expires_passwords_within_90_days_or_less(Check):
|
class iam_password_policy_expires_passwords_within_90_days_or_less(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = iam_client.region
|
|
||||||
report.resource_arn = iam_client.password_policy_arn_template
|
|
||||||
report.resource_id = iam_client.audited_account
|
|
||||||
# Check if password policy exists
|
|
||||||
if iam_client.password_policy:
|
if iam_client.password_policy:
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = iam_client.region
|
||||||
|
report.resource_arn = iam_client.password_policy_arn_template
|
||||||
|
report.resource_id = iam_client.audited_account
|
||||||
# Check if password policy expiration exists
|
# Check if password policy expiration exists
|
||||||
if iam_client.password_policy.max_age:
|
if iam_client.password_policy.max_age:
|
||||||
if iam_client.password_policy.max_age <= 90:
|
if iam_client.password_policy.max_age <= 90:
|
||||||
@@ -22,9 +21,6 @@ class iam_password_policy_expires_passwords_within_90_days_or_less(Check):
|
|||||||
else:
|
else:
|
||||||
report.status = "FAIL"
|
report.status = "FAIL"
|
||||||
report.status_extended = "Password expiration is not set."
|
report.status_extended = "Password expiration is not set."
|
||||||
else:
|
findings.append(report)
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "Password policy cannot be found."
|
|
||||||
findings.append(report)
|
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_password_policy_lowercase(Check):
|
class iam_password_policy_lowercase(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = iam_client.region
|
|
||||||
report.resource_arn = iam_client.password_policy_arn_template
|
|
||||||
report.resource_id = iam_client.audited_account
|
|
||||||
# Check if password policy exists
|
|
||||||
if iam_client.password_policy:
|
if iam_client.password_policy:
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = iam_client.region
|
||||||
|
report.resource_arn = iam_client.password_policy_arn_template
|
||||||
|
report.resource_id = iam_client.audited_account
|
||||||
# Check if lowercase flag is set
|
# Check if lowercase flag is set
|
||||||
if iam_client.password_policy.lowercase:
|
if iam_client.password_policy.lowercase:
|
||||||
report.status = "PASS"
|
report.status = "PASS"
|
||||||
@@ -20,8 +19,5 @@ class iam_password_policy_lowercase(Check):
|
|||||||
else:
|
else:
|
||||||
report.status = "FAIL"
|
report.status = "FAIL"
|
||||||
report.status_extended = "IAM password policy does not require at least one lowercase letter."
|
report.status_extended = "IAM password policy does not require at least one lowercase letter."
|
||||||
else:
|
findings.append(report)
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "Password policy cannot be found."
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_password_policy_minimum_length_14(Check):
|
class iam_password_policy_minimum_length_14(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = iam_client.region
|
|
||||||
report.resource_arn = iam_client.password_policy_arn_template
|
|
||||||
report.resource_id = iam_client.audited_account
|
|
||||||
# Check if password policy exists
|
|
||||||
if iam_client.password_policy:
|
if iam_client.password_policy:
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = iam_client.region
|
||||||
|
report.resource_arn = iam_client.password_policy_arn_template
|
||||||
|
report.resource_id = iam_client.audited_account
|
||||||
# Check password policy length
|
# Check password policy length
|
||||||
if (
|
if (
|
||||||
iam_client.password_policy.length
|
iam_client.password_policy.length
|
||||||
@@ -23,8 +22,5 @@ class iam_password_policy_minimum_length_14(Check):
|
|||||||
else:
|
else:
|
||||||
report.status = "FAIL"
|
report.status = "FAIL"
|
||||||
report.status_extended = "IAM password policy does not require minimum length of 14 characters."
|
report.status_extended = "IAM password policy does not require minimum length of 14 characters."
|
||||||
else:
|
findings.append(report)
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "Password policy cannot be found."
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_password_policy_number(Check):
|
class iam_password_policy_number(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = iam_client.region
|
|
||||||
report.resource_arn = iam_client.password_policy_arn_template
|
|
||||||
report.resource_id = iam_client.audited_account
|
|
||||||
# Check if password policy exists
|
|
||||||
if iam_client.password_policy:
|
if iam_client.password_policy:
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = iam_client.region
|
||||||
|
report.resource_arn = iam_client.password_policy_arn_template
|
||||||
|
report.resource_id = iam_client.audited_account
|
||||||
# Check if number flag is set
|
# Check if number flag is set
|
||||||
if iam_client.password_policy.numbers:
|
if iam_client.password_policy.numbers:
|
||||||
report.status = "PASS"
|
report.status = "PASS"
|
||||||
@@ -22,8 +21,5 @@ class iam_password_policy_number(Check):
|
|||||||
report.status_extended = (
|
report.status_extended = (
|
||||||
"IAM password policy does not require at least one number."
|
"IAM password policy does not require at least one number."
|
||||||
)
|
)
|
||||||
else:
|
findings.append(report)
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "Password policy cannot be found."
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_password_policy_reuse_24(Check):
|
class iam_password_policy_reuse_24(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = iam_client.region
|
|
||||||
report.resource_arn = iam_client.password_policy_arn_template
|
|
||||||
report.resource_id = iam_client.audited_account
|
|
||||||
# Check if password policy exists
|
|
||||||
if iam_client.password_policy:
|
if iam_client.password_policy:
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = iam_client.region
|
||||||
|
report.resource_arn = iam_client.password_policy_arn_template
|
||||||
|
report.resource_id = iam_client.audited_account
|
||||||
# Check if reuse prevention flag is set
|
# Check if reuse prevention flag is set
|
||||||
if (
|
if (
|
||||||
iam_client.password_policy.reuse_prevention
|
iam_client.password_policy.reuse_prevention
|
||||||
@@ -25,8 +24,5 @@ class iam_password_policy_reuse_24(Check):
|
|||||||
report.status_extended = (
|
report.status_extended = (
|
||||||
"IAM password policy reuse prevention is less than 24 or not set."
|
"IAM password policy reuse prevention is less than 24 or not set."
|
||||||
)
|
)
|
||||||
else:
|
findings.append(report)
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "Password policy cannot be found."
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_password_policy_symbol(Check):
|
class iam_password_policy_symbol(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = iam_client.region
|
|
||||||
report.resource_arn = iam_client.password_policy_arn_template
|
|
||||||
report.resource_id = iam_client.audited_account
|
|
||||||
# Check if password policy exists
|
|
||||||
if iam_client.password_policy:
|
if iam_client.password_policy:
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = iam_client.region
|
||||||
|
report.resource_arn = iam_client.password_policy_arn_template
|
||||||
|
report.resource_id = iam_client.audited_account
|
||||||
# Check if symbol flag is set
|
# Check if symbol flag is set
|
||||||
if iam_client.password_policy.symbols:
|
if iam_client.password_policy.symbols:
|
||||||
report.status = "PASS"
|
report.status = "PASS"
|
||||||
@@ -22,8 +21,5 @@ class iam_password_policy_symbol(Check):
|
|||||||
report.status_extended = (
|
report.status_extended = (
|
||||||
"IAM password policy does not require at least one symbol."
|
"IAM password policy does not require at least one symbol."
|
||||||
)
|
)
|
||||||
else:
|
findings.append(report)
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "Password policy cannot be found."
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_password_policy_uppercase(Check):
|
class iam_password_policy_uppercase(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
|
||||||
report.region = iam_client.region
|
|
||||||
report.resource_arn = iam_client.password_policy_arn_template
|
|
||||||
report.resource_id = iam_client.audited_account
|
|
||||||
# Check if password policy exists
|
|
||||||
if iam_client.password_policy:
|
if iam_client.password_policy:
|
||||||
|
report = Check_Report_AWS(self.metadata())
|
||||||
|
report.region = iam_client.region
|
||||||
|
report.resource_arn = iam_client.password_policy_arn_template
|
||||||
|
report.resource_id = iam_client.audited_account
|
||||||
# Check if uppercase flag is set
|
# Check if uppercase flag is set
|
||||||
if iam_client.password_policy.uppercase:
|
if iam_client.password_policy.uppercase:
|
||||||
report.status = "PASS"
|
report.status = "PASS"
|
||||||
@@ -20,8 +19,5 @@ class iam_password_policy_uppercase(Check):
|
|||||||
else:
|
else:
|
||||||
report.status = "FAIL"
|
report.status = "FAIL"
|
||||||
report.status_extended = "IAM password policy does not require at least one uppercase letter."
|
report.status_extended = "IAM password policy does not require at least one uppercase letter."
|
||||||
else:
|
findings.append(report)
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = "Password policy cannot be found."
|
|
||||||
findings.append(report)
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+17
-18
@@ -5,24 +5,23 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_role_administratoraccess_policy(Check):
|
class iam_role_administratoraccess_policy(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
for role in iam_client.roles:
|
if iam_client.roles:
|
||||||
if (
|
for role in iam_client.roles:
|
||||||
not role.is_service_role
|
if (
|
||||||
): # Avoid service roles since they cannot be modified by the user
|
not role.is_service_role
|
||||||
report = Check_Report_AWS(self.metadata())
|
): # Avoid service roles since they cannot be modified by the user
|
||||||
report.region = iam_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_arn = role.arn
|
report.region = iam_client.region
|
||||||
report.resource_id = role.name
|
report.resource_arn = role.arn
|
||||||
report.resource_tags = role.tags
|
report.resource_id = role.name
|
||||||
report.status = "PASS"
|
report.resource_tags = role.tags
|
||||||
report.status_extended = (
|
report.status = "PASS"
|
||||||
f"IAM Role {role.name} does not have AdministratorAccess policy."
|
report.status_extended = f"IAM Role {role.name} does not have AdministratorAccess policy."
|
||||||
)
|
for policy in role.attached_policies:
|
||||||
for policy in role.attached_policies:
|
if policy["PolicyName"] == "AdministratorAccess":
|
||||||
if policy["PolicyName"] == "AdministratorAccess":
|
report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached."
|
||||||
report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached."
|
report.status = "FAIL"
|
||||||
report.status = "FAIL"
|
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+63
-59
@@ -5,78 +5,82 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_role_cross_account_readonlyaccess_policy(Check):
|
class iam_role_cross_account_readonlyaccess_policy(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
for role in iam_client.roles:
|
if iam_client.roles:
|
||||||
if (
|
for role in iam_client.roles:
|
||||||
not role.is_service_role
|
if (
|
||||||
): # Avoid service roles since they cannot be modified by the user
|
not role.is_service_role
|
||||||
report = Check_Report_AWS(self.metadata())
|
): # Avoid service roles since they cannot be modified by the user
|
||||||
report.region = iam_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_arn = role.arn
|
report.region = iam_client.region
|
||||||
report.resource_id = role.name
|
report.resource_arn = role.arn
|
||||||
report.resource_tags = role.tags
|
report.resource_id = role.name
|
||||||
report.status = "PASS"
|
report.resource_tags = role.tags
|
||||||
report.status_extended = (
|
report.status = "PASS"
|
||||||
f"IAM Role {role.name} does not have ReadOnlyAccess policy."
|
report.status_extended = (
|
||||||
)
|
f"IAM Role {role.name} does not have ReadOnlyAccess policy."
|
||||||
for policy in role.attached_policies:
|
)
|
||||||
if policy["PolicyName"] == "ReadOnlyAccess":
|
for policy in role.attached_policies:
|
||||||
report.status_extended = f"IAM Role {role.name} has read-only access but is not cross account."
|
if policy["PolicyName"] == "ReadOnlyAccess":
|
||||||
cross_account_access = False
|
report.status_extended = f"IAM Role {role.name} has read-only access but is not cross account."
|
||||||
if isinstance(role.assume_role_policy["Statement"], list):
|
cross_account_access = False
|
||||||
for statement in role.assume_role_policy["Statement"]:
|
if isinstance(role.assume_role_policy["Statement"], list):
|
||||||
if not cross_account_access:
|
for statement in role.assume_role_policy["Statement"]:
|
||||||
if (
|
if not cross_account_access:
|
||||||
statement["Effect"] == "Allow"
|
if (
|
||||||
and "AWS" in statement["Principal"]
|
statement["Effect"] == "Allow"
|
||||||
):
|
and "AWS" in statement["Principal"]
|
||||||
if isinstance(
|
|
||||||
statement["Principal"]["AWS"], list
|
|
||||||
):
|
):
|
||||||
for aws_account in statement["Principal"][
|
if isinstance(
|
||||||
"AWS"
|
statement["Principal"]["AWS"], list
|
||||||
]:
|
):
|
||||||
|
for aws_account in statement[
|
||||||
|
"Principal"
|
||||||
|
]["AWS"]:
|
||||||
|
if (
|
||||||
|
iam_client.audited_account
|
||||||
|
not in aws_account
|
||||||
|
or "*" == aws_account
|
||||||
|
):
|
||||||
|
cross_account_access = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
if (
|
if (
|
||||||
iam_client.audited_account
|
iam_client.audited_account
|
||||||
not in aws_account
|
not in statement["Principal"]["AWS"]
|
||||||
or "*" == aws_account
|
or "*"
|
||||||
|
== statement["Principal"]["AWS"]
|
||||||
):
|
):
|
||||||
cross_account_access = True
|
cross_account_access = True
|
||||||
break
|
else:
|
||||||
else:
|
break
|
||||||
|
else:
|
||||||
|
statement = role.assume_role_policy["Statement"]
|
||||||
|
if (
|
||||||
|
statement["Effect"] == "Allow"
|
||||||
|
and "AWS" in statement["Principal"]
|
||||||
|
):
|
||||||
|
if isinstance(statement["Principal"]["AWS"], list):
|
||||||
|
for aws_account in statement["Principal"][
|
||||||
|
"AWS"
|
||||||
|
]:
|
||||||
if (
|
if (
|
||||||
iam_client.audited_account
|
iam_client.audited_account
|
||||||
not in statement["Principal"]["AWS"]
|
not in aws_account
|
||||||
or "*" == statement["Principal"]["AWS"]
|
or "*" == aws_account
|
||||||
):
|
):
|
||||||
cross_account_access = True
|
cross_account_access = True
|
||||||
else:
|
break
|
||||||
break
|
else:
|
||||||
else:
|
|
||||||
statement = role.assume_role_policy["Statement"]
|
|
||||||
if (
|
|
||||||
statement["Effect"] == "Allow"
|
|
||||||
and "AWS" in statement["Principal"]
|
|
||||||
):
|
|
||||||
if isinstance(statement["Principal"]["AWS"], list):
|
|
||||||
for aws_account in statement["Principal"]["AWS"]:
|
|
||||||
if (
|
if (
|
||||||
iam_client.audited_account
|
iam_client.audited_account
|
||||||
not in aws_account
|
not in statement["Principal"]["AWS"]
|
||||||
or "*" == aws_account
|
or "*" == statement["Principal"]["AWS"]
|
||||||
):
|
):
|
||||||
cross_account_access = True
|
cross_account_access = True
|
||||||
break
|
if cross_account_access:
|
||||||
else:
|
report.status = "FAIL"
|
||||||
if (
|
report.status_extended = f"IAM Role {role.name} gives cross account read-only access."
|
||||||
iam_client.audited_account
|
|
||||||
not in statement["Principal"]["AWS"]
|
|
||||||
or "*" == statement["Principal"]["AWS"]
|
|
||||||
):
|
|
||||||
cross_account_access = True
|
|
||||||
if cross_account_access:
|
|
||||||
report.status = "FAIL"
|
|
||||||
report.status_extended = f"IAM Role {role.name} gives cross account read-only access."
|
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+31
-30
@@ -8,36 +8,37 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_role_cross_service_confused_deputy_prevention(Check):
|
class iam_role_cross_service_confused_deputy_prevention(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
for role in iam_client.roles:
|
if iam_client.roles:
|
||||||
# This check should only be performed against service roles (avoid Service Linked Roles since the trust relationship cannot be changed)
|
for role in iam_client.roles:
|
||||||
if role.is_service_role and "aws-service-role" not in role.arn:
|
# This check should only be performed against service roles (avoid Service Linked Roles since the trust relationship cannot be changed)
|
||||||
report = Check_Report_AWS(self.metadata())
|
if role.is_service_role and "aws-service-role" not in role.arn:
|
||||||
report.region = iam_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_arn = role.arn
|
report.region = iam_client.region
|
||||||
report.resource_id = role.name
|
report.resource_arn = role.arn
|
||||||
report.resource_tags = role.tags
|
report.resource_id = role.name
|
||||||
report.status = "FAIL"
|
report.resource_tags = role.tags
|
||||||
report.status_extended = f"IAM Service Role {role.name} does not prevent against a cross-service confused deputy attack."
|
report.status = "FAIL"
|
||||||
for statement in role.assume_role_policy["Statement"]:
|
report.status_extended = f"IAM Service Role {role.name} does not prevent against a cross-service confused deputy attack."
|
||||||
if (
|
for statement in role.assume_role_policy["Statement"]:
|
||||||
statement["Effect"] == "Allow"
|
if (
|
||||||
and (
|
statement["Effect"] == "Allow"
|
||||||
"sts:AssumeRole" in statement["Action"]
|
and (
|
||||||
or "sts:*" in statement["Action"]
|
"sts:AssumeRole" in statement["Action"]
|
||||||
or "*" in statement["Action"]
|
or "sts:*" in statement["Action"]
|
||||||
)
|
or "*" in statement["Action"]
|
||||||
# Need to make sure we are checking the part of the assume role policy document that provides a service access
|
)
|
||||||
and "Service" in statement["Principal"]
|
# Need to make sure we are checking the part of the assume role policy document that provides a service access
|
||||||
# Check to see if the appropriate condition statements have been implemented
|
and "Service" in statement["Principal"]
|
||||||
and "Condition" in statement
|
# Check to see if the appropriate condition statements have been implemented
|
||||||
and is_condition_block_restrictive(
|
and "Condition" in statement
|
||||||
statement["Condition"], iam_client.audited_account
|
and is_condition_block_restrictive(
|
||||||
)
|
statement["Condition"], iam_client.audited_account
|
||||||
):
|
)
|
||||||
report.status = "PASS"
|
):
|
||||||
report.status_extended = f"IAM Service Role {role.name} prevents against a cross-service confused deputy attack."
|
report.status = "PASS"
|
||||||
break
|
report.status_extended = f"IAM Service Role {role.name} prevents against a cross-service confused deputy attack."
|
||||||
|
break
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+14
-11
@@ -5,15 +5,18 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_securityaudit_role_created(Check):
|
class iam_securityaudit_role_created(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if iam_client.entities_role_attached_to_securityaudit_policy is not None:
|
||||||
report.region = iam_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = "SecurityAudit"
|
report.region = iam_client.region
|
||||||
report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit"
|
report.resource_id = "SecurityAudit"
|
||||||
if iam_client.entities_role_attached_to_securityaudit_policy:
|
report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit"
|
||||||
report.status = "PASS"
|
if iam_client.entities_role_attached_to_securityaudit_policy:
|
||||||
report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}."
|
report.status = "PASS"
|
||||||
else:
|
report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}."
|
||||||
report.status = "FAIL"
|
else:
|
||||||
report.status_extended = "SecurityAudit policy is not attached to any role."
|
report.status = "FAIL"
|
||||||
findings.append(report)
|
report.status_extended = (
|
||||||
|
"SecurityAudit policy is not attached to any role."
|
||||||
|
)
|
||||||
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
@@ -117,6 +117,16 @@ class IAM(AWSService):
|
|||||||
is_service_role=is_service_role(role),
|
is_service_role=is_service_role(role),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "AccessDenied":
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
roles = None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
@@ -219,11 +229,28 @@ class IAM(AWSService):
|
|||||||
|
|
||||||
except ClientError as error:
|
except ClientError as error:
|
||||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||||
# Password policy does not exist
|
# Password policy is the IAM default
|
||||||
stored_password_policy = None
|
stored_password_policy = PasswordPolicy(
|
||||||
|
length=8,
|
||||||
|
symbols=False,
|
||||||
|
numbers=False,
|
||||||
|
uppercase=False,
|
||||||
|
lowercase=False,
|
||||||
|
allow_change=True,
|
||||||
|
expiration=False,
|
||||||
|
max_age=None,
|
||||||
|
reuse_prevention=None,
|
||||||
|
hard_expiry=None,
|
||||||
|
)
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
)
|
)
|
||||||
|
elif error.response["Error"]["Code"] == "AccessDenied":
|
||||||
|
# User does not have permission to get password policy
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
stored_password_policy = None
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
@@ -395,34 +422,35 @@ class IAM(AWSService):
|
|||||||
def __list_attached_role_policies__(self):
|
def __list_attached_role_policies__(self):
|
||||||
logger.info("IAM - List Attached User Policies...")
|
logger.info("IAM - List Attached User Policies...")
|
||||||
try:
|
try:
|
||||||
for role in self.roles:
|
if self.roles:
|
||||||
try:
|
for role in self.roles:
|
||||||
attached_role_policies = []
|
try:
|
||||||
list_attached_role_policies_paginator = self.client.get_paginator(
|
attached_role_policies = []
|
||||||
"list_attached_role_policies"
|
list_attached_role_policies_paginator = (
|
||||||
)
|
self.client.get_paginator("list_attached_role_policies")
|
||||||
for page in list_attached_role_policies_paginator.paginate(
|
|
||||||
RoleName=role.name
|
|
||||||
):
|
|
||||||
for policy in page["AttachedPolicies"]:
|
|
||||||
attached_role_policies.append(policy)
|
|
||||||
|
|
||||||
role.attached_policies = attached_role_policies
|
|
||||||
except ClientError as error:
|
|
||||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
|
||||||
logger.warning(
|
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
||||||
)
|
)
|
||||||
else:
|
for page in list_attached_role_policies_paginator.paginate(
|
||||||
|
RoleName=role.name
|
||||||
|
):
|
||||||
|
for policy in page["AttachedPolicies"]:
|
||||||
|
attached_role_policies.append(policy)
|
||||||
|
|
||||||
|
role.attached_policies = attached_role_policies
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||||
|
logger.warning(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as error:
|
|
||||||
logger.error(
|
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
@@ -548,66 +576,67 @@ class IAM(AWSService):
|
|||||||
|
|
||||||
def __list_inline_role_policies__(self):
|
def __list_inline_role_policies__(self):
|
||||||
logger.info("IAM - List Inline Role Policies...")
|
logger.info("IAM - List Inline Role Policies...")
|
||||||
for role in self.roles:
|
if self.roles:
|
||||||
try:
|
for role in self.roles:
|
||||||
inline_role_policies = []
|
try:
|
||||||
get_role_inline_policies_paginator = self.client.get_paginator(
|
inline_role_policies = []
|
||||||
"list_role_policies"
|
get_role_inline_policies_paginator = self.client.get_paginator(
|
||||||
)
|
"list_role_policies"
|
||||||
for page in get_role_inline_policies_paginator.paginate(
|
)
|
||||||
RoleName=role.name
|
for page in get_role_inline_policies_paginator.paginate(
|
||||||
):
|
RoleName=role.name
|
||||||
for policy in page["PolicyNames"]:
|
):
|
||||||
try:
|
for policy in page["PolicyNames"]:
|
||||||
inline_role_policies.append(policy)
|
try:
|
||||||
# Get inline policies & their policy documents here:
|
inline_role_policies.append(policy)
|
||||||
inline_policy = self.client.get_role_policy(
|
# Get inline policies & their policy documents here:
|
||||||
RoleName=role.name, PolicyName=policy
|
inline_policy = self.client.get_role_policy(
|
||||||
)
|
RoleName=role.name, PolicyName=policy
|
||||||
inline_role_policy_doc = inline_policy["PolicyDocument"]
|
|
||||||
self.policies.append(
|
|
||||||
Policy(
|
|
||||||
name=policy,
|
|
||||||
arn=role.arn,
|
|
||||||
entity=role.name,
|
|
||||||
type="Inline",
|
|
||||||
attached=True,
|
|
||||||
version_id="v1",
|
|
||||||
document=inline_role_policy_doc,
|
|
||||||
)
|
)
|
||||||
)
|
inline_role_policy_doc = inline_policy["PolicyDocument"]
|
||||||
except ClientError as error:
|
self.policies.append(
|
||||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
Policy(
|
||||||
logger.warning(
|
name=policy,
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
arn=role.arn,
|
||||||
|
entity=role.name,
|
||||||
|
type="Inline",
|
||||||
|
attached=True,
|
||||||
|
version_id="v1",
|
||||||
|
document=inline_role_policy_doc,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||||
|
logger.warning(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as error:
|
role.inline_policies = inline_role_policies
|
||||||
logger.error(
|
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
||||||
)
|
|
||||||
|
|
||||||
role.inline_policies = inline_role_policies
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||||
|
logger.warning(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
|
||||||
except ClientError as error:
|
except Exception as error:
|
||||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
|
||||||
logger.warning(
|
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as error:
|
|
||||||
logger.error(
|
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __list_entities_role_for_policy__(self, policy_arn):
|
def __list_entities_role_for_policy__(self, policy_arn):
|
||||||
logger.info("IAM - List Entities Role For Policy...")
|
logger.info("IAM - List Entities Role For Policy...")
|
||||||
try:
|
try:
|
||||||
@@ -615,6 +644,17 @@ class IAM(AWSService):
|
|||||||
roles = self.client.list_entities_for_policy(
|
roles = self.client.list_entities_for_policy(
|
||||||
PolicyArn=policy_arn, EntityFilter="Role"
|
PolicyArn=policy_arn, EntityFilter="Role"
|
||||||
)["PolicyRoles"]
|
)["PolicyRoles"]
|
||||||
|
return roles
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "AccessDenied":
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
roles = None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
@@ -717,21 +757,24 @@ class IAM(AWSService):
|
|||||||
def __list_tags_for_resource__(self):
|
def __list_tags_for_resource__(self):
|
||||||
logger.info("IAM - List Tags...")
|
logger.info("IAM - List Tags...")
|
||||||
try:
|
try:
|
||||||
for role in self.roles:
|
if self.roles:
|
||||||
try:
|
for role in self.roles:
|
||||||
response = self.client.list_role_tags(RoleName=role.name)["Tags"]
|
try:
|
||||||
role.tags = response
|
response = self.client.list_role_tags(RoleName=role.name)[
|
||||||
except ClientError as error:
|
"Tags"
|
||||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
]
|
||||||
role.tags = []
|
role.tags = response
|
||||||
else:
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||||
|
role.tags = []
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
)
|
)
|
||||||
except Exception as error:
|
|
||||||
logger.error(
|
|
||||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|||||||
+14
-13
@@ -5,17 +5,18 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
|||||||
class iam_support_role_created(Check):
|
class iam_support_role_created(Check):
|
||||||
def execute(self) -> Check_Report_AWS:
|
def execute(self) -> Check_Report_AWS:
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if iam_client.entities_role_attached_to_support_policy is not None:
|
||||||
report.region = iam_client.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = "AWSSupportServiceRolePolicy"
|
report.region = iam_client.region
|
||||||
report.resource_arn = (
|
report.resource_id = "AWSSupportServiceRolePolicy"
|
||||||
"arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
report.resource_arn = (
|
||||||
)
|
"arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||||
if iam_client.entities_role_attached_to_support_policy:
|
)
|
||||||
report.status = "PASS"
|
if iam_client.entities_role_attached_to_support_policy:
|
||||||
report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}."
|
report.status = "PASS"
|
||||||
else:
|
report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}."
|
||||||
report.status = "FAIL"
|
else:
|
||||||
report.status_extended = "Support policy is not attached to any role."
|
report.status = "FAIL"
|
||||||
findings.append(report)
|
report.status_extended = "Support policy is not attached to any role."
|
||||||
|
findings.append(report)
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+14
-13
@@ -7,18 +7,19 @@ from prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_client i
|
|||||||
class resourceexplorer2_indexes_found(Check):
|
class resourceexplorer2_indexes_found(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if resource_explorer_2_client.indexes is not None:
|
||||||
report.status = "FAIL"
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status_extended = "No Resource Explorer Indexes found."
|
report.status = "FAIL"
|
||||||
report.region = resource_explorer_2_client.region
|
report.status_extended = "No Resource Explorer Indexes found."
|
||||||
report.resource_arn = "NoResourceExplorer"
|
report.region = resource_explorer_2_client.region
|
||||||
report.resource_id = resource_explorer_2_client.audited_account
|
report.resource_arn = "NoResourceExplorer"
|
||||||
report.resource_arn = resource_explorer_2_client.index_arn_template
|
report.resource_id = resource_explorer_2_client.audited_account
|
||||||
if resource_explorer_2_client.indexes:
|
report.resource_arn = resource_explorer_2_client.index_arn_template
|
||||||
report.region = resource_explorer_2_client.indexes[0].region
|
if resource_explorer_2_client.indexes:
|
||||||
report.resource_arn = resource_explorer_2_client.indexes[0].arn
|
report.region = resource_explorer_2_client.indexes[0].region
|
||||||
report.status = "PASS"
|
report.resource_arn = resource_explorer_2_client.indexes[0].arn
|
||||||
report.status_extended = f"Resource Explorer Indexes found: {len(resource_explorer_2_client.indexes)}."
|
report.status = "PASS"
|
||||||
findings.append(report)
|
report.status_extended = f"Resource Explorer Indexes found: {len(resource_explorer_2_client.indexes)}."
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from botocore.client import ClientError
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from prowler.lib.logger import logger
|
from prowler.lib.logger import logger
|
||||||
@@ -23,6 +24,8 @@ class ResourceExplorer2(AWSService):
|
|||||||
if not self.audit_resources or (
|
if not self.audit_resources or (
|
||||||
is_resource_filtered(index["Arn"], self.audit_resources)
|
is_resource_filtered(index["Arn"], self.audit_resources)
|
||||||
):
|
):
|
||||||
|
if self.indexes is None:
|
||||||
|
self.indexes = []
|
||||||
self.indexes.append(
|
self.indexes.append(
|
||||||
Indexes(
|
Indexes(
|
||||||
arn=index["Arn"],
|
arn=index["Arn"],
|
||||||
@@ -30,6 +33,17 @@ class ResourceExplorer2(AWSService):
|
|||||||
type=index["Type"],
|
type=index["Type"],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
if not self.indexes:
|
||||||
|
self.indexes = None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
|||||||
+17
-16
@@ -7,22 +7,23 @@ from prowler.providers.aws.services.ssmincidents.ssmincidents_client import (
|
|||||||
class ssmincidents_enabled_with_plans(Check):
|
class ssmincidents_enabled_with_plans(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
report = Check_Report_AWS(self.metadata())
|
if ssmincidents_client.replication_set is not None:
|
||||||
report.status = "FAIL"
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status_extended = "No SSM Incidents replication set exists."
|
report.status = "FAIL"
|
||||||
report.resource_arn = ssmincidents_client.replication_set_arn_template
|
report.status_extended = "No SSM Incidents replication set exists."
|
||||||
report.resource_id = ssmincidents_client.audited_account
|
report.resource_arn = ssmincidents_client.replication_set_arn_template
|
||||||
report.region = ssmincidents_client.region
|
report.resource_id = ssmincidents_client.audited_account
|
||||||
if ssmincidents_client.replication_set:
|
report.region = ssmincidents_client.region
|
||||||
report.resource_arn = ssmincidents_client.replication_set[0].arn
|
if ssmincidents_client.replication_set:
|
||||||
report.resource_tags = [] # Not supported for replication sets
|
report.resource_arn = ssmincidents_client.replication_set[0].arn
|
||||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} exists but not ACTIVE."
|
report.resource_tags = [] # Not supported for replication sets
|
||||||
if ssmincidents_client.replication_set[0].status == "ACTIVE":
|
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} exists but not ACTIVE."
|
||||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE but no response plans exist."
|
if ssmincidents_client.replication_set[0].status == "ACTIVE":
|
||||||
if ssmincidents_client.response_plans:
|
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE but no response plans exist."
|
||||||
report.status = "PASS"
|
if ssmincidents_client.response_plans:
|
||||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE and has response plans."
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE and has response plans."
|
||||||
|
|
||||||
findings.append(report)
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
@@ -45,6 +45,17 @@ class SSMIncidents(AWSService):
|
|||||||
arn=replication_set,
|
arn=replication_set,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
except ClientError as error:
|
||||||
|
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
if not self.replication_set:
|
||||||
|
self.replication_set = None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
|
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
|
||||||
|
|||||||
+24
-23
@@ -7,28 +7,29 @@ from prowler.providers.aws.services.trustedadvisor.trustedadvisor_client import
|
|||||||
class trustedadvisor_errors_and_warnings(Check):
|
class trustedadvisor_errors_and_warnings(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
if trustedadvisor_client.premium_support.enabled:
|
if trustedadvisor_client.premium_support:
|
||||||
if trustedadvisor_client.checks:
|
if trustedadvisor_client.premium_support.enabled:
|
||||||
for check in trustedadvisor_client.checks:
|
if trustedadvisor_client.checks:
|
||||||
if (
|
for check in trustedadvisor_client.checks:
|
||||||
check.status != "not_available"
|
if (
|
||||||
): # avoid not_available checks since there are no resources that apply
|
check.status != "not_available"
|
||||||
report = Check_Report_AWS(self.metadata())
|
): # avoid not_available checks since there are no resources that apply
|
||||||
report.region = check.region
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.resource_id = check.id
|
report.region = check.region
|
||||||
report.resource_arn = check.arn
|
report.resource_id = check.id
|
||||||
report.status = "FAIL"
|
report.resource_arn = check.arn
|
||||||
report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}."
|
report.status = "FAIL"
|
||||||
if check.status == "ok":
|
report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}."
|
||||||
report.status = "PASS"
|
if check.status == "ok":
|
||||||
findings.append(report)
|
report.status = "PASS"
|
||||||
else:
|
findings.append(report)
|
||||||
report = Check_Report_AWS(self.metadata())
|
else:
|
||||||
report.status = "MANUAL"
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service."
|
report.status = "MANUAL"
|
||||||
report.resource_id = trustedadvisor_client.audited_account
|
report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service."
|
||||||
report.resource_arn = trustedadvisor_client.account_arn_template
|
report.resource_id = trustedadvisor_client.audited_account
|
||||||
report.region = trustedadvisor_client.region
|
report.resource_arn = trustedadvisor_client.account_arn_template
|
||||||
findings.append(report)
|
report.region = trustedadvisor_client.region
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|||||||
+6
-1
@@ -7,7 +7,12 @@ from prowler.providers.aws.services.trustedadvisor.trustedadvisor_client import
|
|||||||
class trustedadvisor_premium_support_plan_subscribed(Check):
|
class trustedadvisor_premium_support_plan_subscribed(Check):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
findings = []
|
findings = []
|
||||||
if trustedadvisor_client.audit_config.get("verify_premium_support_plans", True):
|
if (
|
||||||
|
trustedadvisor_client.premium_support
|
||||||
|
and trustedadvisor_client.audit_config.get(
|
||||||
|
"verify_premium_support_plans", True
|
||||||
|
)
|
||||||
|
):
|
||||||
report = Check_Report_AWS(self.metadata())
|
report = Check_Report_AWS(self.metadata())
|
||||||
report.status = "FAIL"
|
report.status = "FAIL"
|
||||||
report.status_extended = (
|
report.status_extended = (
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class TrustedAdvisor(AWSService):
|
|||||||
support_region = "us-east-1"
|
support_region = "us-east-1"
|
||||||
else:
|
else:
|
||||||
support_region = "us-gov-west-1"
|
support_region = "us-gov-west-1"
|
||||||
|
|
||||||
self.client = self.session.client(self.service, region_name=support_region)
|
self.client = self.session.client(self.service, region_name=support_region)
|
||||||
self.client.region = support_region
|
self.client.region = support_region
|
||||||
self.__describe_services__()
|
self.__describe_services__()
|
||||||
@@ -106,6 +105,19 @@ class TrustedAdvisor(AWSService):
|
|||||||
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||||
f" {error}"
|
f" {error}"
|
||||||
)
|
)
|
||||||
|
elif error.response["Error"]["Code"] == "AccessDeniedException":
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} --"
|
||||||
|
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||||
|
f" {error}"
|
||||||
|
)
|
||||||
|
self.premium_support = None
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} --"
|
||||||
|
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||||
|
f" {error}"
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|||||||
+26
@@ -101,3 +101,29 @@ class Test_account_maintain_different_contact_details_to_security_billing_and_op
|
|||||||
assert result[0].region == AWS_REGION
|
assert result[0].region == AWS_REGION
|
||||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||||
assert result[0].resource_arn == AWS_ACCOUNT_ARN
|
assert result[0].resource_arn == AWS_ACCOUNT_ARN
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
account_client = mock.MagicMock
|
||||||
|
account_client.region = AWS_REGION
|
||||||
|
account_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||||
|
account_client.audited_account_arn = AWS_ACCOUNT_ARN
|
||||||
|
account_client.contact_base = None
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.account.account_service.Account",
|
||||||
|
new=account_client,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.account.account_client.account_client",
|
||||||
|
new=account_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.account.account_maintain_different_contact_details_to_security_billing_and_operations.account_maintain_different_contact_details_to_security_billing_and_operations import (
|
||||||
|
account_maintain_different_contact_details_to_security_billing_and_operations,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = (
|
||||||
|
account_maintain_different_contact_details_to_security_billing_and_operations()
|
||||||
|
)
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
@@ -85,3 +85,28 @@ class Test_backup_vaults_exist:
|
|||||||
assert result[0].resource_id == "MyBackupVault"
|
assert result[0].resource_id == "MyBackupVault"
|
||||||
assert result[0].resource_arn == backup_vault_arn
|
assert result[0].resource_arn == backup_vault_arn
|
||||||
assert result[0].region == AWS_REGION
|
assert result[0].region == AWS_REGION
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
backup_client = mock.MagicMock
|
||||||
|
backup_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||||
|
backup_client.audited_account_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||||
|
backup_client.region = AWS_REGION
|
||||||
|
backup_client.audited_partition = "aws"
|
||||||
|
backup_client.backup_vault_arn_template = f"arn:{backup_client.audited_partition}:backup:{backup_client.region}:{backup_client.audited_account}:backup-vault"
|
||||||
|
backup_client.__get_backup_vault_arn_template__ = mock.MagicMock(
|
||||||
|
return_value=backup_client.backup_vault_arn_template
|
||||||
|
)
|
||||||
|
backup_client.backup_vaults = None
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.backup.backup_service.Backup",
|
||||||
|
new=backup_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.backup.backup_vaults_exist.backup_vaults_exist import (
|
||||||
|
backup_vaults_exist,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = backup_vaults_exist()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+32
@@ -254,3 +254,35 @@ class Test_cloudtrail_bucket_requires_mfa_delete:
|
|||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||||
assert result[0].resource_tags == []
|
assert result[0].resource_tags == []
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
@patch(
|
||||||
|
"botocore.client.BaseClient._make_api_call",
|
||||||
|
new=mock_make_api_call_getbucketversioning_mfadelete_enabled,
|
||||||
|
)
|
||||||
|
def test_access_denied(self):
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete.cloudtrail_client",
|
||||||
|
new=Cloudtrail(aws_provider),
|
||||||
|
) as cloudtrail_client, mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete.s3_client",
|
||||||
|
new=S3(aws_provider),
|
||||||
|
) as s3_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete import (
|
||||||
|
cloudtrail_bucket_requires_mfa_delete,
|
||||||
|
)
|
||||||
|
|
||||||
|
cloudtrail_client.trails = None
|
||||||
|
s3_client.buckets = []
|
||||||
|
|
||||||
|
check = cloudtrail_bucket_requires_mfa_delete()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+31
@@ -303,3 +303,34 @@ class Test_cloudtrail_cloudwatch_logging_enabled:
|
|||||||
== f"Single region trail {trail_name_eu} is not logging in the last 24h or not configured to deliver logs."
|
== f"Single region trail {trail_name_eu} is not logging in the last 24h or not configured to deliver logs."
|
||||||
)
|
)
|
||||||
assert report.resource_tags == []
|
assert report.resource_tags == []
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
),
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(
|
||||||
|
set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
) as service_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled import (
|
||||||
|
cloudtrail_cloudwatch_logging_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.trails = None
|
||||||
|
|
||||||
|
check = cloudtrail_cloudwatch_logging_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+23
@@ -128,3 +128,26 @@ class Test_cloudtrail_insights_exist:
|
|||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||||
assert result[0].resource_tags == []
|
assert result[0].resource_tags == []
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
)
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_insights_exist.cloudtrail_insights_exist.cloudtrail_client",
|
||||||
|
new=Cloudtrail(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_insights_exist.cloudtrail_insights_exist import (
|
||||||
|
cloudtrail_insights_exist,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.trails = None
|
||||||
|
check = cloudtrail_insights_exist()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+29
@@ -137,3 +137,32 @@ class Test_cloudtrail_kms_encryption_enabled:
|
|||||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||||
assert result[0].resource_tags == []
|
assert result[0].resource_tags == []
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
),
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(
|
||||||
|
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||||
|
),
|
||||||
|
) as service_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled import (
|
||||||
|
cloudtrail_kms_encryption_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.trails = None
|
||||||
|
check = cloudtrail_kms_encryption_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+28
@@ -155,3 +155,31 @@ class Test_cloudtrail_log_file_validation_enabled:
|
|||||||
assert report.resource_arn == trail_eu["TrailARN"]
|
assert report.resource_arn == trail_eu["TrailARN"]
|
||||||
assert report.resource_tags == []
|
assert report.resource_tags == []
|
||||||
assert report.region == AWS_REGION_EU_WEST_1
|
assert report.region == AWS_REGION_EU_WEST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
),
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(
|
||||||
|
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||||
|
),
|
||||||
|
) as service_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled import (
|
||||||
|
cloudtrail_log_file_validation_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.trails = None
|
||||||
|
|
||||||
|
check = cloudtrail_log_file_validation_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+37
@@ -226,3 +226,40 @@ class Test_cloudtrail_logs_s3_bucket_access_logging_enabled:
|
|||||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||||
assert result[0].resource_tags == []
|
assert result[0].resource_tags == []
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
from prowler.providers.aws.services.s3.s3_service import S3
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
),
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(
|
||||||
|
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||||
|
),
|
||||||
|
) as cloudtrail_client, mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.s3_client",
|
||||||
|
new=S3(
|
||||||
|
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||||
|
),
|
||||||
|
) as s3_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled import (
|
||||||
|
cloudtrail_logs_s3_bucket_access_logging_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
cloudtrail_client.trails = None
|
||||||
|
s3_client.buckets = []
|
||||||
|
|
||||||
|
check = cloudtrail_logs_s3_bucket_access_logging_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+37
@@ -289,3 +289,40 @@ class Test_cloudtrail_logs_s3_bucket_is_not_publicly_accessible:
|
|||||||
)
|
)
|
||||||
assert result[0].resource_tags == []
|
assert result[0].resource_tags == []
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
from prowler.providers.aws.services.s3.s3_service import S3
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
),
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_client",
|
||||||
|
new=Cloudtrail(
|
||||||
|
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||||
|
),
|
||||||
|
) as cloudtrail_client, mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.s3_client",
|
||||||
|
new=S3(
|
||||||
|
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||||
|
),
|
||||||
|
) as s3_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible import (
|
||||||
|
cloudtrail_logs_s3_bucket_is_not_publicly_accessible,
|
||||||
|
)
|
||||||
|
|
||||||
|
cloudtrail_client.trails = None
|
||||||
|
s3_client.buckets = []
|
||||||
|
|
||||||
|
check = cloudtrail_logs_s3_bucket_is_not_publicly_accessible()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+28
@@ -289,3 +289,31 @@ class Test_cloudtrail_multi_region_enabled:
|
|||||||
assert report.resource_id == trail_name_us
|
assert report.resource_id == trail_name_us
|
||||||
assert report.resource_arn == trail_us["TrailARN"]
|
assert report.resource_arn == trail_us["TrailARN"]
|
||||||
assert report.resource_tags == []
|
assert report.resource_tags == []
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled import (
|
||||||
|
cloudtrail_multi_region_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.trails = None
|
||||||
|
check = cloudtrail_multi_region_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+26
@@ -273,3 +273,29 @@ class Test_cloudtrail_multi_region_enabled_logging_management_events:
|
|||||||
result[0].status_extended
|
result[0].status_extended
|
||||||
== "No trail found with multi-region enabled and logging management events."
|
== "No trail found with multi-region enabled and logging management events."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_client",
|
||||||
|
new=Cloudtrail(aws_provider),
|
||||||
|
) as cloudtrail_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_multi_region_enabled_logging_management_events import (
|
||||||
|
cloudtrail_multi_region_enabled_logging_management_events,
|
||||||
|
)
|
||||||
|
|
||||||
|
cloudtrail_client.trails = None
|
||||||
|
check = cloudtrail_multi_region_enabled_logging_management_events()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+30
@@ -391,3 +391,33 @@ class Test_cloudtrail_s3_dataevents_read_enabled:
|
|||||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||||
assert result[0].resource_tags == []
|
assert result[0].resource_tags == []
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
from prowler.providers.aws.services.s3.s3_service import S3
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider()
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(aws_provider),
|
||||||
|
) as cloudtrail_client, mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled.s3_client",
|
||||||
|
new=S3(aws_provider),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled import (
|
||||||
|
cloudtrail_s3_dataevents_read_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
cloudtrail_client.trails = None
|
||||||
|
check = cloudtrail_s3_dataevents_read_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+30
@@ -389,3 +389,33 @@ class Test_cloudtrail_s3_dataevents_write_enabled:
|
|||||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||||
assert result[0].resource_tags == []
|
assert result[0].resource_tags == []
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
from prowler.providers.aws.services.s3.s3_service import S3
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider()
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(aws_provider),
|
||||||
|
) as cloudtrail_service, mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled.s3_client",
|
||||||
|
new=S3(aws_provider),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled import (
|
||||||
|
cloudtrail_s3_dataevents_write_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
cloudtrail_service.trails = None
|
||||||
|
check = cloudtrail_s3_dataevents_write_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+48
@@ -561,3 +561,51 @@ class Test_cloudwatch_changes_to_network_acls_alarm_configured:
|
|||||||
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:metric-filter/test-filter"
|
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:metric-filter/test-filter"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||||
|
CloudWatch,
|
||||||
|
Logs,
|
||||||
|
)
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
from prowler.providers.common.models import Audit_Metadata
|
||||||
|
|
||||||
|
aws_provider.audit_metadata = Audit_Metadata(
|
||||||
|
services_scanned=0,
|
||||||
|
# We need to set this check to call __describe_log_groups__
|
||||||
|
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||||
|
completed_checks=0,
|
||||||
|
audit_progress=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.logs_client",
|
||||||
|
new=Logs(aws_provider),
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_client",
|
||||||
|
new=CloudWatch(aws_provider),
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudtrail_client",
|
||||||
|
new=Cloudtrail(aws_provider),
|
||||||
|
) as cloudtrail_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured import (
|
||||||
|
cloudwatch_changes_to_network_acls_alarm_configured,
|
||||||
|
)
|
||||||
|
|
||||||
|
cloudtrail_client.trails = None
|
||||||
|
check = cloudwatch_changes_to_network_acls_alarm_configured()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+36
@@ -99,3 +99,39 @@ class Test_cloudwatch_cross_account_sharing_disabled:
|
|||||||
== "CloudWatch has allowed cross-account sharing."
|
== "CloudWatch has allowed cross-account sharing."
|
||||||
)
|
)
|
||||||
assert result[0].resource_id == "CloudWatch-CrossAccountSharingRole"
|
assert result[0].resource_id == "CloudWatch-CrossAccountSharingRole"
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
from prowler.providers.common.models import Audit_Metadata
|
||||||
|
|
||||||
|
aws_provider.audit_metadata = Audit_Metadata(
|
||||||
|
services_scanned=0,
|
||||||
|
# We need to set this check to call __describe_log_groups__
|
||||||
|
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||||
|
completed_checks=0,
|
||||||
|
audit_progress=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as iam_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled import (
|
||||||
|
cloudwatch_cross_account_sharing_disabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
iam_client.roles = None
|
||||||
|
check = cloudwatch_cross_account_sharing_disabled()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+36
@@ -138,3 +138,39 @@ class Test_cloudwatch_log_group_kms_encryption_enabled:
|
|||||||
== "Log Group test does have AWS KMS key test_kms_id associated."
|
== "Log Group test does have AWS KMS key test_kms_id associated."
|
||||||
)
|
)
|
||||||
assert result[0].resource_id == "test"
|
assert result[0].resource_id == "test"
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
from prowler.providers.common.models import Audit_Metadata
|
||||||
|
|
||||||
|
aws_provider.audit_metadata = Audit_Metadata(
|
||||||
|
services_scanned=0,
|
||||||
|
# We need to set this check to call __describe_log_groups__
|
||||||
|
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||||
|
completed_checks=0,
|
||||||
|
audit_progress=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled.logs_client",
|
||||||
|
new=Logs(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled import (
|
||||||
|
cloudwatch_log_group_kms_encryption_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.log_groups = None
|
||||||
|
check = cloudwatch_log_group_kms_encryption_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+36
@@ -154,3 +154,39 @@ class Test_cloudwatch_log_group_no_secrets_in_logs:
|
|||||||
"Potential secrets found in log group", result[0].status_extended
|
"Potential secrets found in log group", result[0].status_extended
|
||||||
)
|
)
|
||||||
assert result[0].resource_id == "test"
|
assert result[0].resource_id == "test"
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
from prowler.providers.common.models import Audit_Metadata
|
||||||
|
|
||||||
|
aws_provider.audit_metadata = Audit_Metadata(
|
||||||
|
services_scanned=0,
|
||||||
|
# We need to set this check to call __describe_log_groups__
|
||||||
|
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||||
|
completed_checks=0,
|
||||||
|
audit_progress=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_secrets_in_logs.cloudwatch_log_group_no_secrets_in_logs.logs_client",
|
||||||
|
new=Logs(aws_provider),
|
||||||
|
) as logs_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_secrets_in_logs.cloudwatch_log_group_no_secrets_in_logs import (
|
||||||
|
cloudwatch_log_group_no_secrets_in_logs,
|
||||||
|
)
|
||||||
|
|
||||||
|
logs_client.log_groups = None
|
||||||
|
check = cloudwatch_log_group_no_secrets_in_logs()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+44
@@ -207,3 +207,47 @@ class Test_cloudwatch_log_group_retention_policy_specific_days_enabled:
|
|||||||
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:test"
|
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:test"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
# Generate Logs Client
|
||||||
|
logs_client = client("logs", region_name=AWS_REGION_US_EAST_1)
|
||||||
|
# Request Logs group
|
||||||
|
logs_client.create_log_group(
|
||||||
|
logGroupName="test",
|
||||||
|
)
|
||||||
|
logs_client.put_retention_policy(logGroupName="test", retentionInDays=7)
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||||
|
)
|
||||||
|
aws_provider._audit_config = {"log_group_retention_days": 365}
|
||||||
|
|
||||||
|
from prowler.providers.common.models import Audit_Metadata
|
||||||
|
|
||||||
|
aws_provider.audit_metadata = Audit_Metadata(
|
||||||
|
services_scanned=0,
|
||||||
|
# We need to set this check to call __describe_log_groups__
|
||||||
|
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||||
|
completed_checks=0,
|
||||||
|
audit_progress=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled.logs_client",
|
||||||
|
new=Logs(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled import (
|
||||||
|
cloudwatch_log_group_retention_policy_specific_days_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.log_groups = None
|
||||||
|
check = cloudwatch_log_group_retention_policy_specific_days_enabled()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+50
@@ -575,3 +575,53 @@ class Test_cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_c
|
|||||||
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:metric-filter/test-filter"
|
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:metric-filter/test-filter"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||||
|
Cloudtrail,
|
||||||
|
)
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||||
|
CloudWatch,
|
||||||
|
Logs,
|
||||||
|
)
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider(
|
||||||
|
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||||
|
)
|
||||||
|
|
||||||
|
from prowler.providers.common.models import Audit_Metadata
|
||||||
|
|
||||||
|
aws_provider.audit_metadata = Audit_Metadata(
|
||||||
|
services_scanned=0,
|
||||||
|
# We need to set this check to call __describe_log_groups__
|
||||||
|
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||||
|
completed_checks=0,
|
||||||
|
audit_progress=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.logs_client",
|
||||||
|
new=Logs(aws_provider),
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_client",
|
||||||
|
new=CloudWatch(aws_provider),
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(aws_provider),
|
||||||
|
) as cloudtrail_client:
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled import (
|
||||||
|
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = (
|
||||||
|
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled()
|
||||||
|
)
|
||||||
|
cloudtrail_client.trails = None
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
@@ -26,6 +26,24 @@ class Test_fms_policy_compliant:
|
|||||||
|
|
||||||
assert len(result) == 0
|
assert len(result) == 0
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
fms_client = mock.MagicMock
|
||||||
|
fms_client.region = AWS_REGION_US_EAST_1
|
||||||
|
fms_client.fms_admin_account = None
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.fms.fms_service.FMS",
|
||||||
|
new=fms_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.fms.fms_policy_compliant.fms_policy_compliant import (
|
||||||
|
fms_policy_compliant,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = fms_policy_compliant()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
def test_fms_admin_with_non_compliant_policies(self):
|
def test_fms_admin_with_non_compliant_policies(self):
|
||||||
fms_client = mock.MagicMock
|
fms_client = mock.MagicMock
|
||||||
fms_client.audited_account = AWS_ACCOUNT_NUMBER
|
fms_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||||
|
|||||||
+22
@@ -145,3 +145,25 @@ class Test_iam_password_policy_expires_passwords_within_90_days_or_less:
|
|||||||
"Password expiration is set lower than 90 days",
|
"Password expiration is set lower than 90 days",
|
||||||
result[0].status_extended,
|
result[0].status_extended,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less import (
|
||||||
|
iam_password_policy_expires_passwords_within_90_days_or_less,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.password_policy = None
|
||||||
|
check = iam_password_policy_expires_passwords_within_90_days_or_less()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+22
@@ -85,3 +85,25 @@ class Test_iam_password_policy_lowercase:
|
|||||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_password_policy_lowercase.iam_password_policy_lowercase.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_password_policy_lowercase.iam_password_policy_lowercase import (
|
||||||
|
iam_password_policy_lowercase,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.password_policy = None
|
||||||
|
check = iam_password_policy_lowercase()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+22
@@ -129,3 +129,25 @@ class Test_iam_password_policy_minimum_length_14:
|
|||||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_password_policy_minimum_length_14.iam_password_policy_minimum_length_14.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_password_policy_minimum_length_14.iam_password_policy_minimum_length_14 import (
|
||||||
|
iam_password_policy_minimum_length_14,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.password_policy = None
|
||||||
|
check = iam_password_policy_minimum_length_14()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+22
@@ -92,3 +92,25 @@ class Test_iam_password_policy_number:
|
|||||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_password_policy_number.iam_password_policy_number.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_password_policy_number.iam_password_policy_number import (
|
||||||
|
iam_password_policy_number,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.password_policy = None
|
||||||
|
check = iam_password_policy_number()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+22
@@ -89,3 +89,25 @@ class Test_iam_password_policy_reuse_24:
|
|||||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_password_policy_reuse_24.iam_password_policy_reuse_24.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_password_policy_reuse_24.iam_password_policy_reuse_24 import (
|
||||||
|
iam_password_policy_reuse_24,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.password_policy = None
|
||||||
|
check = iam_password_policy_reuse_24()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+22
@@ -92,3 +92,25 @@ class Test_iam_password_policy_symbol:
|
|||||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_password_policy_symbol.iam_password_policy_symbol.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_password_policy_symbol.iam_password_policy_symbol import (
|
||||||
|
iam_password_policy_symbol,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.password_policy = None
|
||||||
|
check = iam_password_policy_symbol()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+22
@@ -89,3 +89,25 @@ class Test_iam_password_policy_uppercase:
|
|||||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||||
)
|
)
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
):
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_password_policy_uppercase.iam_password_policy_uppercase.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_password_policy_uppercase.iam_password_policy_uppercase import (
|
||||||
|
iam_password_policy_uppercase,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.password_policy = None
|
||||||
|
check = iam_password_policy_uppercase()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+23
@@ -264,3 +264,26 @@ class Test_iam_role_administratoraccess_policy:
|
|||||||
check = iam_role_administratoraccess_policy()
|
check = iam_role_administratoraccess_policy()
|
||||||
result = check.execute()
|
result = check.execute()
|
||||||
assert len(result) == 0
|
assert len(result) == 0
|
||||||
|
|
||||||
|
@mock_aws(config={"iam": {"load_aws_managed_policies": True}})
|
||||||
|
def test_access_denied(self):
|
||||||
|
iam_client = mock.MagicMock
|
||||||
|
iam_client.roles = None
|
||||||
|
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client",
|
||||||
|
new=iam_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import (
|
||||||
|
iam_role_administratoraccess_policy,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = iam_role_administratoraccess_policy()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+22
@@ -85,3 +85,25 @@ class Test_iam_securityaudit_role_created:
|
|||||||
assert result[0].resource_id == "SecurityAudit"
|
assert result[0].resource_id == "SecurityAudit"
|
||||||
assert result[0].resource_arn == "arn:aws:iam::aws:policy/SecurityAudit"
|
assert result[0].resource_arn == "arn:aws:iam::aws:policy/SecurityAudit"
|
||||||
assert result[0].region == "us-east-1"
|
assert result[0].region == "us-east-1"
|
||||||
|
|
||||||
|
@mock_aws(config={"iam": {"load_aws_managed_policies": True}})
|
||||||
|
def test_access_denied(self):
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created import (
|
||||||
|
iam_securityaudit_role_created,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.entities_role_attached_to_securityaudit_policy = None
|
||||||
|
|
||||||
|
check = iam_securityaudit_role_created()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+22
@@ -95,3 +95,25 @@ class Test_iam_support_role_created:
|
|||||||
result[0].resource_arn
|
result[0].resource_arn
|
||||||
== "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
== "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@mock_aws(config={"iam": {"load_aws_managed_policies": True}})
|
||||||
|
def test_access_denied(self):
|
||||||
|
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||||
|
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.common.common.get_global_provider",
|
||||||
|
return_value=aws_provider,
|
||||||
|
), mock.patch(
|
||||||
|
"prowler.providers.aws.services.iam.iam_support_role_created.iam_support_role_created.iam_client",
|
||||||
|
new=IAM(aws_provider),
|
||||||
|
) as service_client:
|
||||||
|
from prowler.providers.aws.services.iam.iam_support_role_created.iam_support_role_created import (
|
||||||
|
iam_support_role_created,
|
||||||
|
)
|
||||||
|
|
||||||
|
service_client.entities_role_attached_to_support_policy = None
|
||||||
|
|
||||||
|
check = iam_support_role_created()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+28
@@ -80,3 +80,31 @@ class Test_resourceexplorer2_indexes_found:
|
|||||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||||
assert result[0].resource_arn == INDEX_ARN
|
assert result[0].resource_arn == INDEX_ARN
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
resourceexplorer2_client = mock.MagicMock
|
||||||
|
resourceexplorer2_client.indexes = None
|
||||||
|
resourceexplorer2_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||||
|
resourceexplorer2_client.audited_account_arn = (
|
||||||
|
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||||
|
)
|
||||||
|
resourceexplorer2_client.audited_partition = "aws"
|
||||||
|
resourceexplorer2_client.region = AWS_REGION_US_EAST_1
|
||||||
|
resourceexplorer2_client.index_arn_template = f"arn:{resourceexplorer2_client.audited_partition}:resource-explorer:{resourceexplorer2_client.region}:{resourceexplorer2_client.audited_account}:index"
|
||||||
|
resourceexplorer2_client.__get_index_arn_template__ = mock.MagicMock(
|
||||||
|
return_value=resourceexplorer2_client.index_arn_template
|
||||||
|
)
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_service.ResourceExplorer2",
|
||||||
|
new=resourceexplorer2_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_indexes_found.resourceexplorer2_indexes_found import (
|
||||||
|
resourceexplorer2_indexes_found,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = resourceexplorer2_indexes_found()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
# Assertions
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+27
@@ -164,3 +164,30 @@ class Test_ssmincidents_enabled_with_plans:
|
|||||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||||
assert result[0].resource_arn == REPLICATION_SET_ARN
|
assert result[0].resource_arn == REPLICATION_SET_ARN
|
||||||
assert result[0].region == AWS_REGION_US_EAST_1
|
assert result[0].region == AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
ssmincidents_client = mock.MagicMock
|
||||||
|
ssmincidents_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||||
|
ssmincidents_client.audited_partition = "aws"
|
||||||
|
ssmincidents_client.audited_account_arn = (
|
||||||
|
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||||
|
)
|
||||||
|
ssmincidents_client.region = AWS_REGION_US_EAST_1
|
||||||
|
ssmincidents_client.replication_set_arn_template = f"arn:{ssmincidents_client.audited_partition}:ssm-incidents:{ssmincidents_client.region}:{ssmincidents_client.audited_account}:replication-set"
|
||||||
|
ssmincidents_client.__get_replication_set_arn_template__ = mock.MagicMock(
|
||||||
|
return_value=ssmincidents_client.replication_set_arn_template
|
||||||
|
)
|
||||||
|
ssmincidents_client.replication_set = None
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.ssmincidents.ssmincidents_service.SSMIncidents",
|
||||||
|
new=ssmincidents_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from prowler.providers.aws.services.ssmincidents.ssmincidents_enabled_with_plans.ssmincidents_enabled_with_plans import (
|
||||||
|
ssmincidents_enabled_with_plans,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = ssmincidents_enabled_with_plans()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+27
@@ -144,3 +144,30 @@ class Test_trustedadvisor_errors_and_warnings:
|
|||||||
check = trustedadvisor_errors_and_warnings()
|
check = trustedadvisor_errors_and_warnings()
|
||||||
result = check.execute()
|
result = check.execute()
|
||||||
assert len(result) == 0
|
assert len(result) == 0
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
trustedadvisor_client = mock.MagicMock
|
||||||
|
trustedadvisor_client.checks = []
|
||||||
|
trustedadvisor_client.premium_support = None
|
||||||
|
trustedadvisor_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||||
|
trustedadvisor_client.audited_account_arn = AWS_ACCOUNT_ARN
|
||||||
|
trustedadvisor_client.checks.append(
|
||||||
|
Check(
|
||||||
|
id=CHECK_NAME,
|
||||||
|
name=CHECK_NAME,
|
||||||
|
arn=CHECK_ARN,
|
||||||
|
region=AWS_REGION_US_EAST_1,
|
||||||
|
status="not_available",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.TrustedAdvisor",
|
||||||
|
trustedadvisor_client,
|
||||||
|
):
|
||||||
|
from prowler.providers.aws.services.trustedadvisor.trustedadvisor_errors_and_warnings.trustedadvisor_errors_and_warnings import (
|
||||||
|
trustedadvisor_errors_and_warnings,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = trustedadvisor_errors_and_warnings()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
+27
@@ -86,3 +86,30 @@ class Test_trustedadvisor_premium_support_plan_subscribed:
|
|||||||
result[0].resource_arn
|
result[0].resource_arn
|
||||||
== f"arn:aws:trusted-advisor:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account"
|
== f"arn:aws:trusted-advisor:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_access_denied(self):
|
||||||
|
trustedadvisor_client = mock.MagicMock
|
||||||
|
trustedadvisor_client.checks = []
|
||||||
|
trustedadvisor_client.premium_support = None
|
||||||
|
trustedadvisor_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||||
|
trustedadvisor_client.audited_account_arn = AWS_ACCOUNT_ARN
|
||||||
|
trustedadvisor_client.audited_partition = "aws"
|
||||||
|
trustedadvisor_client.region = AWS_REGION_US_EAST_1
|
||||||
|
|
||||||
|
# Set verify_premium_support_plans config
|
||||||
|
trustedadvisor_client.audit_config = {"verify_premium_support_plans": True}
|
||||||
|
trustedadvisor_client.account_arn_template = f"arn:{trustedadvisor_client.audited_partition}:trusted-advisor:{trustedadvisor_client.region}:{trustedadvisor_client.audited_account}:account"
|
||||||
|
trustedadvisor_client.__get_account_arn_template__ = mock.MagicMock(
|
||||||
|
return_value=trustedadvisor_client.account_arn_template
|
||||||
|
)
|
||||||
|
with mock.patch(
|
||||||
|
"prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.TrustedAdvisor",
|
||||||
|
trustedadvisor_client,
|
||||||
|
):
|
||||||
|
from prowler.providers.aws.services.trustedadvisor.trustedadvisor_premium_support_plan_subscribed.trustedadvisor_premium_support_plan_subscribed import (
|
||||||
|
trustedadvisor_premium_support_plan_subscribed,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = trustedadvisor_premium_support_plan_subscribed()
|
||||||
|
result = check.execute()
|
||||||
|
assert len(result) == 0
|
||||||
|
|||||||
Reference in New Issue
Block a user