mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
chore(security hub): improve securityhub_enabled check logic (#1851)
Co-authored-by: sergargar <sergargar@users.noreply.github.com>
This commit is contained in:
@@ -12,9 +12,13 @@ class securityhub_enabled(Check):
|
||||
report.region = securityhub.region
|
||||
if securityhub.status == "ACTIVE":
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Security Hub is enabled with standards {securityhub.standards}"
|
||||
)
|
||||
if securityhub.standards:
|
||||
report.status_extended = f"Security Hub is enabled with standards: {securityhub.standards}"
|
||||
elif securityhub.integrations:
|
||||
report.status_extended = f"Security Hub is enabled without standards but with integrations: {securityhub.integrations}"
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Security Hub is enabled but without any standard or integration"
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Security Hub is not enabled"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
|
||||
from botocore.client import ClientError
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
|
||||
@@ -32,87 +34,79 @@ class SecurityHub:
|
||||
def __describe_hub__(self, regional_client):
|
||||
logger.info("SecurityHub - Describing Hub...")
|
||||
try:
|
||||
get_enabled_standards_paginator = regional_client.get_paginator(
|
||||
"get_enabled_standards"
|
||||
)
|
||||
standards = ""
|
||||
for page in get_enabled_standards_paginator.paginate():
|
||||
for standard in page["StandardsSubscriptions"]:
|
||||
standards += f" {standard['StandardsArn'].split('/')[1]}"
|
||||
# Security Hub is not enabled in region
|
||||
if standards == "":
|
||||
self.securityhubs.append(
|
||||
SecurityHubHub(
|
||||
"",
|
||||
"Security Hub",
|
||||
"NOT_AVAILABLE",
|
||||
"",
|
||||
regional_client.region,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# SecurityHub is active so get HubArn
|
||||
# Check if SecurityHub is active
|
||||
try:
|
||||
hub_arn = regional_client.describe_hub()["HubArn"]
|
||||
except ClientError as e:
|
||||
# Check if Account is subscribed to Security Hub
|
||||
if e.response["Error"]["Code"] == "InvalidAccessException":
|
||||
self.securityhubs.append(
|
||||
SecurityHubHub(
|
||||
arn="",
|
||||
id="Security Hub",
|
||||
status="NOT_AVAILABLE",
|
||||
standards="",
|
||||
integrations="",
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
else:
|
||||
if not self.audit_resources or (
|
||||
is_resource_filtered(hub_arn, self.audit_resources)
|
||||
):
|
||||
hub_id = hub_arn.split("/")[1]
|
||||
get_enabled_standards_paginator = regional_client.get_paginator(
|
||||
"get_enabled_standards"
|
||||
)
|
||||
standards = ""
|
||||
for page in get_enabled_standards_paginator.paginate():
|
||||
for standard in page["StandardsSubscriptions"]:
|
||||
standards += f"{standard['StandardsArn'].split('/')[1]} "
|
||||
list_enabled_products_for_import_paginator = (
|
||||
regional_client.get_paginator(
|
||||
"list_enabled_products_for_import"
|
||||
)
|
||||
)
|
||||
integrations = ""
|
||||
for page in list_enabled_products_for_import_paginator.paginate():
|
||||
for integration in page["ProductSubscriptions"]:
|
||||
if (
|
||||
"/aws/securityhub" not in integration
|
||||
): # ignore Security Hub integration with itself
|
||||
integrations += f"{integration.split('/')[-1]} "
|
||||
self.securityhubs.append(
|
||||
SecurityHubHub(
|
||||
hub_arn,
|
||||
hub_id,
|
||||
"ACTIVE",
|
||||
standards,
|
||||
regional_client.region,
|
||||
arn=hub_arn,
|
||||
id=hub_id,
|
||||
status="ACTIVE",
|
||||
standards=standards,
|
||||
integrations=integrations,
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# SecurityHub is filtered
|
||||
self.securityhubs.append(
|
||||
SecurityHubHub(
|
||||
"",
|
||||
"Security Hub",
|
||||
"NOT_AVAILABLE",
|
||||
"",
|
||||
regional_client.region,
|
||||
arn="",
|
||||
id="Security Hub",
|
||||
status="NOT_AVAILABLE",
|
||||
standards="",
|
||||
integrations="",
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
# Check if Account is subscribed to Security Hub
|
||||
if "InvalidAccessException" in str(error):
|
||||
self.securityhubs.append(
|
||||
SecurityHubHub(
|
||||
"",
|
||||
"Security Hub",
|
||||
"NOT_AVAILABLE",
|
||||
"",
|
||||
regional_client.region,
|
||||
)
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SecurityHubHub:
|
||||
class SecurityHubHub(BaseModel):
|
||||
arn: str
|
||||
id: str
|
||||
status: str
|
||||
standards: str
|
||||
integrations: str
|
||||
region: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
arn,
|
||||
id,
|
||||
status,
|
||||
standards,
|
||||
region,
|
||||
):
|
||||
self.arn = arn
|
||||
self.id = id
|
||||
self.status = status
|
||||
self.standards = standards
|
||||
self.region = region
|
||||
|
||||
@@ -10,11 +10,12 @@ class Test_accessanalyzer_enabled_without_findings:
|
||||
securityhub_client = mock.MagicMock
|
||||
securityhub_client.securityhubs = [
|
||||
SecurityHubHub(
|
||||
"",
|
||||
"Security Hub",
|
||||
"NOT_AVAILABLE",
|
||||
"",
|
||||
"eu-west-1",
|
||||
arn="",
|
||||
id="Security Hub",
|
||||
status="NOT_AVAILABLE",
|
||||
standards="",
|
||||
integrations="",
|
||||
region="eu-west-1",
|
||||
)
|
||||
]
|
||||
with mock.patch(
|
||||
@@ -33,15 +34,16 @@ class Test_accessanalyzer_enabled_without_findings:
|
||||
assert result[0].status_extended == "Security Hub is not enabled"
|
||||
assert result[0].resource_id == "Security Hub"
|
||||
|
||||
def test_securityhub_hub_active(self):
|
||||
def test_securityhub_hub_active_with_standards(self):
|
||||
securityhub_client = mock.MagicMock
|
||||
securityhub_client.securityhubs = [
|
||||
SecurityHubHub(
|
||||
"arn:aws:securityhub:us-east-1:0123456789012:hub/default",
|
||||
"default",
|
||||
"ACTIVE",
|
||||
"cis-aws-foundations-benchmark/v/1.2.0",
|
||||
"eu-west-1",
|
||||
arn="arn:aws:securityhub:us-east-1:0123456789012:hub/default",
|
||||
id="default",
|
||||
status="ACTIVE",
|
||||
standards="cis-aws-foundations-benchmark/v/1.2.0",
|
||||
integrations="",
|
||||
region="eu-west-1",
|
||||
)
|
||||
]
|
||||
with mock.patch(
|
||||
@@ -59,6 +61,68 @@ class Test_accessanalyzer_enabled_without_findings:
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Security Hub is enabled with standards cis-aws-foundations-benchmark/v/1.2.0"
|
||||
== "Security Hub is enabled with standards: cis-aws-foundations-benchmark/v/1.2.0"
|
||||
)
|
||||
assert result[0].resource_id == "default"
|
||||
|
||||
def test_securityhub_hub_active_with_integrations(self):
|
||||
securityhub_client = mock.MagicMock
|
||||
securityhub_client.securityhubs = [
|
||||
SecurityHubHub(
|
||||
arn="arn:aws:securityhub:us-east-1:0123456789012:hub/default",
|
||||
id="default",
|
||||
status="ACTIVE",
|
||||
standards="",
|
||||
integrations="prowler",
|
||||
region="eu-west-1",
|
||||
)
|
||||
]
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.securityhub.securityhub_service.SecurityHub",
|
||||
new=securityhub_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.securityhub.securityhub_enabled.securityhub_enabled import (
|
||||
securityhub_enabled,
|
||||
)
|
||||
|
||||
check = securityhub_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Security Hub is enabled without standards but with integrations: prowler"
|
||||
)
|
||||
assert result[0].resource_id == "default"
|
||||
|
||||
def test_securityhub_hub_active_without_integrations_or_standards(self):
|
||||
securityhub_client = mock.MagicMock
|
||||
securityhub_client.securityhubs = [
|
||||
SecurityHubHub(
|
||||
arn="arn:aws:securityhub:us-east-1:0123456789012:hub/default",
|
||||
id="default",
|
||||
status="ACTIVE",
|
||||
standards="",
|
||||
integrations="",
|
||||
region="eu-west-1",
|
||||
)
|
||||
]
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.securityhub.securityhub_service.SecurityHub",
|
||||
new=securityhub_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.securityhub.securityhub_enabled.securityhub_enabled import (
|
||||
securityhub_enabled,
|
||||
)
|
||||
|
||||
check = securityhub_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Security Hub is enabled but without any standard or integration"
|
||||
)
|
||||
assert result[0].resource_id == "default"
|
||||
|
||||
@@ -30,6 +30,12 @@ def mock_make_api_call(self, operation_name, kwarg):
|
||||
},
|
||||
]
|
||||
}
|
||||
if operation_name == "ListEnabledProductsForImport":
|
||||
return {
|
||||
"ProductSubscriptions": [
|
||||
"arn:aws:securityhub:us-east-1:0123456789012:product-subscription/prowler/prowler",
|
||||
]
|
||||
}
|
||||
if operation_name == "DescribeHub":
|
||||
return {
|
||||
"HubArn": "arn:aws:securityhub:us-east-1:0123456789012:hub/default",
|
||||
@@ -74,4 +80,5 @@ class Test_SecurityHub_Service:
|
||||
== "arn:aws:securityhub:us-east-1:0123456789012:hub/default"
|
||||
)
|
||||
assert securityhub.securityhubs[0].id == "default"
|
||||
assert securityhub.securityhubs[0].standards == " cis-aws-foundations-benchmark"
|
||||
assert securityhub.securityhubs[0].standards == "cis-aws-foundations-benchmark "
|
||||
assert securityhub.securityhubs[0].integrations == "prowler "
|
||||
|
||||
Reference in New Issue
Block a user