mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
feat(resource metadata): add resource metadata to JSON OCSF (#6592)
Co-authored-by: Rubén De la Torre Vico <ruben@prowler.com>
This commit is contained in:
committed by
Pepe Fagoaga
parent
b1de41619b
commit
11d9cdf24e
+40
-65
@@ -3,7 +3,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import asdict, dataclass, is_dataclass
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, Set
|
||||
|
||||
@@ -405,36 +405,34 @@ class Check_Report:
|
||||
status: str
|
||||
status_extended: str
|
||||
check_metadata: CheckMetadata
|
||||
resource_metadata: dict
|
||||
resource: dict
|
||||
resource_details: str
|
||||
resource_tags: list
|
||||
muted: bool
|
||||
|
||||
def __init__(self, metadata: Dict, resource: Any = None) -> None:
|
||||
def __init__(self, metadata: Dict, resource: Any) -> None:
|
||||
"""Initialize the Check's finding information.
|
||||
|
||||
Args:
|
||||
metadata: The metadata of the check.
|
||||
resource: Basic information about the resource. Defaults to None.
|
||||
Only accepted dict, list, BaseModels (dict attribute), custom models (with to_dict attribute) or objects with __dict__.
|
||||
Only accepted dict, list, BaseModels (dict attribute), custom models (with to_dict attribute) and dataclasses.
|
||||
"""
|
||||
self.status = ""
|
||||
self.check_metadata = CheckMetadata.parse_raw(metadata)
|
||||
if isinstance(resource, dict):
|
||||
self.resource_metadata = resource
|
||||
elif isinstance(resource, list):
|
||||
self.resource_metadata = dict(enumerate(resource))
|
||||
self.resource = resource
|
||||
elif hasattr(resource, "dict"):
|
||||
self.resource_metadata = resource.dict()
|
||||
self.resource = resource.dict()
|
||||
elif hasattr(resource, "to_dict"):
|
||||
self.resource_metadata = resource.to_dict()
|
||||
elif hasattr(resource, "__dict__"):
|
||||
self.resource_metadata = resource.__dict__
|
||||
self.resource = resource.to_dict()
|
||||
elif is_dataclass(resource):
|
||||
self.resource = asdict(resource)
|
||||
else:
|
||||
logger.error(
|
||||
f"Resource metadata {type(resource)} could not be converted to dict"
|
||||
f"Resource metadata {type(resource)} in {self.check_metadata.CheckID} could not be converted to dict"
|
||||
)
|
||||
self.resource_metadata = {}
|
||||
self.resource = {}
|
||||
self.status_extended = ""
|
||||
self.resource_details = ""
|
||||
self.resource_tags = getattr(resource, "tags", []) if resource else []
|
||||
@@ -449,20 +447,13 @@ class Check_Report_AWS(Check_Report):
|
||||
resource_arn: str
|
||||
region: str
|
||||
|
||||
def __init__(self, metadata, resource_metadata=None):
|
||||
super().__init__(metadata, resource_metadata)
|
||||
if resource_metadata:
|
||||
self.resource_id = (
|
||||
getattr(resource_metadata, "id", None)
|
||||
or getattr(resource_metadata, "name", None)
|
||||
or ""
|
||||
)
|
||||
self.resource_arn = getattr(resource_metadata, "arn", "")
|
||||
self.region = getattr(resource_metadata, "region", "")
|
||||
else:
|
||||
self.resource_id = ""
|
||||
self.resource_arn = ""
|
||||
self.region = ""
|
||||
def __init__(self, metadata: Dict, resource: Any) -> None:
|
||||
super().__init__(metadata, resource)
|
||||
self.resource_id = (
|
||||
getattr(resource, "id", None) or getattr(resource, "name", None) or ""
|
||||
)
|
||||
self.resource_arn = getattr(resource, "arn", "")
|
||||
self.region = getattr(resource, "region", "")
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -474,34 +465,20 @@ class Check_Report_Azure(Check_Report):
|
||||
subscription: str
|
||||
location: str
|
||||
|
||||
def __init__(self, metadata: Dict, resource_metadata: Any = None) -> None:
|
||||
def __init__(self, metadata: Dict, resource: Any) -> None:
|
||||
"""Initialize the Azure Check's finding information.
|
||||
|
||||
Args:
|
||||
metadata: The metadata of the check.
|
||||
resource_metadata: Basic information about the resource. Defaults to None.
|
||||
resource: Basic information about the resource. Defaults to None.
|
||||
"""
|
||||
super().__init__(metadata, resource_metadata)
|
||||
self.resource_name = (
|
||||
resource_metadata.name
|
||||
if hasattr(resource_metadata, "name")
|
||||
else (
|
||||
resource_metadata.resource_name
|
||||
if hasattr(resource_metadata, "resource_name")
|
||||
else ""
|
||||
)
|
||||
)
|
||||
self.resource_id = (
|
||||
resource_metadata.id
|
||||
if hasattr(resource_metadata, "id")
|
||||
else (
|
||||
resource_metadata.resource_id
|
||||
if hasattr(resource_metadata, "resource_id")
|
||||
else ""
|
||||
)
|
||||
super().__init__(metadata, resource)
|
||||
self.resource_name = getattr(
|
||||
resource, "name", getattr(resource, "resource_name", "")
|
||||
)
|
||||
self.resource_id = getattr(resource, "id", getattr(resource, "resource_id", ""))
|
||||
self.subscription = ""
|
||||
self.location = getattr(resource_metadata, "location", "global")
|
||||
self.location = getattr(resource, "location", "global")
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -515,26 +492,26 @@ class Check_Report_GCP(Check_Report):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
metadata,
|
||||
resource_metadata,
|
||||
metadata: Dict,
|
||||
resource: Any,
|
||||
location=None,
|
||||
resource_name=None,
|
||||
resource_id=None,
|
||||
project_id=None,
|
||||
):
|
||||
super().__init__(metadata, resource_metadata)
|
||||
) -> None:
|
||||
super().__init__(metadata, resource)
|
||||
self.resource_id = (
|
||||
resource_id
|
||||
or getattr(resource_metadata, "id", None)
|
||||
or getattr(resource_metadata, "name", None)
|
||||
or getattr(resource, "id", None)
|
||||
or getattr(resource, "name", None)
|
||||
or ""
|
||||
)
|
||||
self.resource_name = resource_name or getattr(resource_metadata, "name", "")
|
||||
self.project_id = project_id or getattr(resource_metadata, "project_id", "")
|
||||
self.resource_name = resource_name or getattr(resource, "name", "")
|
||||
self.project_id = project_id or getattr(resource, "project_id", "")
|
||||
self.location = (
|
||||
location
|
||||
or getattr(resource_metadata, "location", "")
|
||||
or getattr(resource_metadata, "region", "")
|
||||
or getattr(resource, "location", "")
|
||||
or getattr(resource, "region", "")
|
||||
)
|
||||
|
||||
|
||||
@@ -547,15 +524,13 @@ class Check_Report_Kubernetes(Check_Report):
|
||||
resource_id: str
|
||||
namespace: str
|
||||
|
||||
def __init__(self, metadata, resource_metadata):
|
||||
super().__init__(metadata, resource_metadata)
|
||||
def __init__(self, metadata: Dict, resource: Any) -> None:
|
||||
super().__init__(metadata, resource)
|
||||
self.resource_id = (
|
||||
getattr(resource_metadata, "uid", None)
|
||||
or getattr(resource_metadata, "name", None)
|
||||
or ""
|
||||
getattr(resource, "uid", None) or getattr(resource, "name", None) or ""
|
||||
)
|
||||
self.resource_name = getattr(resource_metadata, "name", "")
|
||||
self.namespace = getattr(resource_metadata, "namespace", "cluster-wide")
|
||||
self.resource_name = getattr(resource, "name", "")
|
||||
self.namespace = getattr(resource, "namespace", "cluster-wide")
|
||||
if not self.namespace:
|
||||
self.namespace = "cluster-wide"
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ def fill_common_finding_data(finding: dict, unix_timestamp: bool) -> dict:
|
||||
"status_extended": finding.status_extended,
|
||||
"muted": finding.muted,
|
||||
"resource_details": finding.resource_details,
|
||||
# "resource_metadata": finding.resource_metadata, TODO: add resource_metadata to the finding
|
||||
"resource": finding.resource,
|
||||
"resource_tags": unroll_tags(finding.resource_tags),
|
||||
}
|
||||
return finding_data
|
||||
|
||||
@@ -35,7 +35,7 @@ class Finding(BaseModel):
|
||||
status_extended: str
|
||||
muted: bool = False
|
||||
resource_uid: str
|
||||
# resource_metadata: dict = Field(default_factory=dict) TODO: add resource_metadata to the finding
|
||||
resource_metadata: dict = Field(default_factory=dict)
|
||||
resource_name: str
|
||||
resource_details: str
|
||||
resource_tags: dict = Field(default_factory=dict)
|
||||
@@ -121,6 +121,7 @@ class Finding(BaseModel):
|
||||
)
|
||||
try:
|
||||
output_data["provider"] = provider.type
|
||||
output_data["resource_metadata"] = check_output.resource
|
||||
|
||||
if provider.type == "aws":
|
||||
output_data["account_uid"] = get_nested_attribute(
|
||||
|
||||
@@ -122,7 +122,7 @@ class OCSF(Output):
|
||||
region=finding.region,
|
||||
data={
|
||||
"details": finding.resource_details,
|
||||
# "metadata": finding.resource_metadata, TODO: add the resource_metadata to the finding
|
||||
"metadata": finding.resource_metadata,
|
||||
},
|
||||
)
|
||||
]
|
||||
@@ -136,7 +136,7 @@ class OCSF(Output):
|
||||
type=finding.metadata.ResourceType,
|
||||
data={
|
||||
"details": finding.resource_details,
|
||||
# "metadata": finding.resource_metadata, TODO: add the resource_metadata to the finding
|
||||
"metadata": finding.resource_metadata,
|
||||
},
|
||||
namespace=finding.region.replace("namespace: ", ""),
|
||||
)
|
||||
@@ -202,10 +202,15 @@ class OCSF(Output):
|
||||
):
|
||||
self._file_descriptor.write("[")
|
||||
for finding in self._data:
|
||||
self._file_descriptor.write(
|
||||
finding.json(exclude_none=True, indent=4)
|
||||
)
|
||||
self._file_descriptor.write(",")
|
||||
try:
|
||||
self._file_descriptor.write(
|
||||
finding.json(exclude_none=True, indent=4)
|
||||
)
|
||||
self._file_descriptor.write(",")
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if self._file_descriptor.tell() > 0:
|
||||
if self._file_descriptor.tell() != 1:
|
||||
self._file_descriptor.seek(
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class accessanalyzer_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for analyzer in accessanalyzer_client.analyzers:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=analyzer
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=analyzer)
|
||||
if analyzer.status == "ACTIVE":
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class accessanalyzer_enabled_without_findings(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for analyzer in accessanalyzer_client.analyzers:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=analyzer
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=analyzer)
|
||||
if analyzer.status == "ACTIVE":
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"IAM Access Analyzer {analyzer.name} does not have active findings."
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ from prowler.providers.aws.services.account.account_client import account_client
|
||||
class account_maintain_current_contact_details(Check):
|
||||
def execute(self):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=account_client.contact_base
|
||||
metadata=self.metadata(), resource=account_client.contact_base
|
||||
)
|
||||
report.region = account_client.region
|
||||
report.resource_id = account_client.audited_account
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ class account_maintain_different_contact_details_to_security_billing_and_operati
|
||||
findings = []
|
||||
if account_client.contact_base:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=account_client.contact_base
|
||||
metadata=self.metadata(), resource=account_client.contact_base
|
||||
)
|
||||
report.resource_id = account_client.audited_account
|
||||
report.resource_arn = account_client.audited_account_arn
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ from prowler.providers.aws.services.account.account_client import account_client
|
||||
class account_security_contact_information_is_registered(Check):
|
||||
def execute(self):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=account_client.contact_base
|
||||
metadata=self.metadata(), resource=account_client.contact_base
|
||||
)
|
||||
report.region = account_client.region
|
||||
report.resource_id = account_client.audited_account
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ from prowler.providers.aws.services.account.account_client import account_client
|
||||
class account_security_questions_are_registered_in_the_aws_account(Check):
|
||||
def execute(self):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=account_client.contacts_security
|
||||
metadata=self.metadata(), resource=account_client.contacts_security
|
||||
)
|
||||
report.region = account_client.region
|
||||
report.resource_id = account_client.audited_account
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class acm_certificates_expiration_check(Check):
|
||||
for certificate in acm_client.certificates.values():
|
||||
if certificate.in_use or acm_client.provider.scan_unused_services:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=certificate
|
||||
metadata=self.metadata(), resource=certificate
|
||||
)
|
||||
if certificate.expiration_days > acm_client.audit_config.get(
|
||||
"days_to_expire_threshold", 7
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class acm_certificates_transparency_logs_enabled(Check):
|
||||
for certificate in acm_client.certificates.values():
|
||||
if certificate.in_use or acm_client.provider.scan_unused_services:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=certificate
|
||||
metadata=self.metadata(), resource=certificate
|
||||
)
|
||||
if certificate.type == "IMPORTED":
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class acm_certificates_with_secure_key_algorithms(Check):
|
||||
for certificate in acm_client.certificates.values():
|
||||
if certificate.in_use or acm_client.provider.scan_unused_services:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=certificate
|
||||
metadata=self.metadata(), resource=certificate
|
||||
)
|
||||
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class apigateway_restapi_authorizers_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=rest_api
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=rest_api)
|
||||
report.resource_id = rest_api.name
|
||||
|
||||
# it there are not authorizers at api level and resources without methods (default case) ->
|
||||
|
||||
+1
-3
@@ -10,9 +10,7 @@ class apigateway_restapi_cache_encrypted(Check):
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
for stage in rest_api.stages:
|
||||
if stage.cache_enabled:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=stage
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stage)
|
||||
report.region = rest_api.region
|
||||
report.resource_id = rest_api.name
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class apigateway_restapi_client_certificate_enabled(Check):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
for stage in rest_api.stages:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=stage
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stage)
|
||||
report.resource_id = rest_api.name
|
||||
report.region = rest_api.region
|
||||
if stage.client_certificate:
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class apigateway_restapi_logging_enabled(Check):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
for stage in rest_api.stages:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=stage
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stage)
|
||||
report.resource_id = rest_api.name
|
||||
report.region = rest_api.region
|
||||
if stage.logging:
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class apigateway_restapi_public(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=rest_api
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=rest_api)
|
||||
report.resource_id = rest_api.name
|
||||
|
||||
if rest_api.public_endpoint:
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class apigateway_restapi_public_with_authorizer(Check):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
if rest_api.public_endpoint:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=rest_api
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=rest_api)
|
||||
report.resource_id = rest_api.name
|
||||
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class apigateway_restapi_tracing_enabled(Check):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
for stage in rest_api.stages:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=stage
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stage)
|
||||
report.region = rest_api.region
|
||||
report.resource_id = rest_api.name
|
||||
report.status = "FAIL"
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class apigateway_restapi_waf_acl_attached(Check):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
for stage in rest_api.stages:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=stage
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stage)
|
||||
report.resource_id = rest_api.name
|
||||
report.region = rest_api.region
|
||||
if stage.waf:
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class apigatewayv2_api_access_logging_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for api in apigatewayv2_client.apis:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=api)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=api)
|
||||
for stage in api.stages:
|
||||
if stage.logging:
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class apigatewayv2_api_authorizers_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for api in apigatewayv2_client.apis:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=api)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=api)
|
||||
report.resource_id = api.name
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"API Gateway V2 {api.name} ID {api.id} does not have an authorizer configured."
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ class appstream_fleet_default_internet_access_disabled(Check):
|
||||
"""Execute the appstream_fleet_default_internet_access_disabled check"""
|
||||
findings = []
|
||||
for fleet in appstream_client.fleets:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=fleet)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=fleet)
|
||||
|
||||
if fleet.enable_default_internet_access:
|
||||
report.status = "FAIL"
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class appstream_fleet_maximum_session_duration(Check):
|
||||
|
||||
findings = []
|
||||
for fleet in appstream_client.fleets:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=fleet)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=fleet)
|
||||
|
||||
if fleet.max_user_duration_in_seconds < max_session_duration_seconds:
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class appstream_fleet_session_disconnect_timeout(Check):
|
||||
|
||||
findings = []
|
||||
for fleet in appstream_client.fleets:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=fleet)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=fleet)
|
||||
|
||||
if fleet.disconnect_timeout_in_seconds <= max_disconnect_timeout_in_seconds:
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class appstream_fleet_session_idle_disconnect_timeout(Check):
|
||||
|
||||
findings = []
|
||||
for fleet in appstream_client.fleets:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=fleet)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=fleet)
|
||||
|
||||
if (
|
||||
fleet.idle_disconnect_timeout_in_seconds
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ class appsync_field_level_logging_enabled(Check):
|
||||
findings = []
|
||||
# Check only GraphQL APIs because boto3 does not have a method to get other types of AppSync APIs (list_apis is not working)
|
||||
for api in appsync_client.graphql_apis.values():
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=api)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=api)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"AppSync API {api.name} has field log level enabled."
|
||||
|
||||
+1
-3
@@ -7,9 +7,7 @@ class appsync_graphql_api_no_api_key_authentication(Check):
|
||||
findings = []
|
||||
for api in appsync_client.graphql_apis.values():
|
||||
if api.type == "GRAPHQL":
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=api
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=api)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"AppSync GraphQL API {api.name} is not using an API KEY for authentication."
|
||||
if api.authentication_type == "API_KEY":
|
||||
|
||||
+1
-3
@@ -13,9 +13,7 @@ class athena_workgroup_encryption(Check):
|
||||
if (
|
||||
workgroup.state == "ENABLED" and workgroup.queries
|
||||
) or athena_client.provider.scan_unused_services:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=workgroup
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=workgroup)
|
||||
|
||||
if workgroup.encryption_configuration.encrypted:
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-3
@@ -13,9 +13,7 @@ class athena_workgroup_enforce_configuration(Check):
|
||||
if (
|
||||
workgroup.state == "ENABLED" and workgroup.queries
|
||||
) or athena_client.provider.scan_unused_services:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=workgroup
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=workgroup)
|
||||
|
||||
if workgroup.enforce_workgroup_configuration:
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-3
@@ -21,9 +21,7 @@ class athena_workgroup_logging_enabled(Check):
|
||||
if (
|
||||
workgroup.state == "ENABLED" and workgroup.queries
|
||||
) or athena_client.provider.scan_unused_services:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=workgroup
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=workgroup)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Athena WorkGroup {workgroup.name} has CloudWatch logging enabled."
|
||||
|
||||
+1
-3
@@ -20,9 +20,7 @@ class autoscaling_find_secrets_ec2_launch_configuration(Check):
|
||||
configuration_arn,
|
||||
configuration,
|
||||
) in autoscaling_client.launch_configurations.items():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=configuration
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=configuration)
|
||||
|
||||
if configuration.user_data:
|
||||
user_data = b64decode(configuration.user_data)
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class autoscaling_group_capacity_rebalance_enabled(Check):
|
||||
findings = []
|
||||
for group in autoscaling_client.groups:
|
||||
if group.load_balancers and group.target_groups:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=group
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=group)
|
||||
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Autoscaling group {group.name} does not have capacity rebalance enabled."
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class autoscaling_group_elb_health_check_enabled(Check):
|
||||
findings = []
|
||||
for group in autoscaling_client.groups:
|
||||
if group.load_balancers and group.target_groups:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=group
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=group)
|
||||
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Autoscaling group {group.name} is associated with a load balancer but does not have ELB health checks enabled, instead it has {group.health_check_type} health checks."
|
||||
|
||||
+1
-3
@@ -10,9 +10,7 @@ class autoscaling_group_launch_configuration_no_public_ip(Check):
|
||||
for group in autoscaling_client.groups:
|
||||
for lc in autoscaling_client.launch_configurations.values():
|
||||
if lc.name == group.launch_configuration_name:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=group
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=group)
|
||||
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Autoscaling group {group.name} does not have an associated launch configuration assigning a public IP address."
|
||||
|
||||
+1
-3
@@ -12,9 +12,7 @@ class autoscaling_group_launch_configuration_requires_imdsv2(Check):
|
||||
launch_configuration
|
||||
) in autoscaling_client.launch_configurations.values():
|
||||
if launch_configuration.name == group.launch_configuration_name:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=group
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=group)
|
||||
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Autoscaling group {group.name} has IMDSv2 disabled or not required."
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class autoscaling_group_multiple_az(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for group in autoscaling_client.groups:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=group)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=group)
|
||||
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class autoscaling_group_multiple_instance_types(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for group in autoscaling_client.groups:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=group)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=group)
|
||||
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Autoscaling group {group.name} does not have multiple instance types in multiple Availability Zones."
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class autoscaling_group_using_ec2_launch_template(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for group in autoscaling_client.groups:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=group)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=group)
|
||||
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class awslambda_function_inside_vpc(Check):
|
||||
def execute(self) -> List[Check_Report_AWS]:
|
||||
findings = []
|
||||
for function_arn, function in awslambda_client.functions.items():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
|
||||
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class awslambda_function_invoke_api_operations_cloudtrail_logging_enabled(Check)
|
||||
def execute(self):
|
||||
findings = []
|
||||
for function in awslambda_client.functions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
|
||||
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ class awslambda_function_no_secrets_in_code(Check):
|
||||
for function, function_code in awslambda_client._get_function_code():
|
||||
if function_code:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
metadata=self.metadata(), resource=function
|
||||
)
|
||||
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-3
@@ -13,9 +13,7 @@ class awslambda_function_no_secrets_in_variables(Check):
|
||||
"secrets_ignore_patterns", []
|
||||
)
|
||||
for function in awslambda_client.functions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
|
||||
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
|
||||
+1
-3
@@ -7,9 +7,7 @@ class awslambda_function_not_publicly_accessible(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for function in awslambda_client.functions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
|
||||
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Lambda function {function.name} has a policy resource-based policy not public."
|
||||
|
||||
+1
-3
@@ -6,9 +6,7 @@ class awslambda_function_url_cors_policy(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for function in awslambda_client.functions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
|
||||
|
||||
if function.url_config:
|
||||
if "*" in function.url_config.cors_config.allow_origins:
|
||||
|
||||
+1
-3
@@ -7,9 +7,7 @@ class awslambda_function_url_public(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for function in awslambda_client.functions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
|
||||
|
||||
if function.url_config:
|
||||
if function.url_config.auth_type == AuthType.AWS_IAM:
|
||||
|
||||
+1
-3
@@ -33,9 +33,7 @@ class awslambda_function_using_supported_runtimes(Check):
|
||||
findings = []
|
||||
for function in awslambda_client.functions.values():
|
||||
if function.runtime:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
|
||||
|
||||
if function.runtime in awslambda_client.audit_config.get(
|
||||
"obsolete_lambda_runtimes", default_obsolete_lambda_runtimes
|
||||
|
||||
+1
-3
@@ -16,9 +16,7 @@ class awslambda_function_vpc_multi_az(Check):
|
||||
awslambda_function_inside_vpc.__name__,
|
||||
function_arn,
|
||||
):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=function
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
|
||||
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
@@ -8,14 +8,14 @@ class backup_plans_exist(Check):
|
||||
if backup_client.backup_plans:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(),
|
||||
resource_metadata=backup_client.backup_plans[0],
|
||||
resource=backup_client.backup_plans[0],
|
||||
)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"At least one Backup Plan exists: {backup_client.backup_plans[0].name}."
|
||||
report.resource_id = backup_client.backup_plans[0].name
|
||||
findings.append(report)
|
||||
elif backup_client.backup_vaults:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata={})
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource={})
|
||||
report.region = backup_client.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No Backup Plan exist."
|
||||
|
||||
+1
-3
@@ -6,9 +6,7 @@ class backup_recovery_point_encrypted(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for recovery_point in backup_client.recovery_points:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=recovery_point
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=recovery_point)
|
||||
report.region = recovery_point.backup_vault_region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Backup Recovery Point {recovery_point.id} for Backup Vault {recovery_point.backup_vault_name} is not encrypted at rest."
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ class backup_reportplans_exist(Check):
|
||||
if backup_client.backup_plans:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(),
|
||||
resource_metadata=backup_client.backup_plans[0],
|
||||
resource=backup_client.backup_plans[0],
|
||||
)
|
||||
report.resource_arn = backup_client.report_plan_arn_template
|
||||
report.resource_id = backup_client.audited_account
|
||||
@@ -19,7 +19,7 @@ class backup_reportplans_exist(Check):
|
||||
if backup_client.backup_report_plans:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(),
|
||||
resource_metadata=backup_client.backup_report_plans[0],
|
||||
resource=backup_client.backup_report_plans[0],
|
||||
)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"At least one backup report plan exists: {backup_client.backup_report_plans[0].name}."
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class backup_vaults_encrypted(Check):
|
||||
if backup_client.backup_vaults:
|
||||
for backup_vault in backup_client.backup_vaults:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=backup_vault
|
||||
metadata=self.metadata(), resource=backup_vault
|
||||
)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
@@ -6,9 +6,7 @@ class backup_vaults_exist(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
if backup_client.backup_vaults is not None:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=backup_client.backup_vaults
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource={})
|
||||
report.resource_arn = backup_client.backup_vault_arn_template
|
||||
report.resource_id = backup_client.audited_account
|
||||
report.region = backup_client.region
|
||||
@@ -18,7 +16,7 @@ class backup_vaults_exist(Check):
|
||||
if backup_client.backup_vaults:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(),
|
||||
resource_metadata=backup_client.backup_vaults[0],
|
||||
resource=backup_client.backup_vaults[0],
|
||||
)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}."
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class bedrock_agent_guardrail_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for agent in bedrock_agent_client.agents.values():
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=agent)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=agent)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Bedrock Agent {agent.name} is not using any guardrail to protect agent sessions."
|
||||
if agent.guardrail_id:
|
||||
|
||||
+1
-3
@@ -6,9 +6,7 @@ class bedrock_guardrail_prompt_attack_filter_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for guardrail in bedrock_client.guardrails.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=guardrail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=guardrail)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Bedrock Guardrail {guardrail.name} is configured to detect and block prompt attacks with a HIGH strength."
|
||||
if not guardrail.prompt_attack_filter_strength:
|
||||
|
||||
+1
-3
@@ -6,9 +6,7 @@ class bedrock_guardrail_sensitive_information_filter_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for guardrail in bedrock_client.guardrails.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=guardrail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=guardrail)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Bedrock Guardrail {guardrail.name} is blocking or masking sensitive information."
|
||||
if not guardrail.sensitive_information_filter:
|
||||
|
||||
+1
-3
@@ -6,9 +6,7 @@ class bedrock_model_invocation_logging_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for region, logging in bedrock_client.logging_configurations.items():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=logging
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=logging)
|
||||
report.region = region
|
||||
report.resource_id = "model-invocation-logging"
|
||||
report.resource_arn = (
|
||||
|
||||
+1
-3
@@ -11,9 +11,7 @@ class bedrock_model_invocation_logs_encryption_enabled(Check):
|
||||
if logging.enabled:
|
||||
s3_encryption = True
|
||||
cloudwatch_encryption = True
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=logging
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=logging)
|
||||
report.region = region
|
||||
report.resource_id = "model-invocation-logging"
|
||||
report.resource_arn = (
|
||||
|
||||
+1
-3
@@ -23,9 +23,7 @@ class cloudformation_stack_cdktoolkit_bootstrap_version(Check):
|
||||
bootstrap_version = int(output.split(":")[1])
|
||||
break
|
||||
if bootstrap_version:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=stack
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stack)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudFormation Stack CDKToolkit has a Bootstrap version {bootstrap_version}, which meets the recommended version."
|
||||
if bootstrap_version < recommended_cdk_bootstrap_version:
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class cloudformation_stack_outputs_find_secrets(Check):
|
||||
"secrets_ignore_patterns", []
|
||||
)
|
||||
for stack in cloudformation_client.stacks:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource_metadata=stack)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stack)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"No secrets found in CloudFormation Stack {stack.name} Outputs."
|
||||
|
||||
+1
-3
@@ -12,9 +12,7 @@ class cloudformation_stacks_termination_protection_enabled(Check):
|
||||
findings = []
|
||||
for stack in cloudformation_client.stacks:
|
||||
if not stack.is_nested_stack:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=stack
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stack)
|
||||
|
||||
if stack.enable_termination_protection:
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudfront_distributions_custom_ssl_certificate(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudFront Distribution {distribution.id} is using a custom SSL/TLS certificate."
|
||||
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudfront_distributions_default_root_object(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
|
||||
if distribution.default_root_object:
|
||||
report.status = "PASS"
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudfront_distributions_field_level_encryption_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
if (
|
||||
distribution.default_cache_config
|
||||
and distribution.default_cache_config.field_level_encryption_id
|
||||
|
||||
+1
-3
@@ -11,9 +11,7 @@ class cloudfront_distributions_geo_restrictions_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
|
||||
if distribution.geo_restriction_type == GeoRestrictionType.none:
|
||||
report.status = "FAIL"
|
||||
|
||||
+1
-3
@@ -11,9 +11,7 @@ class cloudfront_distributions_https_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
|
||||
if (
|
||||
distribution.default_cache_config
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ class cloudfront_distributions_https_sni_enabled(Check):
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
if distribution.certificate:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
metadata=self.metadata(), resource=distribution
|
||||
)
|
||||
|
||||
if distribution.ssl_support_method == SSLSupportMethod.sni_only:
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudfront_distributions_logging_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
if distribution.logging_enabled or (
|
||||
distribution.default_cache_config
|
||||
and distribution.default_cache_config.realtime_log_config_arn
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudfront_distributions_multiple_origin_failover_configured(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudFront Distribution {distribution.id} does not have an origin group configured with at least 2 origins."
|
||||
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudfront_distributions_origin_traffic_encrypted(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudFront Distribution {distribution.id} does encrypt traffic to custom origins."
|
||||
unencrypted_origins = []
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudfront_distributions_s3_origin_access_control(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
|
||||
if any(origin.s3_origin_config for origin in distribution.origins):
|
||||
s3_buckets_with_no_oac = []
|
||||
|
||||
+1
-3
@@ -9,9 +9,7 @@ class cloudfront_distributions_s3_origin_non_existent_bucket(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudFront Distribution {distribution.id} does not have non-existent S3 buckets as origins."
|
||||
non_existent_buckets = []
|
||||
|
||||
+1
-3
@@ -11,9 +11,7 @@ class cloudfront_distributions_using_deprecated_ssl_protocols(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudFront Distribution {distribution.id} is not using a deprecated SSL protocol."
|
||||
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudfront_distributions_using_waf(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for distribution in cloudfront_client.distributions.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=distribution
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
|
||||
if distribution.web_acl_id:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudFront Distribution {distribution.id} is using AWS WAF web ACL {distribution.web_acl_id}."
|
||||
|
||||
+1
-3
@@ -13,9 +13,7 @@ class cloudtrail_bucket_requires_mfa_delete(Check):
|
||||
if trail.is_logging:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=trail)
|
||||
report.region = trail.home_region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled."
|
||||
|
||||
+1
-3
@@ -14,9 +14,7 @@ class cloudtrail_cloudwatch_logging_enabled(Check):
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=trail)
|
||||
report.region = trail.home_region
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
|
||||
+1
-3
@@ -10,9 +10,7 @@ class cloudtrail_insights_exist(Check):
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_logging:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=trail)
|
||||
report.region = trail.home_region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging."
|
||||
|
||||
+1
-3
@@ -10,9 +10,7 @@ class cloudtrail_kms_encryption_enabled(Check):
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=trail)
|
||||
report.region = trail.home_region
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
|
||||
+1
-3
@@ -10,9 +10,7 @@ class cloudtrail_log_file_validation_enabled(Check):
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=trail)
|
||||
report.region = trail.home_region
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
|
||||
+1
-3
@@ -13,9 +13,7 @@ class cloudtrail_logs_s3_bucket_access_logging_enabled(Check):
|
||||
if trail.name:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=trail)
|
||||
report.region = trail.home_region
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
|
||||
+1
-3
@@ -13,9 +13,7 @@ class cloudtrail_logs_s3_bucket_is_not_publicly_accessible(Check):
|
||||
if trail.name:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=trail)
|
||||
report.region = trail.home_region
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ class cloudtrail_multi_region_enabled(Check):
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.region == region or trail.is_multiregion:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
metadata=self.metadata(), resource=trail
|
||||
)
|
||||
report.region = region
|
||||
if trail.is_logging:
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ class cloudtrail_multi_region_enabled_logging_management_events(Check):
|
||||
if cloudtrail_client.trails is not None:
|
||||
for region in cloudtrail_client.regional_clients.keys():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudTrail trails enabled and logging management events were found."
|
||||
@@ -48,7 +48,7 @@ class cloudtrail_multi_region_enabled_logging_management_events(Check):
|
||||
trail_is_logging_management_events = True
|
||||
if trail_is_logging_management_events:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
metadata=self.metadata(), resource=trail
|
||||
)
|
||||
report.region = region
|
||||
report.status = "PASS"
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@ class cloudtrail_s3_dataevents_read_enabled(Check):
|
||||
):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(),
|
||||
resource_metadata=trail,
|
||||
resource=trail,
|
||||
)
|
||||
report.region = trail.home_region
|
||||
report.status = "PASS"
|
||||
@@ -45,7 +45,7 @@ class cloudtrail_s3_dataevents_read_enabled(Check):
|
||||
and field_selector["Equals"][0] == "AWS::S3::Object"
|
||||
):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
metadata=self.metadata(), resource=trail
|
||||
)
|
||||
report.region = trail.home_region
|
||||
report.status = "PASS"
|
||||
@@ -55,7 +55,7 @@ class cloudtrail_s3_dataevents_read_enabled(Check):
|
||||
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
||||
):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@ class cloudtrail_s3_dataevents_write_enabled(Check):
|
||||
):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(),
|
||||
resource_metadata=trail,
|
||||
resource=trail,
|
||||
)
|
||||
report.region = trail.home_region
|
||||
report.status = "PASS"
|
||||
@@ -45,7 +45,7 @@ class cloudtrail_s3_dataevents_write_enabled(Check):
|
||||
and field_selector["Equals"][0] == "AWS::S3::Object"
|
||||
):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=trail
|
||||
metadata=self.metadata(), resource=trail
|
||||
)
|
||||
report.region = trail.home_region
|
||||
report.status = "PASS"
|
||||
@@ -55,7 +55,7 @@ class cloudtrail_s3_dataevents_write_enabled(Check):
|
||||
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
||||
):
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
|
||||
+2
-2
@@ -160,7 +160,7 @@ class cloudtrail_threat_detection_enumeration(Check):
|
||||
if len(actions) / len(enumeration_actions) > threshold:
|
||||
found_potential_enumeration = True
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = aws_identity_arn.split("/")[-1]
|
||||
@@ -170,7 +170,7 @@ class cloudtrail_threat_detection_enumeration(Check):
|
||||
findings.append(report)
|
||||
if not found_potential_enumeration:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
|
||||
+2
-2
@@ -82,7 +82,7 @@ class cloudtrail_threat_detection_llm_jacking(Check):
|
||||
if len(actions) / len(llm_jacking_actions) > threshold:
|
||||
found_potential_llm_jacking = True
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = aws_identity_arn.split("/")[-1]
|
||||
@@ -92,7 +92,7 @@ class cloudtrail_threat_detection_llm_jacking(Check):
|
||||
findings.append(report)
|
||||
if not found_potential_llm_jacking:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
|
||||
+2
-2
@@ -123,7 +123,7 @@ class cloudtrail_threat_detection_privilege_escalation(Check):
|
||||
if len(actions) / len(privilege_escalation_actions) > threshold:
|
||||
found_potential_privilege_escalation = True
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = aws_identity_arn.split("/")[-1]
|
||||
@@ -133,7 +133,7 @@ class cloudtrail_threat_detection_privilege_escalation(Check):
|
||||
findings.append(report)
|
||||
if not found_potential_privilege_escalation:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=cloudtrail_client.trails
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudwatch_alarm_actions_alarm_state_configured(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for metric_alarm in cloudwatch_client.metric_alarms:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=metric_alarm
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=metric_alarm)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch metric alarm {metric_alarm.name} has actions configured for the ALARM state."
|
||||
if not metric_alarm.alarm_actions:
|
||||
|
||||
+1
-3
@@ -8,9 +8,7 @@ class cloudwatch_alarm_actions_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for metric_alarm in cloudwatch_client.metric_alarms:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=metric_alarm
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=metric_alarm)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"CloudWatch metric alarm {metric_alarm.name} has actions enabled."
|
||||
|
||||
+1
-3
@@ -26,9 +26,7 @@ class cloudwatch_changes_to_network_acls_alarm_configured(Check):
|
||||
|
||||
if cloudtrail_client.trails is not None:
|
||||
if report is None:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=logs_client.log_groups
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource={})
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
|
||||
+1
-3
@@ -26,9 +26,7 @@ class cloudwatch_changes_to_network_gateways_alarm_configured(Check):
|
||||
|
||||
if cloudtrail_client.trails is not None:
|
||||
if report is None:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=logs_client.log_groups
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource={})
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
|
||||
+1
-3
@@ -26,9 +26,7 @@ class cloudwatch_changes_to_network_route_tables_alarm_configured(Check):
|
||||
|
||||
if cloudtrail_client.trails is not None:
|
||||
if report is None:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=logs_client.log_groups
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource={})
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
|
||||
+1
-3
@@ -26,9 +26,7 @@ class cloudwatch_changes_to_vpcs_alarm_configured(Check):
|
||||
|
||||
if cloudtrail_client.trails is not None:
|
||||
if report is None:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=logs_client.log_groups
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource={})
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
|
||||
+2
-6
@@ -6,9 +6,7 @@ class cloudwatch_cross_account_sharing_disabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
if iam_client.roles is not None:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=iam_client.roles
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource={})
|
||||
report.status = "PASS"
|
||||
report.status_extended = "CloudWatch doesn't allow cross-account sharing."
|
||||
report.region = iam_client.region
|
||||
@@ -16,9 +14,7 @@ class cloudwatch_cross_account_sharing_disabled(Check):
|
||||
report.resource_id = iam_client.audited_account
|
||||
for role in iam_client.roles:
|
||||
if role.name == "CloudWatch-CrossAccountSharingRole":
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=role
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=role)
|
||||
report.region = iam_client.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
+1
-3
@@ -7,9 +7,7 @@ class cloudwatch_log_group_kms_encryption_enabled(Check):
|
||||
findings = []
|
||||
if logs_client.log_groups:
|
||||
for log_group in logs_client.log_groups.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=log_group
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=log_group)
|
||||
if log_group.kms_id:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated."
|
||||
|
||||
+1
-3
@@ -16,9 +16,7 @@ class cloudwatch_log_group_no_secrets_in_logs(Check):
|
||||
"secrets_ignore_patterns", []
|
||||
)
|
||||
for log_group in logs_client.log_groups.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=log_group
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=log_group)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"No secrets found in {log_group.name} log group."
|
||||
|
||||
+1
-3
@@ -25,9 +25,7 @@ class cloudwatch_log_group_not_publicly_accessible(Check):
|
||||
if log_group.arn in resource or resource == "*":
|
||||
public_log_groups.append(log_group.arn)
|
||||
for log_group in logs_client.log_groups.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=log_group
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=log_group)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Log Group {log_group.name} is not publicly accessible."
|
||||
|
||||
+1
-3
@@ -12,9 +12,7 @@ class cloudwatch_log_group_retention_policy_specific_days_enabled(Check):
|
||||
)
|
||||
if logs_client.log_groups:
|
||||
for log_group in logs_client.log_groups.values():
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource_metadata=log_group
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=log_group)
|
||||
if (
|
||||
log_group.never_expire is False
|
||||
and log_group.retention_days < specific_retention_days
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user