mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat(api): add Cloudflare provider support (#9907)
This commit is contained in:
committed by
GitHub
parent
d01f399cb2
commit
b1f9971617
@@ -6,6 +6,7 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- Cloudflare provider support [(#9907)](https://github.com/prowler-cloud/prowler/pull/9907)
|
||||
- Attack Paths: Bedrock Code Interpreter and AttachRolePolicy privilege escalation queries [(#9885)](https://github.com/prowler-cloud/prowler/pull/9885)
|
||||
- `provider_id` and `provider_id__in` filters for resources endpoints (`GET /resources` and `GET /resources/metadata/latest`) [(#9864)](https://github.com/prowler-cloud/prowler/pull/9864)
|
||||
- Added memory optimizations for large compliance report generation [(#9444)](https://github.com/prowler-cloud/prowler/pull/9444)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Generated by Django migration for Cloudflare provider support
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
import api.db_utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("api", "0074_findings_fail_new_index_parent"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="provider",
|
||||
name="provider",
|
||||
field=api.db_utils.ProviderEnumField(
|
||||
choices=[
|
||||
("aws", "AWS"),
|
||||
("azure", "Azure"),
|
||||
("gcp", "GCP"),
|
||||
("kubernetes", "Kubernetes"),
|
||||
("m365", "M365"),
|
||||
("github", "GitHub"),
|
||||
("mongodbatlas", "MongoDB Atlas"),
|
||||
("iac", "IaC"),
|
||||
("oraclecloud", "Oracle Cloud Infrastructure"),
|
||||
("alibabacloud", "Alibaba Cloud"),
|
||||
("cloudflare", "Cloudflare"),
|
||||
],
|
||||
default="aws",
|
||||
),
|
||||
),
|
||||
migrations.RunSQL(
|
||||
"ALTER TYPE provider ADD VALUE IF NOT EXISTS 'cloudflare';",
|
||||
reverse_sql=migrations.RunSQL.noop,
|
||||
),
|
||||
]
|
||||
@@ -287,6 +287,7 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
IAC = "iac", _("IaC")
|
||||
ORACLECLOUD = "oraclecloud", _("Oracle Cloud Infrastructure")
|
||||
ALIBABACLOUD = "alibabacloud", _("Alibaba Cloud")
|
||||
CLOUDFLARE = "cloudflare", _("Cloudflare")
|
||||
|
||||
@staticmethod
|
||||
def validate_aws_uid(value):
|
||||
@@ -400,6 +401,15 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
pointer="/data/attributes/uid",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def validate_cloudflare_uid(value):
|
||||
if not re.match(r"^[a-f0-9]{32}$", value):
|
||||
raise ModelValidationError(
|
||||
detail="Cloudflare Account ID must be a 32-character hexadecimal string.",
|
||||
code="cloudflare-uid",
|
||||
pointer="/data/attributes/uid",
|
||||
)
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
inserted_at = models.DateTimeField(auto_now_add=True, editable=False)
|
||||
updated_at = models.DateTimeField(auto_now=True, editable=False)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@ from prowler.providers.alibabacloud.alibabacloud_provider import AlibabacloudPro
|
||||
from prowler.providers.aws.aws_provider import AwsProvider
|
||||
from prowler.providers.aws.lib.security_hub.security_hub import SecurityHubConnection
|
||||
from prowler.providers.azure.azure_provider import AzureProvider
|
||||
from prowler.providers.cloudflare.cloudflare_provider import CloudflareProvider
|
||||
from prowler.providers.gcp.gcp_provider import GcpProvider
|
||||
from prowler.providers.github.github_provider import GithubProvider
|
||||
from prowler.providers.iac.iac_provider import IacProvider
|
||||
@@ -118,6 +119,7 @@ class TestReturnProwlerProvider:
|
||||
(Provider.ProviderChoices.ORACLECLOUD.value, OraclecloudProvider),
|
||||
(Provider.ProviderChoices.IAC.value, IacProvider),
|
||||
(Provider.ProviderChoices.ALIBABACLOUD.value, AlibabacloudProvider),
|
||||
(Provider.ProviderChoices.CLOUDFLARE.value, CloudflareProvider),
|
||||
],
|
||||
)
|
||||
def test_return_prowler_provider(self, provider_type, expected_provider):
|
||||
@@ -221,6 +223,10 @@ class TestGetProwlerProviderKwargs:
|
||||
Provider.ProviderChoices.MONGODBATLAS.value,
|
||||
{"atlas_organization_id": "provider_uid"},
|
||||
),
|
||||
(
|
||||
Provider.ProviderChoices.CLOUDFLARE.value,
|
||||
{"filter_accounts": ["provider_uid"]},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_get_prowler_provider_kwargs(self, provider_type, expected_extra_kwargs):
|
||||
|
||||
@@ -1174,6 +1174,11 @@ class TestProviderViewSet:
|
||||
"uid": "1234567890123456",
|
||||
"alias": "Alibaba Cloud Account",
|
||||
},
|
||||
{
|
||||
"provider": "cloudflare",
|
||||
"uid": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
|
||||
"alias": "Cloudflare Account",
|
||||
},
|
||||
]
|
||||
),
|
||||
)
|
||||
@@ -1553,6 +1558,46 @@ class TestProviderViewSet:
|
||||
"alibabacloud-uid",
|
||||
"uid",
|
||||
),
|
||||
# Cloudflare UID validation - too short (not 32 hex chars)
|
||||
(
|
||||
{
|
||||
"provider": "cloudflare",
|
||||
"uid": "abc123",
|
||||
"alias": "test",
|
||||
},
|
||||
"cloudflare-uid",
|
||||
"uid",
|
||||
),
|
||||
# Cloudflare UID validation - uppercase hex (must be lowercase)
|
||||
(
|
||||
{
|
||||
"provider": "cloudflare",
|
||||
"uid": "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4",
|
||||
"alias": "test",
|
||||
},
|
||||
"cloudflare-uid",
|
||||
"uid",
|
||||
),
|
||||
# Cloudflare UID validation - non-hex characters
|
||||
(
|
||||
{
|
||||
"provider": "cloudflare",
|
||||
"uid": "g1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
|
||||
"alias": "test",
|
||||
},
|
||||
"cloudflare-uid",
|
||||
"uid",
|
||||
),
|
||||
# Cloudflare UID validation - too long (33 chars)
|
||||
(
|
||||
{
|
||||
"provider": "cloudflare",
|
||||
"uid": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e",
|
||||
"alias": "test",
|
||||
},
|
||||
"cloudflare-uid",
|
||||
"uid",
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
@@ -1726,21 +1771,21 @@ class TestProviderViewSet:
|
||||
(
|
||||
"uid.icontains",
|
||||
"1",
|
||||
8,
|
||||
9,
|
||||
),
|
||||
("alias", "aws_testing_1", 1),
|
||||
("alias.icontains", "aws", 2),
|
||||
("inserted_at", TODAY, 9),
|
||||
("inserted_at", TODAY, 10),
|
||||
(
|
||||
"inserted_at.gte",
|
||||
"2024-01-01",
|
||||
9,
|
||||
10,
|
||||
),
|
||||
("inserted_at.lte", "2024-01-01", 0),
|
||||
(
|
||||
"updated_at.gte",
|
||||
"2024-01-01",
|
||||
9,
|
||||
10,
|
||||
),
|
||||
("updated_at.lte", "2024-01-01", 0),
|
||||
]
|
||||
@@ -2330,6 +2375,23 @@ class TestProviderSecretViewSet:
|
||||
"role_session_name": "ProwlerAuditSession",
|
||||
},
|
||||
),
|
||||
# Cloudflare with API Token
|
||||
(
|
||||
Provider.ProviderChoices.CLOUDFLARE.value,
|
||||
ProviderSecret.TypeChoices.STATIC,
|
||||
{
|
||||
"api_token": "fake-cloudflare-api-token-for-testing",
|
||||
},
|
||||
),
|
||||
# Cloudflare with API Key + Email
|
||||
(
|
||||
Provider.ProviderChoices.CLOUDFLARE.value,
|
||||
ProviderSecret.TypeChoices.STATIC,
|
||||
{
|
||||
"api_key": "fake-cloudflare-api-key-for-testing",
|
||||
"api_email": "user@example.com",
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_provider_secrets_create_valid(
|
||||
|
||||
@@ -24,6 +24,7 @@ if TYPE_CHECKING:
|
||||
)
|
||||
from prowler.providers.aws.aws_provider import AwsProvider
|
||||
from prowler.providers.azure.azure_provider import AzureProvider
|
||||
from prowler.providers.cloudflare.cloudflare_provider import CloudflareProvider
|
||||
from prowler.providers.gcp.gcp_provider import GcpProvider
|
||||
from prowler.providers.github.github_provider import GithubProvider
|
||||
from prowler.providers.iac.iac_provider import IacProvider
|
||||
@@ -91,7 +92,7 @@ def return_prowler_provider(
|
||||
provider (Provider): The provider object containing the provider type and associated secrets.
|
||||
|
||||
Returns:
|
||||
AlibabacloudProvider | AwsProvider | AzureProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OraclecloudProvider: The corresponding provider class.
|
||||
AlibabacloudProvider | AwsProvider | AzureProvider | CloudflareProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OraclecloudProvider: The corresponding provider class.
|
||||
|
||||
Raises:
|
||||
ValueError: If the provider type specified in `provider.provider` is not supported.
|
||||
@@ -145,6 +146,12 @@ def return_prowler_provider(
|
||||
)
|
||||
|
||||
prowler_provider = AlibabacloudProvider
|
||||
case Provider.ProviderChoices.CLOUDFLARE.value:
|
||||
from prowler.providers.cloudflare.cloudflare_provider import (
|
||||
CloudflareProvider,
|
||||
)
|
||||
|
||||
prowler_provider = CloudflareProvider
|
||||
case _:
|
||||
raise ValueError(f"Provider type {provider.provider} not supported")
|
||||
return prowler_provider
|
||||
@@ -196,6 +203,11 @@ def get_prowler_provider_kwargs(
|
||||
**prowler_provider_kwargs,
|
||||
"atlas_organization_id": provider.uid,
|
||||
}
|
||||
elif provider.provider == Provider.ProviderChoices.CLOUDFLARE.value:
|
||||
prowler_provider_kwargs = {
|
||||
**prowler_provider_kwargs,
|
||||
"filter_accounts": [provider.uid],
|
||||
}
|
||||
|
||||
if mutelist_processor:
|
||||
mutelist_content = mutelist_processor.configuration.get("Mutelist", {})
|
||||
@@ -213,6 +225,7 @@ def initialize_prowler_provider(
|
||||
AlibabacloudProvider
|
||||
| AwsProvider
|
||||
| AzureProvider
|
||||
| CloudflareProvider
|
||||
| GcpProvider
|
||||
| GithubProvider
|
||||
| IacProvider
|
||||
@@ -228,7 +241,7 @@ def initialize_prowler_provider(
|
||||
mutelist_processor (Processor): The mutelist processor object containing the mutelist configuration.
|
||||
|
||||
Returns:
|
||||
AlibabacloudProvider | AwsProvider | AzureProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OraclecloudProvider: An instance of the corresponding provider class
|
||||
AlibabacloudProvider | AwsProvider | AzureProvider | CloudflareProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OraclecloudProvider: An instance of the corresponding provider class
|
||||
initialized with the provider's secrets.
|
||||
"""
|
||||
prowler_provider = return_prowler_provider(provider)
|
||||
|
||||
@@ -346,6 +346,33 @@ from rest_framework_json_api import serializers
|
||||
},
|
||||
"required": ["role_arn", "access_key_id", "access_key_secret"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Cloudflare API Token",
|
||||
"properties": {
|
||||
"api_token": {
|
||||
"type": "string",
|
||||
"description": "Cloudflare API Token for authentication (recommended).",
|
||||
},
|
||||
},
|
||||
"required": ["api_token"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Cloudflare API Key + Email",
|
||||
"properties": {
|
||||
"api_key": {
|
||||
"type": "string",
|
||||
"description": "Cloudflare Global API Key for authentication (legacy).",
|
||||
},
|
||||
"api_email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"description": "Email address associated with the Cloudflare account.",
|
||||
},
|
||||
},
|
||||
"required": ["api_key", "api_email"],
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1503,6 +1503,18 @@ class BaseWriteProviderSecretSerializer(BaseWriteSerializer):
|
||||
serializer = MongoDBAtlasProviderSecret(data=secret)
|
||||
elif provider_type == Provider.ProviderChoices.ALIBABACLOUD.value:
|
||||
serializer = AlibabaCloudProviderSecret(data=secret)
|
||||
elif provider_type == Provider.ProviderChoices.CLOUDFLARE.value:
|
||||
if "api_token" in secret:
|
||||
serializer = CloudflareTokenProviderSecret(data=secret)
|
||||
elif "api_key" in secret and "api_email" in secret:
|
||||
serializer = CloudflareApiKeyProviderSecret(data=secret)
|
||||
else:
|
||||
raise serializers.ValidationError(
|
||||
{
|
||||
"secret": "Cloudflare credentials must include either 'api_token' "
|
||||
"or both 'api_key' and 'api_email'."
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise serializers.ValidationError(
|
||||
{"provider": f"Provider type not supported {provider_type}"}
|
||||
@@ -1654,6 +1666,21 @@ class OracleCloudProviderSecret(serializers.Serializer):
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class CloudflareTokenProviderSecret(serializers.Serializer):
|
||||
api_token = serializers.CharField()
|
||||
|
||||
class Meta:
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class CloudflareApiKeyProviderSecret(serializers.Serializer):
|
||||
api_key = serializers.CharField()
|
||||
api_email = serializers.EmailField()
|
||||
|
||||
class Meta:
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class AlibabaCloudProviderSecret(serializers.Serializer):
|
||||
access_key_id = serializers.CharField()
|
||||
access_key_secret = serializers.CharField()
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import logging
|
||||
from types import SimpleNamespace
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from allauth.socialaccount.models import SocialLogin
|
||||
from django.conf import settings
|
||||
from django.db import connection as django_connection
|
||||
@@ -14,6 +12,11 @@ from django.urls import reverse
|
||||
from django_celery_results.models import TaskResult
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
from tasks.jobs.backfill import (
|
||||
backfill_resource_scan_summaries,
|
||||
backfill_scan_category_summaries,
|
||||
backfill_scan_resource_group_summaries,
|
||||
)
|
||||
|
||||
from api.attack_paths import (
|
||||
AttackPathsQueryDefinition,
|
||||
@@ -59,11 +62,6 @@ from api.rls import Tenant
|
||||
from api.v1.serializers import TokenSerializer
|
||||
from prowler.lib.check.models import Severity
|
||||
from prowler.lib.outputs.finding import Status
|
||||
from tasks.jobs.backfill import (
|
||||
backfill_resource_scan_summaries,
|
||||
backfill_scan_category_summaries,
|
||||
backfill_scan_resource_group_summaries,
|
||||
)
|
||||
|
||||
TODAY = str(datetime.today().date())
|
||||
API_JSON_CONTENT_TYPE = "application/vnd.api+json"
|
||||
@@ -533,6 +531,12 @@ def providers_fixture(tenants_fixture):
|
||||
alias="alibabacloud_testing",
|
||||
tenant_id=tenant.id,
|
||||
)
|
||||
provider10 = Provider.objects.create(
|
||||
provider="cloudflare",
|
||||
uid="a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
|
||||
alias="cloudflare_testing",
|
||||
tenant_id=tenant.id,
|
||||
)
|
||||
|
||||
return (
|
||||
provider1,
|
||||
@@ -544,6 +548,7 @@ def providers_fixture(tenants_fixture):
|
||||
provider7,
|
||||
provider8,
|
||||
provider9,
|
||||
provider10,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `rds_instance_extended_support` check for AWS provider [(#9865)](https://github.com/prowler-cloud/prowler/pull/9865)
|
||||
- `OpenStack` provider support with Compute service including 1 security check [(#9811)](https://github.com/prowler-cloud/prowler/pull/9811)
|
||||
- `OpenStack` documentation for the support in the CLI [(#9848)](https://github.com/prowler-cloud/prowler/pull/9848)
|
||||
- Cloudflare provider credentials as constructor parameters (`api_token`, `api_key`, `api_email`) [(#9907)](https://github.com/prowler-cloud/prowler/pull/9907)
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ class CloudflareProvider(Provider):
|
||||
fixer_config: dict = {},
|
||||
mutelist_path: str = None,
|
||||
mutelist_content: dict = None,
|
||||
api_token: str = None,
|
||||
api_key: str = None,
|
||||
api_email: str = None,
|
||||
):
|
||||
logger.info("Instantiating Cloudflare provider...")
|
||||
|
||||
@@ -61,7 +64,12 @@ class CloudflareProvider(Provider):
|
||||
|
||||
max_retries = self._audit_config.get("max_retries", 2)
|
||||
|
||||
self._session = CloudflareProvider.setup_session(max_retries=max_retries)
|
||||
self._session = CloudflareProvider.setup_session(
|
||||
max_retries=max_retries,
|
||||
api_token=api_token,
|
||||
api_key=api_key,
|
||||
api_email=api_email,
|
||||
)
|
||||
|
||||
self._identity = CloudflareProvider.setup_identity(self._session)
|
||||
|
||||
@@ -135,19 +143,28 @@ class CloudflareProvider(Provider):
|
||||
return self._identity.accounts
|
||||
|
||||
@staticmethod
|
||||
def setup_session(max_retries: int = 2) -> CloudflareSession:
|
||||
def setup_session(
|
||||
max_retries: int = 2,
|
||||
api_token: str = None,
|
||||
api_key: str = None,
|
||||
api_email: str = None,
|
||||
) -> CloudflareSession:
|
||||
"""Initialize Cloudflare SDK client.
|
||||
|
||||
Credentials are read from environment variables:
|
||||
Credentials can be provided as arguments or read from environment variables:
|
||||
- CLOUDFLARE_API_TOKEN (recommended)
|
||||
- CLOUDFLARE_API_KEY and CLOUDFLARE_API_EMAIL (legacy)
|
||||
|
||||
Args:
|
||||
max_retries: Maximum number of retries for API requests (default is 2).
|
||||
api_token: Cloudflare API token (optional, falls back to env var).
|
||||
api_key: Cloudflare API key (optional, falls back to env var).
|
||||
api_email: Cloudflare API email (optional, falls back to env var).
|
||||
"""
|
||||
token = os.environ.get("CLOUDFLARE_API_TOKEN", "")
|
||||
key = os.environ.get("CLOUDFLARE_API_KEY", "")
|
||||
email = os.environ.get("CLOUDFLARE_API_EMAIL", "")
|
||||
# Use provided credentials or fall back to environment variables
|
||||
token = api_token or os.environ.get("CLOUDFLARE_API_TOKEN", "")
|
||||
key = api_key or os.environ.get("CLOUDFLARE_API_KEY", "")
|
||||
email = api_email or os.environ.get("CLOUDFLARE_API_EMAIL", "")
|
||||
|
||||
# Warn if both auth methods are set, use API Token (recommended)
|
||||
if token and key and email:
|
||||
@@ -293,14 +310,42 @@ class CloudflareProvider(Provider):
|
||||
|
||||
print_boxes(report_lines, report_title)
|
||||
|
||||
def test_connection(self) -> Connection:
|
||||
@staticmethod
|
||||
def test_connection(
|
||||
api_token: str = None,
|
||||
api_key: str = None,
|
||||
api_email: str = None,
|
||||
raise_on_exception: bool = True,
|
||||
provider_id: str = None,
|
||||
) -> Connection:
|
||||
"""Test connection to Cloudflare.
|
||||
|
||||
Test the connection to Cloudflare using the provided credentials.
|
||||
|
||||
Args:
|
||||
api_token: Cloudflare API token (optional, falls back to env var).
|
||||
api_key: Cloudflare API key (optional, falls back to env var).
|
||||
api_email: Cloudflare API email (optional, falls back to env var).
|
||||
raise_on_exception: Flag indicating whether to raise an exception if the connection fails.
|
||||
provider_id: The provider ID (Cloudflare account ID).
|
||||
|
||||
Returns:
|
||||
Connection: Connection object with is_connected status.
|
||||
"""
|
||||
try:
|
||||
_ = self._session.client.user.get()
|
||||
session = CloudflareProvider.setup_session(
|
||||
api_token=api_token,
|
||||
api_key=api_key,
|
||||
api_email=api_email,
|
||||
)
|
||||
_ = session.client.user.get()
|
||||
return Connection(is_connected=True)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if raise_on_exception:
|
||||
raise error
|
||||
return Connection(is_connected=False, error=error)
|
||||
|
||||
def validate_arguments(self) -> None:
|
||||
|
||||
@@ -149,7 +149,7 @@ class Zone(CloudflareService):
|
||||
|
||||
def _get_zones_universal_ssl(self) -> None:
|
||||
"""Get Universal SSL settings for all zones."""
|
||||
logger.info("Zones - Getting Universal SSL settings...")
|
||||
logger.info("Zone - Getting Universal SSL settings...")
|
||||
for zone in self.zones.values():
|
||||
try:
|
||||
universal_ssl = self.client.ssl.universal.settings.get(zone_id=zone.id)
|
||||
@@ -220,7 +220,7 @@ class Zone(CloudflareService):
|
||||
|
||||
def _get_zones_firewall_rules(self) -> None:
|
||||
"""Get firewall rules for all zones."""
|
||||
logger.info("Zones - Getting firewall rules...")
|
||||
logger.info("Zone - Getting firewall rules...")
|
||||
for zone in self.zones.values():
|
||||
try:
|
||||
self._get_zone_firewall_rules(zone)
|
||||
@@ -284,7 +284,7 @@ class Zone(CloudflareService):
|
||||
|
||||
def _get_zones_waf_rulesets(self) -> None:
|
||||
"""Get WAF rulesets for all zones."""
|
||||
logger.info("Zones - Getting WAF rulesets...")
|
||||
logger.info("Zone - Getting WAF rulesets...")
|
||||
for zone in self.zones.values():
|
||||
try:
|
||||
self._get_zone_waf_rulesets(zone)
|
||||
|
||||
Reference in New Issue
Block a user