refactor: remove waf and firewall services

Checks and logic were moved to zones service
This commit is contained in:
HugoPBrito
2025-12-16 12:00:11 +01:00
parent 557b5aa480
commit 9d658ef531
21 changed files with 281 additions and 349 deletions

View File

@@ -1,4 +0,0 @@
from prowler.providers.cloudflare.services.firewall.firewall_service import Firewall
from prowler.providers.common.provider import Provider
firewall_client = Firewall(Provider.get_global_provider())

View File

@@ -1,30 +0,0 @@
{
"Provider": "cloudflare",
"CheckID": "firewall_has_blocking_rules",
"CheckTitle": "Firewall rules use blocking actions to protect against threats",
"CheckType": [],
"ServiceName": "firewall",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "medium",
"ResourceType": "FirewallRule",
"Description": "Verifies that Cloudflare firewall rules use blocking actions (block, challenge, js_challenge, managed_challenge) to actively protect against threats rather than only logging.",
"Risk": "Firewall rules configured only for logging provide visibility but no protection. Malicious traffic reaches the origin server, enabling attacks such as credential stuffing, application exploits, and data exfiltration.",
"RelatedUrl": "https://developers.cloudflare.com/waf/custom-rules/",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": "resource \"cloudflare_ruleset\" \"example\" {\n zone_id = var.zone_id\n name = \"Block malicious requests\"\n kind = \"zone\"\n phase = \"http_request_firewall_custom\"\n rules {\n action = \"block\"\n expression = \"(ip.geoip.country eq \\\"XX\\\")\"\n description = \"Block traffic from country XX\"\n }\n}"
},
"Recommendation": {
"Text": "Configure firewall rules with blocking actions to enforce security policies. Use challenge actions for suspicious traffic and block actions for known malicious patterns following the principle of least privilege.",
"Url": "https://hub.prowler.com/checks/firewall_has_blocking_rules"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Blocking actions include: block, challenge, js_challenge, managed_challenge."
}

View File

@@ -1,30 +0,0 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.firewall.firewall_client import (
firewall_client,
)
BLOCKING_ACTIONS = {"block", "challenge", "js_challenge", "managed_challenge"}
class firewall_has_blocking_rules(Check):
def execute(self) -> list[CheckReportCloudflare]:
findings = []
for rule in firewall_client.rules:
report = CheckReportCloudflare(
metadata=self.metadata(),
resource=rule,
zone=rule.zone,
)
if rule.action in BLOCKING_ACTIONS:
report.status = "PASS"
report.status_extended = (
f"Firewall rule '{rule.name}' uses blocking action '{rule.action}'."
)
else:
report.status = "FAIL"
report.status_extended = f"Firewall rule '{rule.name}' uses non-blocking action '{rule.action}'."
findings.append(report)
return findings

View File

@@ -1,30 +0,0 @@
{
"Provider": "cloudflare",
"CheckID": "firewall_rate_limiting_configured",
"CheckTitle": "Firewall rule is configured as a rate limiting rule",
"CheckType": [],
"ServiceName": "firewall",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "informational",
"ResourceType": "FirewallRule",
"Description": "Identifies firewall rules configured as rate limiting rules to protect against volumetric attacks, brute force attempts, and API abuse.",
"Risk": "Without rate limiting rules, applications are vulnerable to DDoS attacks, credential brute forcing, and API abuse that can exhaust resources, compromise accounts, or cause service degradation.",
"RelatedUrl": "https://developers.cloudflare.com/waf/rate-limiting-rules/",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": "resource \"cloudflare_ruleset\" \"rate_limit\" {\n zone_id = var.zone_id\n name = \"Rate limiting\"\n kind = \"zone\"\n phase = \"http_ratelimit\"\n rules {\n action = \"block\"\n ratelimit {\n characteristics = [\"ip.src\"]\n period = 60\n requests_per_period = 100\n mitigation_timeout = 600\n }\n expression = \"true\"\n description = \"Rate limit all requests\"\n }\n}"
},
"Recommendation": {
"Text": "Implement rate limiting rules as part of a defense in depth strategy. Configure appropriate thresholds based on expected traffic patterns to protect authentication endpoints, APIs, and resource-intensive operations.",
"Url": "https://hub.prowler.com/checks/firewall_rate_limiting_configured"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Rate limiting rules are in the http_ratelimit phase."
}

View File

@@ -1,32 +0,0 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.firewall.firewall_client import (
firewall_client,
)
class firewall_rate_limiting_configured(Check):
def execute(self) -> list[CheckReportCloudflare]:
findings = []
for rule in firewall_client.rules:
# Only evaluate rate limit phase rules
if rule.phase != "http_ratelimit":
continue
report = CheckReportCloudflare(
metadata=self.metadata(),
resource=rule,
zone=rule.zone,
)
if rule.enabled:
report.status = "PASS"
report.status_extended = f"Rate limiting rule '{rule.name}' is enabled."
else:
report.status = "FAIL"
report.status_extended = (
f"Rate limiting rule '{rule.name}' is disabled."
)
findings.append(report)
return findings

View File

@@ -1,86 +0,0 @@
from typing import Optional
from pydantic.v1 import BaseModel
from prowler.lib.logger import logger
from prowler.providers.cloudflare.lib.service.service import CloudflareService
from prowler.providers.cloudflare.models import CloudflareZone
class CloudflareFirewallRule(BaseModel):
"""Represents a firewall rule from custom rulesets."""
id: str
name: str = ""
description: Optional[str] = None
action: Optional[str] = None
enabled: bool = True
expression: Optional[str] = None
phase: Optional[str] = None
zone: CloudflareZone
class Config:
arbitrary_types_allowed = True
class Firewall(CloudflareService):
"""Collect Cloudflare firewall rules for each zone using rulesets API."""
def __init__(self, provider):
super().__init__(__class__.__name__, provider)
self.rules: list[CloudflareFirewallRule] = []
self.__threading_call__(self._list_firewall_rules)
def _list_firewall_rules(self, zone: CloudflareZone):
"""List firewall rules from custom rulesets for a zone."""
seen_ruleset_ids: set[str] = set()
try:
for ruleset in self.client.rulesets.list(zone_id=zone.id):
ruleset_id = getattr(ruleset, "id", "")
if ruleset_id in seen_ruleset_ids:
break
seen_ruleset_ids.add(ruleset_id)
ruleset_phase = getattr(ruleset, "phase", "")
if ruleset_phase in [
"http_request_firewall_custom",
"http_ratelimit",
"http_request_firewall_managed",
]:
try:
ruleset_detail = self.client.rulesets.get(
ruleset_id=ruleset_id, zone_id=zone.id
)
rules = getattr(ruleset_detail, "rules", []) or []
seen_rule_ids: set[str] = set()
for rule in rules:
rule_id = getattr(rule, "id", "")
if rule_id in seen_rule_ids:
break
seen_rule_ids.add(rule_id)
try:
self.rules.append(
CloudflareFirewallRule(
id=rule_id,
name=getattr(rule, "description", "")
or rule_id,
description=getattr(rule, "description", None),
action=getattr(rule, "action", None),
enabled=getattr(rule, "enabled", True),
expression=getattr(rule, "expression", None),
phase=ruleset_phase,
zone=zone,
)
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

View File

@@ -1,4 +0,0 @@
from prowler.providers.cloudflare.services.waf.waf_service import WAF
from prowler.providers.common.provider import Provider
waf_client = WAF(Provider.get_global_provider())

View File

@@ -1,32 +0,0 @@
{
"Provider": "cloudflare",
"CheckID": "waf_owasp_enabled",
"CheckTitle": "OWASP managed WAF rulesets are enabled for the zone",
"CheckType": [],
"ServiceName": "waf",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "high",
"ResourceType": "WAFRuleset",
"Description": "Verifies that OWASP managed WAF rulesets are enabled to protect against common web application vulnerabilities including SQL injection, XSS, and other OWASP Top 10 threats.",
"Risk": "Without OWASP managed rulesets, web applications are exposed to well-known attack vectors including SQL injection, cross-site scripting (XSS), and remote code execution that could lead to data breaches or system compromise.",
"RelatedUrl": "https://developers.cloudflare.com/waf/managed-rules/",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable OWASP Core Ruleset managed rules as part of a defense in depth strategy. Regularly review and tune rule sensitivity based on application requirements while maintaining protection against known attack patterns.",
"Url": "https://hub.prowler.com/checks/waf_owasp_enabled"
}
},
"Categories": [
"vulnerabilities"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

View File

@@ -1,34 +0,0 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.waf.waf_client import waf_client
class waf_owasp_enabled(Check):
def execute(self) -> list[CheckReportCloudflare]:
findings = []
for ruleset in waf_client.rulesets:
report = CheckReportCloudflare(
metadata=self.metadata(),
resource=ruleset,
zone=ruleset.zone,
)
# Check if this is an OWASP managed ruleset
is_owasp = (
"owasp" in (ruleset.name or "").lower()
or ruleset.phase == "http_request_firewall_managed"
)
if is_owasp:
report.status = "PASS"
report.status_extended = (
f"WAF ruleset '{ruleset.name}' is an OWASP managed ruleset."
)
else:
report.status = "FAIL"
report.status_extended = (
f"WAF ruleset '{ruleset.name}' is not an OWASP managed ruleset."
)
findings.append(report)
return findings

View File

@@ -1,59 +0,0 @@
from typing import Optional
from pydantic.v1 import BaseModel
from prowler.lib.logger import logger
from prowler.providers.cloudflare.lib.service.service import CloudflareService
from prowler.providers.cloudflare.models import CloudflareZone
class CloudflareWAFRuleset(BaseModel):
"""Represents a WAF ruleset (managed rules) for a zone."""
id: str
name: str
kind: Optional[str] = None
phase: Optional[str] = None
enabled: bool = True
zone: CloudflareZone
class Config:
arbitrary_types_allowed = True
class WAF(CloudflareService):
"""Collect WAF ruleset information for Cloudflare zones using rulesets API."""
def __init__(self, provider):
super().__init__(__class__.__name__, provider)
self.rulesets: list[CloudflareWAFRuleset] = []
self.__threading_call__(self._list_waf_rulesets)
def _list_waf_rulesets(self, zone: CloudflareZone):
"""List WAF rulesets for a zone using the new rulesets API."""
seen_ids: set[str] = set()
try:
for ruleset in self.client.rulesets.list(zone_id=zone.id):
ruleset_id = getattr(ruleset, "id", "")
if ruleset_id in seen_ids:
break
seen_ids.add(ruleset_id)
try:
self.rulesets.append(
CloudflareWAFRuleset(
id=ruleset_id,
name=getattr(ruleset, "name", ""),
kind=getattr(ruleset, "kind", None),
phase=getattr(ruleset, "phase", None),
enabled=True,
zone=zone,
)
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

View File

@@ -0,0 +1,35 @@
{
"Provider": "cloudflare",
"CheckID": "zones_firewall_blocking_rules_configured",
"CheckTitle": "Firewall rules use blocking actions to protect against threats",
"CheckType": [],
"ServiceName": "zones",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "medium",
"ResourceType": "Zone",
"Description": "**Cloudflare zones** are assessed for **firewall blocking rules** by checking if custom rules use block, challenge, js_challenge, or managed_challenge actions to actively protect against threats rather than only logging.",
"Risk": "Firewall rules configured only for **logging** provide visibility but no protection.\n- **Confidentiality**: malicious traffic can access and exfiltrate sensitive data\n- **Integrity**: application exploits can modify data without being blocked\n- **Availability**: credential stuffing and abuse attacks reach the origin unimpeded",
"RelatedUrl": "",
"AdditionalURLs": [
"https://developers.cloudflare.com/waf/custom-rules/"
],
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "1. Log in to the Cloudflare dashboard and select your account and domain\n2. Go to Security > WAF > Custom rules\n3. Review existing rules and their actions\n4. Update rules to use blocking actions (Block, Challenge, JS Challenge, Managed Challenge)\n5. Test rules in log mode first, then enable blocking actions",
"Terraform": "```hcl\n# Configure firewall rule with blocking action\nresource \"cloudflare_ruleset\" \"blocking_rule\" {\n zone_id = \"<ZONE_ID>\"\n name = \"Block malicious requests\"\n kind = \"zone\"\n phase = \"http_request_firewall_custom\"\n rules {\n action = \"block\" # Actively blocks matching traffic\n expression = \"(ip.geoip.country eq \\\"XX\\\")\"\n description = \"Block traffic from high-risk country\"\n }\n}\n```"
},
"Recommendation": {
"Text": "Configure **firewall rules** with blocking actions to enforce security policies.\n- Use challenge actions for suspicious traffic to verify human visitors\n- Use block actions for known malicious patterns and high-risk sources\n- Test rules in log mode before enabling blocking to avoid false positives\n- Follow the principle of least privilege in rule configuration",
"Url": "https://hub.prowler.com/checks/cloudflare/zones_firewall_blocking_rules_configured"
}
},
"Categories": [
"internet-exposed"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Blocking actions include: block, challenge, js_challenge, managed_challenge. Log-only rules provide visibility but do not prevent attacks."
}

View File

@@ -0,0 +1,35 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.zones.zones_client import zones_client
BLOCKING_ACTIONS = {"block", "challenge", "js_challenge", "managed_challenge"}
class zones_firewall_blocking_rules_configured(Check):
def execute(self) -> list[CheckReportCloudflare]:
findings = []
for zone in zones_client.zones.values():
report = CheckReportCloudflare(
metadata=self.metadata(),
resource=zone,
)
# Find blocking rules for this zone
blocking_rules = [
rule for rule in zone.firewall_rules if rule.action in BLOCKING_ACTIONS
]
if blocking_rules:
report.status = "PASS"
report.status_extended = (
f"Zone {zone.name} has firewall rules with blocking actions "
f"({len(blocking_rules)} rule(s))."
)
else:
report.status = "FAIL"
report.status_extended = (
f"Zone {zone.name} has no firewall rules with blocking actions."
)
findings.append(report)
return findings

View File

@@ -1,7 +1,4 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.firewall.firewall_client import (
firewall_client,
)
from prowler.providers.cloudflare.services.zones.zones_client import zones_client
@@ -9,7 +6,7 @@ class zones_rate_limiting_enabled(Check):
def execute(self) -> list[CheckReportCloudflare]:
findings = []
for zone in zones_client.zones:
for zone in zones_client.zones.values():
report = CheckReportCloudflare(
metadata=self.metadata(),
resource=zone,
@@ -18,10 +15,8 @@ class zones_rate_limiting_enabled(Check):
# Find rate limiting rules for this zone
rate_limit_rules = [
rule
for rule in firewall_client.rules
if rule.zone.id == zone.id
and rule.phase == "http_ratelimit"
and rule.enabled
for rule in zone.firewall_rules
if rule.phase == "http_ratelimit" and rule.enabled
]
if rate_limit_rules:

View File

@@ -7,6 +7,34 @@ from prowler.providers.cloudflare.lib.service.service import CloudflareService
from prowler.providers.cloudflare.models import CloudflareAccount
class CloudflareFirewallRule(BaseModel):
"""Represents a firewall rule from custom rulesets."""
id: str
name: str = ""
description: Optional[str] = None
action: Optional[str] = None
enabled: bool = True
expression: Optional[str] = None
phase: Optional[str] = None
class Config:
arbitrary_types_allowed = True
class CloudflareWAFRuleset(BaseModel):
"""Represents a WAF ruleset (managed rules) for a zone."""
id: str
name: str
kind: Optional[str] = None
phase: Optional[str] = None
enabled: bool = True
class Config:
arbitrary_types_allowed = True
class Zones(CloudflareService):
"""Retrieve Cloudflare zones with security-relevant settings."""
@@ -16,6 +44,8 @@ class Zones(CloudflareService):
self._list_zones()
self._get_zones_settings()
self._get_zones_dnssec()
self._get_zones_firewall_rules()
self._get_zones_waf_rulesets()
def _list_zones(self) -> None:
"""List all Cloudflare zones with their basic information."""
@@ -107,6 +137,109 @@ class Zones(CloudflareService):
f"{zone.id} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _get_zones_firewall_rules(self) -> None:
"""Get firewall rules for all zones."""
logger.info("Zones - Getting firewall rules...")
for zone in self.zones.values():
try:
self._get_zone_firewall_rules(zone)
except Exception as error:
logger.error(
f"{zone.id} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _get_zone_firewall_rules(self, zone: "CloudflareZone") -> None:
"""List firewall rules from custom rulesets for a zone."""
seen_ruleset_ids: set[str] = set()
try:
for ruleset in self.client.rulesets.list(zone_id=zone.id):
ruleset_id = getattr(ruleset, "id", "")
if ruleset_id in seen_ruleset_ids:
break
seen_ruleset_ids.add(ruleset_id)
ruleset_phase = getattr(ruleset, "phase", "")
if ruleset_phase in [
"http_request_firewall_custom",
"http_ratelimit",
"http_request_firewall_managed",
]:
try:
ruleset_detail = self.client.rulesets.get(
ruleset_id=ruleset_id, zone_id=zone.id
)
rules = getattr(ruleset_detail, "rules", []) or []
seen_rule_ids: set[str] = set()
for rule in rules:
rule_id = getattr(rule, "id", "")
if rule_id in seen_rule_ids:
break
seen_rule_ids.add(rule_id)
try:
zone.firewall_rules.append(
CloudflareFirewallRule(
id=rule_id,
name=getattr(rule, "description", "")
or rule_id,
description=getattr(rule, "description", None),
action=getattr(rule, "action", None),
enabled=getattr(rule, "enabled", True),
expression=getattr(rule, "expression", None),
phase=ruleset_phase,
)
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _get_zones_waf_rulesets(self) -> None:
"""Get WAF rulesets for all zones."""
logger.info("Zones - Getting WAF rulesets...")
for zone in self.zones.values():
try:
self._get_zone_waf_rulesets(zone)
except Exception as error:
logger.error(
f"{zone.id} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _get_zone_waf_rulesets(self, zone: "CloudflareZone") -> None:
"""List WAF rulesets for a zone using the rulesets API."""
seen_ids: set[str] = set()
try:
for ruleset in self.client.rulesets.list(zone_id=zone.id):
ruleset_id = getattr(ruleset, "id", "")
if ruleset_id in seen_ids:
break
seen_ids.add(ruleset_id)
try:
zone.waf_rulesets.append(
CloudflareWAFRuleset(
id=ruleset_id,
name=getattr(ruleset, "name", ""),
kind=getattr(ruleset, "kind", None),
phase=getattr(ruleset, "phase", None),
enabled=True,
)
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _get_zone_setting(self, zone_id: str, setting_id: str):
"""Get a single zone setting by ID."""
try:
@@ -241,3 +374,5 @@ class CloudflareZone(BaseModel):
plan: Optional[str] = None
settings: CloudflareZoneSettings = Field(default_factory=CloudflareZoneSettings)
dnssec_status: Optional[str] = None
firewall_rules: list[CloudflareFirewallRule] = Field(default_factory=list)
waf_rulesets: list[CloudflareWAFRuleset] = Field(default_factory=list)

View File

@@ -0,0 +1,35 @@
{
"Provider": "cloudflare",
"CheckID": "zones_waf_owasp_ruleset_enabled",
"CheckTitle": "OWASP managed WAF rulesets are enabled for the zone",
"CheckType": [],
"ServiceName": "zones",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "high",
"ResourceType": "Zone",
"Description": "**Cloudflare zones** are assessed for **OWASP managed rulesets** by checking if they are enabled to protect against common web application vulnerabilities including **SQL injection**, **XSS**, and other **OWASP Top 10** threats.",
"Risk": "Without **OWASP managed rulesets**, web applications are exposed to well-known attack vectors.\n- **Confidentiality**: SQL injection attacks can exfiltrate sensitive database contents\n- **Integrity**: XSS attacks can modify page content and steal session tokens\n- **Availability**: remote code execution can compromise server availability",
"RelatedUrl": "",
"AdditionalURLs": [
"https://developers.cloudflare.com/waf/managed-rules/"
],
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "1. Log in to the Cloudflare dashboard and select your account and domain\n2. Go to Security > WAF > Managed rules\n3. Enable the Cloudflare OWASP Core Ruleset\n4. Review and configure rule sensitivity based on your application\n5. Monitor WAF analytics to tune rules and reduce false positives",
"Terraform": "```hcl\n# Enable OWASP managed WAF rulesets\nresource \"cloudflare_ruleset\" \"waf_owasp\" {\n zone_id = \"<ZONE_ID>\"\n name = \"OWASP Managed Rules\"\n kind = \"zone\"\n phase = \"http_request_firewall_managed\"\n rules {\n action = \"execute\"\n action_parameters {\n id = \"4814384a9e5d4991b9815dcfc25d2f1f\" # Cloudflare OWASP Core Ruleset\n }\n expression = \"true\"\n description = \"Execute Cloudflare OWASP Core Ruleset\"\n }\n}\n```"
},
"Recommendation": {
"Text": "Enable **OWASP Core Ruleset** managed rules as part of a defense in depth strategy.\n- Protects against OWASP Top 10 vulnerabilities including SQLi and XSS\n- Regularly review and tune rule sensitivity based on application requirements\n- Monitor WAF analytics to identify and address false positives\n- Combine with custom rules for application-specific protection",
"Url": "https://hub.prowler.com/checks/cloudflare/zones_waf_owasp_ruleset_enabled"
}
},
"Categories": [
"vulnerabilities"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "OWASP managed rulesets are available on Pro, Business, and Enterprise plans. The Cloudflare OWASP Core Ruleset provides protection against common web application vulnerabilities."
}

View File

@@ -0,0 +1,38 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.zones.zones_client import zones_client
class zones_waf_owasp_ruleset_enabled(Check):
def execute(self) -> list[CheckReportCloudflare]:
findings = []
for zone in zones_client.zones.values():
report = CheckReportCloudflare(
metadata=self.metadata(),
resource=zone,
)
# Find OWASP managed rulesets for this zone
owasp_rulesets = [
ruleset
for ruleset in zone.waf_rulesets
if (
"owasp" in (ruleset.name or "").lower()
or ruleset.phase == "http_request_firewall_managed"
)
]
if owasp_rulesets:
report.status = "PASS"
report.status_extended = (
f"Zone {zone.name} has OWASP managed WAF ruleset enabled "
f"({len(owasp_rulesets)} ruleset(s))."
)
else:
report.status = "FAIL"
report.status_extended = (
f"Zone {zone.name} does not have OWASP managed WAF ruleset enabled."
)
findings.append(report)
return findings