mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"Provider": "cloudflare",
|
||||
"CheckID": "zones_security_level",
|
||||
"CheckTitle": "Security level is set to medium or higher for adequate threat protection",
|
||||
"CheckType": [],
|
||||
"ServiceName": "zones",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Zone",
|
||||
"Description": "**Cloudflare zones** are assessed for **Security Level** configuration by checking if it is set to `medium`, `high`, or `under_attack` to ensure adequate **threat protection** based on visitor reputation scores.",
|
||||
"Risk": "A **low security level** allows more potentially malicious traffic to reach your origin.\n- **Confidentiality**: increased exposure to credential stuffing and data exfiltration attempts\n- **Integrity**: higher risk of successful web application attacks\n- **Availability**: greater attack surface for DDoS and resource exhaustion attacks",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://developers.cloudflare.com/waf/tools/security-level/"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Log in to the Cloudflare dashboard and select your account and domain\n2. Go to Security > Settings\n3. Scroll to Security Level\n4. Select Medium or High from the dropdown based on your threat tolerance\n5. Use Under Attack mode only during active attacks (temporarily)",
|
||||
"Terraform": "```hcl\n# Set security level to medium or higher\nresource \"cloudflare_zone_settings_override\" \"security_level\" {\n zone_id = \"<ZONE_ID>\"\n settings {\n security_level = \"medium\" # Options: off, essentially_off, low, medium, high, under_attack\n }\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Set **Security Level** to `Medium` or `High` based on your threat tolerance.\n- **Low**: minimal protection, may allow suspicious traffic\n- **Medium**: balanced protection for most sites (recommended baseline)\n- **High**: aggressive filtering, may challenge more visitors\n- **Under Attack**: use only during active attacks, impacts user experience",
|
||||
"Url": "https://hub.prowler.com/checks/cloudflare/zones_security_level"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"internet-exposed"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Avoid keeping Under Attack mode enabled permanently as it presents challenges to all visitors. Use Page Rules for path-specific security levels if needed."
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"Provider": "cloudflare",
|
||||
"CheckID": "zones_security_under_attack_disabled",
|
||||
"CheckTitle": "Under Attack Mode is disabled during normal operations",
|
||||
"CheckType": [],
|
||||
"ServiceName": "zones",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "low",
|
||||
"ResourceType": "Zone",
|
||||
"Description": "**Cloudflare zones** are assessed for **Under Attack Mode** configuration by checking if it is disabled during normal operations, as this mode performs additional security checks including an **interstitial JavaScript challenge page** that significantly impacts user experience.",
|
||||
"Risk": "Keeping **Under Attack Mode** permanently enabled causes operational issues.\n- **Availability**: all visitors face a 5-second interstitial challenge page before accessing the site\n- **Accessibility**: visitors without JavaScript support cannot access the site at all\n- **User Experience**: legitimate users experience unnecessary delays and third-party analytics show degraded performance",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://developers.cloudflare.com/fundamentals/reference/under-attack-mode/",
|
||||
"https://developers.cloudflare.com/waf/tools/security-level/"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Log in to the Cloudflare dashboard and select your account and domain\n2. Go to Security > Settings\n3. Under 'Under Attack Mode', toggle it OFF\n4. Consider setting Security Level to 'High' for continued protection without the interstitial",
|
||||
"Terraform": "```hcl\n# Set security level to high instead of under_attack\nresource \"cloudflare_zone_settings_override\" \"security_settings\" {\n zone_id = \"<ZONE_ID>\"\n settings {\n security_level = \"high\" # Use high instead of under_attack for normal operations\n }\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable **Under Attack Mode** when not actively under a DDoS attack.\n- Use it only as a **last resort** during active layer 7 DDoS attacks\n- For ongoing protection, use **Security Level** settings (Low, Medium, High)\n- Configure specific **WAF rules** for targeted protection without impacting all users",
|
||||
"Url": "https://hub.prowler.com/checks/cloudflare/zones_security_under_attack_disabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"internet-exposed"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Under Attack Mode is designed as a temporary measure during active attacks. The interstitial challenge page validates visitors using JavaScript, blocking automated attacks while allowing legitimate users through after a brief delay."
|
||||
}
|
||||
@@ -2,10 +2,9 @@ from prowler.lib.check.models import Check, CheckReportCloudflare
|
||||
from prowler.providers.cloudflare.services.zones.zones_client import zones_client
|
||||
|
||||
|
||||
class zones_security_level(Check):
|
||||
class zones_security_under_attack_disabled(Check):
|
||||
def execute(self) -> list[CheckReportCloudflare]:
|
||||
findings = []
|
||||
acceptable_levels = ["medium", "high", "under_attack"]
|
||||
|
||||
for zone in zones_client.zones.values():
|
||||
report = CheckReportCloudflare(
|
||||
@@ -13,15 +12,16 @@ class zones_security_level(Check):
|
||||
resource=zone,
|
||||
)
|
||||
security_level = (zone.settings.security_level or "").lower()
|
||||
if security_level in acceptable_levels:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Security level is set to '{security_level}' for zone {zone.name}."
|
||||
)
|
||||
else:
|
||||
|
||||
if security_level == "under_attack":
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Security level is set to '{security_level}' for zone {zone.name}."
|
||||
f"Zone {zone.name} has Under Attack Mode enabled."
|
||||
)
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Zone {zone.name} does not have Under Attack Mode enabled."
|
||||
)
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -11,7 +11,7 @@ from tests.providers.cloudflare.cloudflare_fixtures import (
|
||||
)
|
||||
|
||||
|
||||
class Test_zones_security_level:
|
||||
class Test_zones_security_under_attack_disabled:
|
||||
def test_no_zones(self):
|
||||
zones_client = mock.MagicMock
|
||||
zones_client.zones = {}
|
||||
@@ -22,18 +22,57 @@ class Test_zones_security_level:
|
||||
return_value=set_mocked_cloudflare_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level.zones_client",
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled.zones_client",
|
||||
new=zones_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level import (
|
||||
zones_security_level,
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled import (
|
||||
zones_security_under_attack_disabled,
|
||||
)
|
||||
|
||||
check = zones_security_level()
|
||||
check = zones_security_under_attack_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_zone_under_attack_mode_enabled(self):
|
||||
zones_client = mock.MagicMock
|
||||
zones_client.zones = {
|
||||
ZONE_ID: CloudflareZone(
|
||||
id=ZONE_ID,
|
||||
name=ZONE_NAME,
|
||||
status="active",
|
||||
paused=False,
|
||||
settings=CloudflareZoneSettings(
|
||||
security_level="under_attack",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_cloudflare_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled.zones_client",
|
||||
new=zones_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled import (
|
||||
zones_security_under_attack_disabled,
|
||||
)
|
||||
|
||||
check = zones_security_under_attack_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].resource_id == ZONE_ID
|
||||
assert result[0].resource_name == ZONE_NAME
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Zone {ZONE_NAME} has Under Attack Mode enabled."
|
||||
)
|
||||
|
||||
def test_zone_security_level_high(self):
|
||||
zones_client = mock.MagicMock
|
||||
zones_client.zones = {
|
||||
@@ -54,21 +93,22 @@ class Test_zones_security_level:
|
||||
return_value=set_mocked_cloudflare_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level.zones_client",
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled.zones_client",
|
||||
new=zones_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level import (
|
||||
zones_security_level,
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled import (
|
||||
zones_security_under_attack_disabled,
|
||||
)
|
||||
|
||||
check = zones_security_level()
|
||||
check = zones_security_under_attack_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].resource_id == ZONE_ID
|
||||
assert result[0].resource_name == ZONE_NAME
|
||||
assert result[0].status == "PASS"
|
||||
assert "high" in result[0].status_extended
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Zone {ZONE_NAME} does not have Under Attack Mode enabled."
|
||||
)
|
||||
|
||||
def test_zone_security_level_medium(self):
|
||||
zones_client = mock.MagicMock
|
||||
@@ -90,53 +130,18 @@ class Test_zones_security_level:
|
||||
return_value=set_mocked_cloudflare_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level.zones_client",
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled.zones_client",
|
||||
new=zones_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level import (
|
||||
zones_security_level,
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled import (
|
||||
zones_security_under_attack_disabled,
|
||||
)
|
||||
|
||||
check = zones_security_level()
|
||||
check = zones_security_under_attack_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert "medium" in result[0].status_extended
|
||||
|
||||
def test_zone_security_level_under_attack(self):
|
||||
zones_client = mock.MagicMock
|
||||
zones_client.zones = {
|
||||
ZONE_ID: CloudflareZone(
|
||||
id=ZONE_ID,
|
||||
name=ZONE_NAME,
|
||||
status="active",
|
||||
paused=False,
|
||||
settings=CloudflareZoneSettings(
|
||||
security_level="under_attack",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_cloudflare_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level.zones_client",
|
||||
new=zones_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level import (
|
||||
zones_security_level,
|
||||
)
|
||||
|
||||
check = zones_security_level()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert "under_attack" in result[0].status_extended
|
||||
|
||||
def test_zone_security_level_low(self):
|
||||
zones_client = mock.MagicMock
|
||||
@@ -158,53 +163,18 @@ class Test_zones_security_level:
|
||||
return_value=set_mocked_cloudflare_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level.zones_client",
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled.zones_client",
|
||||
new=zones_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level import (
|
||||
zones_security_level,
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled import (
|
||||
zones_security_under_attack_disabled,
|
||||
)
|
||||
|
||||
check = zones_security_level()
|
||||
check = zones_security_under_attack_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert "low" in result[0].status_extended
|
||||
|
||||
def test_zone_security_level_essentially_off(self):
|
||||
zones_client = mock.MagicMock
|
||||
zones_client.zones = {
|
||||
ZONE_ID: CloudflareZone(
|
||||
id=ZONE_ID,
|
||||
name=ZONE_NAME,
|
||||
status="active",
|
||||
paused=False,
|
||||
settings=CloudflareZoneSettings(
|
||||
security_level="essentially_off",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_cloudflare_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level.zones_client",
|
||||
new=zones_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level import (
|
||||
zones_security_level,
|
||||
)
|
||||
|
||||
check = zones_security_level()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert "essentially_off" in result[0].status_extended
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_zone_security_level_none(self):
|
||||
zones_client = mock.MagicMock
|
||||
@@ -226,15 +196,15 @@ class Test_zones_security_level:
|
||||
return_value=set_mocked_cloudflare_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level.zones_client",
|
||||
"prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled.zones_client",
|
||||
new=zones_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_level.zones_security_level import (
|
||||
zones_security_level,
|
||||
from prowler.providers.cloudflare.services.zones.zones_security_under_attack_disabled.zones_security_under_attack_disabled import (
|
||||
zones_security_under_attack_disabled,
|
||||
)
|
||||
|
||||
check = zones_security_level()
|
||||
check = zones_security_under_attack_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].status == "PASS"
|
||||
Reference in New Issue
Block a user