Compare commits

...

139 Commits

Author SHA1 Message Date
HugoPBrito
ef48c662ed Merge branch 'master' of https://github.com/prowler-cloud/prowler into PROWLER-822-high-cloudflare-zone-waf-enabled-check-false-positive
# Conflicts:
#	prowler/providers/cloudflare/cloudflare_provider.py
#	prowler/providers/cloudflare/services/zone/zone_service.py
#	prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/zone_waf_owasp_ruleset_enabled.py
#	tests/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/zone_waf_owasp_ruleset_enabled_test.py
2026-01-27 14:25:45 +01:00
HugoPBrito
395f101e05 chore: add to changelog 2026-01-27 14:06:18 +01:00
HugoPBrito
d158e0673b fix zone_waf_enabled false positive on free plan accounts 2026-01-27 13:24:53 +01:00
HugoPBrito
4ab769aff3 Merge branch 'cloudflare-pr4-dns-firewall-waf' into PROWLER-821-high-cloudflare-filtering-with-f-flag-not-working 2026-01-27 12:30:14 +01:00
HugoPBrito
8aa321c20d Merge branch 'master' of https://github.com/prowler-cloud/prowler into cloudflare-pr4-dns-firewall-waf 2026-01-27 12:29:20 +01:00
HugoPBrito
9818575395 Reapply "Merge branch 'master' of https://github.com/prowler-cloud/prowler into PROWLER-821-high-cloudflare-filtering-with-f-flag-not-working"
This reverts commit 00376582b2.
2026-01-27 12:28:09 +01:00
HugoPBrito
00376582b2 Revert "Merge branch 'master' of https://github.com/prowler-cloud/prowler into PROWLER-821-high-cloudflare-filtering-with-f-flag-not-working"
This reverts commit b29c688cdd, reversing
changes made to ae69b4f80a.
2026-01-27 12:27:28 +01:00
HugoPBrito
ba40d18643 Revert "chore: add to changelog"
This reverts commit cc00ddf877.
2026-01-27 12:26:16 +01:00
HugoPBrito
cc00ddf877 chore: add to changelog 2026-01-27 12:24:39 +01:00
HugoPBrito
b29c688cdd Merge branch 'master' of https://github.com/prowler-cloud/prowler into PROWLER-821-high-cloudflare-filtering-with-f-flag-not-working 2026-01-27 12:23:32 +01:00
HugoPBrito
ae69b4f80a chore: only show audited accounts 2026-01-27 12:23:15 +01:00
HugoPBrito
8804ce1bd0 feat: --account-id filter support 2026-01-27 12:16:56 +01:00
HugoPBrito
00c57cea8d chore: re run actions 2026-01-27 10:39:13 +01:00
HugoPBrito
1b2c08649e chore: enhancements 2026-01-26 12:51:05 +01:00
HugoPBrito
47317680e5 fix(cloudflare): use zone_name as region for DNS records in CheckReportCloudflare 2026-01-20 15:31:56 +01:00
HugoPBrito
c7558a9f78 chore: remove accidentally committed uv.lock 2026-01-20 14:57:09 +01:00
HugoPBrito
86ecec542b Revert "chore: remove uv.lock files"
This reverts commit cb82a42035.
2026-01-20 14:54:55 +01:00
HugoPBrito
cb82a42035 chore: remove uv.lock files 2026-01-20 14:54:34 +01:00
HugoPBrito
2c69eb58c9 chore(cloudflare): remove unnecessary __init__.py files from tests 2026-01-20 14:54:15 +01:00
HugoPBrito
fcd9e2d40f fix(cloudflare): remove redundant provider assignment and single quotes from status messages 2026-01-20 14:52:00 +01:00
HugoPBrito
f0c69874e0 fix(cloudflare): correct SRV record target extraction for Cloudflare format 2026-01-20 14:49:30 +01:00
HugoPBrito
21444f7880 feat(cloudflare): expand dangling record check to include MX, NS, and SRV
- Dangling MX records can allow mail interception
- Dangling NS records can lead to subdomain delegation takeover
- Dangling SRV records expose service discovery vulnerabilities
2026-01-20 14:35:26 +01:00
HugoPBrito
3e3f56629f feat(cloudflare): expand wildcard check to include MX and SRV records
Wildcard MX records can allow mail interception for arbitrary subdomains.
Wildcard SRV records can expose services on any subdomain.
2026-01-20 14:35:21 +01:00
HugoPBrito
38f6ca9514 fix(cloudflare): get zones directly from API in DNS and Firewall services
This fixes an issue where DNS and Firewall services would have empty
records because they depended on zone_client which might not be
initialized when the services are loaded.
2026-01-20 14:35:14 +01:00
HugoPBrito
7f71b93eec fix(cloudflare): only match OWASP rulesets by name, not by phase 2026-01-20 14:35:08 +01:00
HugoPBrito
12752a5839 chore: add to changelog 2026-01-20 13:58:52 +01:00
HugoPBrito
eb76e2b986 Merge branch 'master' into cloudflare-pr4-dns-firewall-waf
Resolved conflicts by merging both features:
- Kept rate_limit_rules from master
- Kept firewall_rules and waf_rulesets from HEAD
- Updated CloudflareZone to include all three: rate_limit_rules, firewall_rules, waf_rulesets
- Set severity to 'high' for zone_rate_limiting_enabled check
2026-01-16 13:46:34 +01:00
HugoPBrito
42c56fa33a Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf
And add Resourcegroup
2026-01-14 15:43:49 +01:00
HugoPBrito
5dcdeed782 fix: add ResourceGroup to metadata 2026-01-14 15:23:24 +01:00
HugoPBrito
c35eaa8aa9 Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2026-01-14 15:17:45 +01:00
HugoPBrito
bca7c3a479 fix: nested folders and add ResourceGroup 2026-01-14 15:16:04 +01:00
Hugo Pereira Brito
e03fb88ca2 chore: update prowler/CHANGELOG.md
Co-authored-by: Andoni Alonso  <14891798+andoniaf@users.noreply.github.com>
2026-01-14 14:50:55 +01:00
HugoPBrito
cecf288d4f chore: add to changelog 2026-01-14 14:48:42 +01:00
HugoPBrito
8c4d251c51 feat: fix zone_bot_fight_mode_enabled and add zone_browser_integrity_check_enabled 2026-01-14 14:36:42 +01:00
HugoPBrito
98d4e08cbb zone_development_mode_disabled 2026-01-14 14:20:08 +01:00
HugoPBrito
3c004582d7 feat: enhance zone_rate_limiting_enabled 2026-01-14 14:19:42 +01:00
HugoPBrito
726aeec64b feat: enhance zone_challenge_passage_configured check 2026-01-14 14:19:08 +01:00
HugoPBrito
3d1a0b1270 feat: enhance zone_challenge_passage_configured check 2026-01-14 14:18:43 +01:00
HugoPBrito
b014fdbde3 chore: remove deprecated check 2026-01-14 13:23:42 +01:00
HugoPBrito
d693a34747 chore: rename zone records checks and add docstrings 2026-01-14 13:17:25 +01:00
HugoPBrito
a6860ffa7d Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf 2026-01-14 13:02:58 +01:00
HugoPBrito
d06af16a5c chore: rename zone records checks and add docstrings 2026-01-14 12:52:21 +01:00
Hugo Pereira Brito
0250bc3b0e Merge branch 'master' into cloudflare-pr2-tls-email-checks 2026-01-14 12:44:24 +01:00
HugoPBrito
ee1e6c35f2 Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2026-01-14 12:28:10 +01:00
HugoPBrito
2e552c65a5 chore: add docstrings 2026-01-14 12:19:56 +01:00
HugoPBrito
910514e964 chore: rename zone records checks 2026-01-14 12:02:50 +01:00
HugoPBrito
695a3466cd fix: filter_zones 2026-01-14 11:07:49 +01:00
HugoPBrito
7590ed7913 Merge branch 'master' of https://github.com/prowler-cloud/prowler into cloudflare-pr2-tls-email-checks 2026-01-14 11:02:28 +01:00
HugoPBrito
7fb82b0650 chore: run acions 2026-01-13 15:28:52 +01:00
HugoPBrito
fa21a300fb fix: linter 2026-01-13 11:48:57 +01:00
HugoPBrito
d51fa60e58 feat: add m365 mutelist to labeler.yaml 2026-01-13 11:46:43 +01:00
HugoPBrito
9d69d3a25f feat: add cloudflare to labeler.yaml 2026-01-13 11:44:33 +01:00
HugoPBrito
74da022e48 chore: add version badge to docs 2026-01-13 11:38:52 +01:00
HugoPBrito
da2b6d028b chore: add cloudflare to providers table 2026-01-13 11:38:30 +01:00
HugoPBrito
55d8a5d664 Merge branch 'master' of https://github.com/prowler-cloud/prowler into cloudflare-pr2-tls-email-checks 2026-01-13 11:33:03 +01:00
HugoPBrito
ecc1cf8b04 chore: add to changelog 2026-01-13 11:13:48 +01:00
HugoPBrito
394a62fab1 Merge branch 'master' into cloudflare-pr2-tls-email-checks 2026-01-13 11:10:52 +01:00
HugoPBrito
0ef68f55a1 Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2026-01-12 17:28:26 +01:00
HugoPBrito
94b14d1592 Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2026-01-12 17:28:15 +01:00
HugoPBrito
36ac1bc47e fix: revert ui changes 2026-01-12 17:27:42 +01:00
HugoPBrito
41de65ceaa Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2026-01-12 17:25:48 +01:00
HugoPBrito
12f95e3a19 fix: only accept strict 2026-01-12 17:25:09 +01:00
HugoPBrito
f5cada05c3 Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2026-01-12 16:25:06 +01:00
HugoPBrito
bfd8abdd89 Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2026-01-12 16:24:52 +01:00
HugoPBrito
99d2736116 chore: enhance status extended 2026-01-12 16:24:40 +01:00
HugoPBrito
b11e074f41 fix: zones_ssl_strict 2026-01-12 16:17:26 +01:00
HugoPBrito
a4e084afc9 chore: add docstrings 2026-01-12 16:17:11 +01:00
HugoPBrito
2bba3efbb1 fix: remove file 2026-01-12 14:09:57 +01:00
HugoPBrito
a2af18885d Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2026-01-12 14:04:13 +01:00
HugoPBrito
edcac4e4bc fix: api peotry lock eof 2026-01-12 11:35:08 +01:00
HugoPBrito
71ed16ee29 chore: restore api poetry lock 2026-01-12 11:33:46 +01:00
HugoPBrito
f2b2f0af95 Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf 2026-01-12 09:56:11 +01:00
HugoPBrito
90ace9265b Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2026-01-09 13:52:37 +01:00
HugoPBrito
773e4b23b5 Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2026-01-09 13:48:37 +01:00
HugoPBrito
9a22b1238a fix: remove config vars 2026-01-09 13:48:26 +01:00
HugoPBrito
80095603fd chore: correct CheckTitle 2026-01-09 13:35:39 +01:00
HugoPBrito
f234c16015 Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2026-01-09 11:51:50 +01:00
HugoPBrito
b3b1ee3252 chore: correct CheckTitle 2026-01-09 11:50:38 +01:00
HugoPBrito
3ba5d43c64 Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2026-01-09 11:47:13 +01:00
HugoPBrito
3720f1d235 fix: metadata 2026-01-09 11:46:58 +01:00
HugoPBrito
21868d7741 chore: resolve comment 2026-01-09 11:43:07 +01:00
HugoPBrito
ba4f93ec36 Merge branch 'master' of https://github.com/prowler-cloud/prowler into PROWLER-386-add-cloudflare-provider-to-cli 2026-01-09 11:36:16 +01:00
HugoPBrito
5b00846afe feat: enhance zone record checks 2026-01-09 11:35:56 +01:00
HugoPBrito
a5a5c35f90 Merge branch 'master' of https://github.com/prowler-cloud/prowler into PROWLER-386-add-cloudflare-provider-to-cli 2026-01-08 14:59:27 +01:00
HugoPBrito
5d894dcf94 fix: delete redundant check 2026-01-07 16:19:29 +01:00
HugoPBrito
5c0f9b19b0 chore: rename zone records checks 2026-01-07 16:18:31 +01:00
HugoPBrito
a7d8c8f679 feat: enhance zones_caa_record_exists and zones_universal_ssl_enabled 2026-01-07 16:09:24 +01:00
HugoPBrito
979ae1150c Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2026-01-07 15:36:26 +01:00
HugoPBrito
6d182f3efd chore: remove single quotes from status extended 2026-01-07 15:36:14 +01:00
HugoPBrito
8f937e4530 chore: add to changelog 2026-01-02 12:59:06 +00:00
HugoPBrito
9dd149d20a Merge branch 'master' of https://github.com/prowler-cloud/prowler into PROWLER-386-add-cloudflare-provider-to-cli 2026-01-02 10:04:22 +00:00
HugoPBrito
8e96f8361a Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf 2025-12-16 15:28:04 +01:00
HugoPBrito
0822692903 Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2025-12-16 15:27:50 +01:00
HugoPBrito
7c4b814b43 fix: zones_security_level check 2025-12-16 15:27:40 +01:00
HugoPBrito
c0b63e8564 feat: add tests 2025-12-16 14:49:33 +01:00
HugoPBrito
e3ae9b37d0 Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf 2025-12-16 13:59:02 +01:00
HugoPBrito
95087dcba7 feat: add tests 2025-12-16 13:39:31 +01:00
HugoPBrito
346c17b57d Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2025-12-16 13:32:42 +01:00
HugoPBrito
c8af89aa23 feat: add tests 2025-12-16 13:32:08 +01:00
HugoPBrito
1d386e7f27 Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf 2025-12-16 13:09:29 +01:00
HugoPBrito
8e2d2f00e6 fix: iteration and attribute read 2025-12-16 13:03:16 +01:00
HugoPBrito
22d1daf3c4 Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2025-12-16 12:42:46 +01:00
HugoPBrito
32f39e2366 fix: iteration and attribute read 2025-12-16 12:42:18 +01:00
HugoPBrito
c7050b1979 Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2025-12-16 12:35:28 +01:00
HugoPBrito
c612637a86 allow --list-checks without authentication 2025-12-16 12:35:15 +01:00
HugoPBrito
f6373387fd Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf 2025-12-16 12:26:42 +01:00
HugoPBrito
b390d6925b Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2025-12-16 12:26:25 +01:00
HugoPBrito
8d76e923cf Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2025-12-16 12:24:58 +01:00
HugoPBrito
25b732655c fix: remove terraform link 2025-12-16 12:24:35 +01:00
HugoPBrito
f8fe1a3655 fix: remove terraform links 2025-12-16 12:23:15 +01:00
HugoPBrito
05a07567b5 fix: logic from regional changes 2025-12-16 12:18:10 +01:00
HugoPBrito
9d658ef531 refactor: remove waf and firewall services
Checks and logic were moved to zones service
2025-12-16 12:00:11 +01:00
HugoPBrito
557b5aa480 feat: add more dns checks 2025-12-16 11:57:24 +01:00
HugoPBrito
c1140dfcc0 Merge branch 'cloudflare-pr3-bot-config-checks' into cloudflare-pr4-dns-firewall-waf 2025-12-16 11:16:07 +01:00
HugoPBrito
49e02bfbd1 chore: enhance metadata 2025-12-16 11:15:33 +01:00
HugoPBrito
f086106d53 Merge branch 'cloudflare-pr2-tls-email-checks' into cloudflare-pr3-bot-config-checks 2025-12-16 10:49:40 +01:00
HugoPBrito
55e55bc7a3 chore: enhance metadata 2025-12-16 10:49:24 +01:00
HugoPBrito
f9efe08984 Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks 2025-12-16 10:24:54 +01:00
HugoPBrito
5ff390e6fb fix: parser tests 2025-12-16 10:23:05 +01:00
HugoPBrito
72d2ff40f2 chore: remove authentication arguments 2025-12-16 08:20:17 +01:00
HugoPBrito
c667ff91be feat: add tests 2025-12-15 17:58:47 +01:00
HugoPBrito
1e31fe7441 fix: use pydantic v2 2025-12-15 17:34:25 +01:00
HugoPBrito
e4d1d647c5 fix: move logic from provider config to config.yaml 2025-12-15 17:29:35 +01:00
HugoPBrito
4d078aece5 fix: type checking from circular dependency
Forgotten to remove
2025-12-15 17:21:14 +01:00
HugoPBrito
c2c73db4e7 fix: circular dependencies
Enhanced provider logic to avoid conflicting structure
2025-12-15 16:45:20 +01:00
HugoPBrito
6e736fcdac chore: add comment explaining fix 2025-12-10 15:18:46 +01:00
HugoPBrito
5bb5fbe468 chore: resolve github-advanced-security comments 2025-12-10 14:14:30 +01:00
HugoPBrito
c0da0f909f Merge branch 'master' of https://github.com/prowler-cloud/prowler into PROWLER-386-add-cloudflare-provider-to-cli 2025-12-10 14:01:26 +01:00
HugoPBrito
aa2b86f96d feat: add to docs 2025-12-10 13:50:25 +01:00
HugoPBrito
c005959835 fix: checks behavior and flags 2025-12-10 13:49:03 +01:00
HugoPBrito
7ea76a71f7 feat: max retries and avoid conflicting env vars
The Cloudflare SDK automatically reads credentials from environment variables, which causes conflicts. In that case, we prioritize the recommended token auth now.
2025-12-10 13:14:14 +01:00
HugoPBrito
e89e50a3de chore: add zone name to all status extended 2025-12-10 09:59:30 +01:00
HugoPBrito
943c5bf2ea fix: mutelist 2025-12-09 17:57:55 +01:00
HugoPBrito
bac6aa85c0 fix: zones service and checks 2025-12-09 15:28:16 +01:00
HugoPBrito
22540fc0ae chore: adapt metadata to new format 2025-12-09 14:53:58 +01:00
HugoPBrito
3a335583df feat(cloudflare): add DNS, Firewall, and WAF services with checks
Adds additional Cloudflare services to complete the provider:

DNS service:
- dns_records_proxied: Validates DNS records are proxied through Cloudflare

Firewall service:
- firewall_has_blocking_rules: Ensures firewall has blocking rules configured
- firewall_rate_limiting_configured: Validates rate limiting is configured

WAF service:
- waf_owasp_enabled: Validates OWASP ruleset is enabled
2025-12-03 12:36:46 +01:00
HugoPBrito
6668931db7 feat(cloudflare): add bot protection and configuration checks for zones
Adds 9 additional security checks for Cloudflare zones:

Bot protection:
- zones_bot_fight_mode_enabled: Validates Bot Fight Mode is enabled
- zones_waf_enabled: Ensures WAF is enabled for zone
- zones_rate_limiting_enabled: Validates rate limiting is configured

Security configuration:
- zones_challenge_passage_configured: Validates challenge passage settings
- zones_development_mode_disabled: Ensures development mode is disabled
- zones_always_online_disabled: Validates Always Online is properly configured

Content protection:
- zones_hotlink_protection_enabled: Ensures hotlink protection is enabled
- zones_server_side_excludes_enabled: Validates server-side excludes
- zones_ip_geolocation_enabled: Validates IP geolocation is enabled
2025-12-03 12:36:25 +01:00
HugoPBrito
6202b45a97 feat(cloudflare): add TLS/SSL and email security checks for zones
Adds 9 additional security checks for Cloudflare zones:

TLS/SSL checks:
- zones_tls_1_3_enabled: Validates TLS 1.3 is enabled
- zones_hsts_include_subdomains: Ensures HSTS includes subdomains
- zones_automatic_https_rewrites_enabled: Validates automatic HTTPS rewrites
- zones_universal_ssl_enabled: Ensures Universal SSL is enabled

Email security checks:
- zones_dmarc_record_exists: Validates DMARC record exists
- zones_spf_record_exists: Validates SPF record exists
- zones_caa_record_exists: Validates CAA record exists
- zones_email_obfuscation_enabled: Ensures email obfuscation is enabled

Security configuration:
- zones_security_level: Validates security level configuration
2025-12-03 12:35:46 +01:00
HugoPBrito
2636351f5d feat(cloudflare): add Cloudflare provider with zones service and critical security checks
Adds the Cloudflare provider to Prowler with:

Core infrastructure:
- CloudflareProvider with API token authentication
- Zones service for fetching zone configurations
- CLI integration (parser arguments, outputs)
- Mutelist support and config files

Critical security checks (5):
- zones_ssl_strict: Ensures SSL/TLS encryption mode is strict
- zones_min_tls_version_secure: Ensures minimum TLS 1.2
- zones_dnssec_enabled: Validates DNSSEC is enabled
- zones_https_redirect_enabled: Ensures automatic HTTPS redirect
- zones_hsts_enabled: Validates HTTP Strict Transport Security
2025-12-03 12:35:25 +01:00
7 changed files with 340 additions and 178 deletions

View File

@@ -25,6 +25,9 @@ All notable changes to the **Prowler SDK** are documented in this file.
- Update Azure IAM service metadata to new format [(#9620)](https://github.com/prowler-cloud/prowler/pull/9620)
- Update Azure Policy service metadata to new format [(#9625)](https://github.com/prowler-cloud/prowler/pull/9625)
### Fixed
- Cloudflare `zone_waf_enabled` false positives [(#9896)](https://github.com/prowler-cloud/prowler/pull/9896)
---
## [5.17.0] (Prowler v5.17.0)

View File

@@ -32,14 +32,34 @@ class CloudflareFirewallRule(BaseModel):
arbitrary_types_allowed = True
class CloudflareWAFRulesetRule(BaseModel):
"""A rule inside a WAF managed entrypoint ruleset.
Each rule with ``action == "execute"`` deploys a specific managed
ruleset identified by ``managed_ruleset_id`` (from
``action_parameters.id``), e.g. the Cloudflare Managed Ruleset or
OWASP Core Ruleset.
"""
id: str
name: str = ""
action: Optional[str] = None
enabled: bool = False
managed_ruleset_id: Optional[str] = None
class CloudflareWAFRuleset(BaseModel):
"""Represents a WAF ruleset (managed rules) for a zone."""
"""Represents the WAF entrypoint ruleset for a zone (phase
``http_request_firewall_managed``).
Contains the individual rules that deploy managed rulesets.
"""
id: str
name: str
kind: Optional[str] = None
phase: Optional[str] = None
enabled: bool = True
rules: list[CloudflareWAFRulesetRule] = Field(default_factory=list)
class Zone(CloudflareService):
@@ -55,7 +75,6 @@ class Zone(CloudflareService):
self._get_zones_rate_limit_rules()
self._get_zones_bot_management()
self._get_zones_firewall_rules()
self._get_zones_waf_rulesets()
def _list_zones(self) -> None:
"""List all Cloudflare zones with their basic information."""
@@ -230,7 +249,18 @@ class Zone(CloudflareService):
)
def _get_zone_firewall_rules(self, zone: "CloudflareZone") -> None:
"""List firewall rules from custom rulesets for a zone."""
"""List firewall rules from rulesets for a zone.
Iterates all rulesets and, for phases relevant to security
(custom firewall, rate-limit, WAF managed), fetches the ruleset
detail to extract individual rules into ``zone.firewall_rules``.
For WAF managed rulesets (phase ``http_request_firewall_managed``)
it also populates ``zone.waf_rulesets`` with a
``CloudflareWAFRuleset`` containing a ``CloudflareWAFRulesetRule``
per rule. Each ``execute`` rule references a managed ruleset via
``action_parameters.id`` (e.g. Cloudflare Managed, OWASP Core).
"""
seen_ruleset_ids: set[str] = set()
try:
for ruleset in self.client.rulesets.list(zone_id=zone.id):
@@ -251,11 +281,17 @@ class Zone(CloudflareService):
)
rules = getattr(ruleset_detail, "rules", []) or []
seen_rule_ids: set[str] = set()
waf_ruleset_rules: list[CloudflareWAFRulesetRule] = []
for rule in rules:
rule_id = getattr(rule, "id", "")
if rule_id in seen_rule_ids:
break
seen_rule_ids.add(rule_id)
rule_action = getattr(rule, "action", None)
rule_enabled = getattr(rule, "enabled", True)
try:
zone.firewall_rules.append(
CloudflareFirewallRule(
@@ -263,8 +299,8 @@ class Zone(CloudflareService):
name=getattr(rule, "description", "")
or rule_id,
description=getattr(rule, "description", None),
action=getattr(rule, "action", None),
enabled=getattr(rule, "enabled", True),
action=rule_action,
enabled=rule_enabled,
expression=getattr(rule, "expression", None),
phase=ruleset_phase,
)
@@ -273,6 +309,47 @@ class Zone(CloudflareService):
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
# Collect WAF rules for the managed phase
if ruleset_phase == "http_request_firewall_managed":
action_params = getattr(rule, "action_parameters", None)
managed_id = (
getattr(action_params, "id", None)
if action_params
else None
)
try:
waf_ruleset_rules.append(
CloudflareWAFRulesetRule(
id=rule_id,
name=getattr(rule, "description", "")
or rule_id,
action=rule_action,
enabled=rule_enabled,
managed_ruleset_id=managed_id,
)
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
# Build the WAF ruleset with its collected rules
if ruleset_phase == "http_request_firewall_managed":
try:
zone.waf_rulesets.append(
CloudflareWAFRuleset(
id=ruleset_id,
name=getattr(ruleset, "name", ""),
kind=getattr(ruleset, "kind", None),
phase=ruleset_phase,
rules=waf_ruleset_rules,
)
)
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}"
@@ -282,45 +359,6 @@ class Zone(CloudflareService):
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:
@@ -342,7 +380,6 @@ class Zone(CloudflareService):
"tls_1_3",
"automatic_https_rewrites",
"security_header",
"waf",
"security_level",
"browser_check",
"challenge_ttl",
@@ -364,7 +401,6 @@ class Zone(CloudflareService):
strict_transport_security=self._get_strict_transport_security(
settings.get("security_header")
),
waf=settings.get("waf"),
security_level=settings.get("security_level"),
browser_check=settings.get("browser_check"),
challenge_ttl=settings.get("challenge_ttl") or 0,
@@ -428,7 +464,6 @@ class CloudflareZoneSettings(BaseModel):
default_factory=StrictTransportSecurity
)
# Security settings
waf: Optional[str] = None
security_level: Optional[str] = None
browser_check: Optional[str] = None
challenge_ttl: Optional[int] = None

View File

@@ -9,7 +9,7 @@
"Severity": "high",
"ResourceType": "Zone",
"ResourceGroup": "network",
"Description": "**Cloudflare zones** are assessed for **Web Application Firewall (WAF)** configuration by checking if it is enabled to protect against common web vulnerabilities including **SQL injection**, **XSS**, and **OWASP Top 10** threats.",
"Description": "**Cloudflare zones** are assessed for **Cloudflare Managed Ruleset** deployment. This managed ruleset (available on Pro, Business and Enterprise plans) provides fast and effective protection against common web vulnerabilities including **SQL injection**, **XSS**, and other threats. The Free Managed Ruleset (always active) is excluded. OWASP Core Ruleset coverage is handled by a separate check (`zone_waf_owasp_ruleset_enabled`).",
"Risk": "Without **WAF**, web applications are exposed to common 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**: application-layer attacks can cause service disruption",
"RelatedUrl": "",
"AdditionalURLs": [
@@ -32,5 +32,5 @@
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "WAF is available on Pro, Business, and Enterprise plans. Configure managed rulesets and create custom rules to match your application's specific security requirements."
"Notes": "WAF Managed Ruleset is available on Pro, Business, and Enterprise plans. This check verifies deployment of the Cloudflare Managed Ruleset (efb7b8c949ac4650a09736fc376e9aee) via the managed rulesets API (phase http_request_firewall_managed). The Free Managed Ruleset (always active on all plans, cannot be disabled) is excluded. OWASP Core Ruleset coverage is handled by zone_waf_owasp_ruleset_enabled."
}

View File

@@ -1,26 +1,37 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.zone.zone_client import zone_client
# The Cloudflare Managed Ruleset (Pro+ plans) provides comprehensive WAF
# protection. The Free Managed Ruleset (always active on all plans) is
# excluded because it cannot be disabled or configured.
# OWASP Core Ruleset coverage is handled by zone_waf_owasp_ruleset_enabled.
CLOUDFLARE_MANAGED_RULESET_ID = "efb7b8c949ac4650a09736fc376e9aee"
class zone_waf_enabled(Check):
"""Ensure that WAF is enabled for Cloudflare zones.
"""Ensure that the Cloudflare Managed WAF Ruleset is enabled for zones.
The Web Application Firewall (WAF) protects against common web vulnerabilities
including SQL injection, cross-site scripting (XSS), and other OWASP Top 10
threats. When enabled, it inspects HTTP requests and blocks malicious traffic
before it reaches the origin server.
The Cloudflare Managed Ruleset protects against common web vulnerabilities
including SQL injection, cross-site scripting (XSS), and other threats.
It requires a Pro, Business, or Enterprise plan.
The Free Managed Ruleset (available on all plans, always active) is excluded
because it provides only basic protection and cannot be configured.
OWASP Core Ruleset coverage is handled separately by
``zone_waf_owasp_ruleset_enabled``.
"""
def execute(self) -> list[CheckReportCloudflare]:
"""Execute the WAF enabled check.
Iterates through all Cloudflare zones and verifies that the Web Application
Firewall is enabled. The WAF provides essential protection against common
web application attacks.
Iterates through all Cloudflare zones and verifies that the Cloudflare
Managed Ruleset (``efb7b8c949ac4650a09736fc376e9aee``) is deployed and
has at least one enabled rule.
Returns:
A list of CheckReportCloudflare objects with PASS status if WAF is
enabled, or FAIL status if it is disabled for the zone.
A list of CheckReportCloudflare objects with PASS status if the
Cloudflare Managed Ruleset is active, or FAIL otherwise.
"""
findings = []
for zone in zone_client.zones.values():
@@ -28,9 +39,20 @@ class zone_waf_enabled(Check):
metadata=self.metadata(),
resource=zone,
)
waf_setting = (zone.settings.waf or "").lower()
if waf_setting == "on":
waf_enabled = False
for waf_ruleset in zone.waf_rulesets:
for rule in waf_ruleset.rules:
if (
rule.enabled
and rule.managed_ruleset_id == CLOUDFLARE_MANAGED_RULESET_ID
):
waf_enabled = True
break
if waf_enabled:
break
if waf_enabled:
report.status = "PASS"
report.status_extended = f"WAF is enabled for zone {zone.name}."
else:

View File

@@ -1,6 +1,8 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.zone.zone_client import zone_client
OWASP_CORE_RULESET_ID = "4814384a9e5d4991b9815dcfc25d2f1f"
class zone_waf_owasp_ruleset_enabled(Check):
"""Ensure that OWASP managed WAF rulesets are enabled for Cloudflare zones.
@@ -14,13 +16,12 @@ class zone_waf_owasp_ruleset_enabled(Check):
def execute(self) -> list[CheckReportCloudflare]:
"""Execute the OWASP WAF ruleset enabled check.
Iterates through all Cloudflare zones and verifies that OWASP managed
WAF rulesets are enabled. The check identifies OWASP rulesets by name
containing "owasp" or by the http_request_firewall_managed phase.
Iterates through all Cloudflare zones and verifies that the OWASP Core
Ruleset (``4814384a9e5d4991b9815dcfc25d2f1f``) is deployed and enabled.
Returns:
A list of CheckReportCloudflare objects with PASS status if OWASP
rulesets are enabled, or FAIL status if no OWASP protection exists.
ruleset is enabled, or FAIL status if no OWASP protection exists.
"""
findings = []
@@ -30,23 +31,24 @@ class zone_waf_owasp_ruleset_enabled(Check):
resource=zone,
)
# Find OWASP managed rulesets for this zone
# Only match rulesets that explicitly contain "owasp" in the name
# The phase check was too broad as it matched any managed ruleset
owasp_rulesets = [
ruleset
for ruleset in zone.waf_rulesets
if "owasp" in (ruleset.name or "").lower()
]
# Find enabled OWASP rules by matching the well-known
# managed ruleset ID across all WAF entrypoint rulesets.
owasp_enabled = False
for waf_ruleset in zone.waf_rulesets:
for rule in waf_ruleset.rules:
if (
rule.enabled
and rule.managed_ruleset_id == OWASP_CORE_RULESET_ID
):
owasp_enabled = True
break
if owasp_enabled:
break
if owasp_rulesets:
if owasp_enabled:
report.status = "PASS"
ruleset_descriptions = ", ".join(
ruleset.name for ruleset in owasp_rulesets
)
report.status_extended = (
f"Zone {zone.name} has OWASP managed WAF ruleset enabled: "
f"{ruleset_descriptions}."
f"Zone {zone.name} has OWASP managed WAF ruleset enabled."
)
else:
report.status = "FAIL"

View File

@@ -1,6 +1,8 @@
from unittest import mock
from prowler.providers.cloudflare.services.zone.zone_service import (
CloudflareWAFRuleset,
CloudflareWAFRulesetRule,
CloudflareZone,
CloudflareZoneSettings,
)
@@ -10,6 +12,10 @@ from tests.providers.cloudflare.cloudflare_fixtures import (
set_mocked_cloudflare_provider,
)
CLOUDFLARE_MANAGED_ID = "efb7b8c949ac4650a09736fc376e9aee"
OWASP_CORE_ID = "4814384a9e5d4991b9815dcfc25d2f1f"
FREE_MANAGED_ID = "77454fe2d30c4220b5701f6fdfb893ba"
class Test_zone_waf_enabled:
def test_no_zones(self):
@@ -34,7 +40,8 @@ class Test_zone_waf_enabled:
result = check.execute()
assert len(result) == 0
def test_zone_waf_enabled(self):
def test_zone_waf_enabled_with_cloudflare_managed(self):
"""PASS when Cloudflare Managed Ruleset is deployed and enabled."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
@@ -42,9 +49,24 @@ class Test_zone_waf_enabled:
name=ZONE_NAME,
status="active",
paused=False,
settings=CloudflareZoneSettings(
waf="on",
),
settings=CloudflareZoneSettings(),
waf_rulesets=[
CloudflareWAFRuleset(
id="entrypoint-id",
name="zone",
kind="zone",
phase="http_request_firewall_managed",
rules=[
CloudflareWAFRulesetRule(
id="rule-1",
name="Execute Cloudflare Managed Ruleset",
action="execute",
enabled=True,
managed_ruleset_id=CLOUDFLARE_MANAGED_ID,
),
],
)
],
)
}
@@ -65,12 +87,11 @@ class Test_zone_waf_enabled:
check = zone_waf_enabled()
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 "WAF is enabled" in result[0].status_extended
def test_zone_waf_disabled(self):
def test_zone_waf_fail_only_owasp(self):
"""FAIL when only OWASP is deployed (covered by separate check)."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
@@ -78,9 +99,73 @@ class Test_zone_waf_enabled:
name=ZONE_NAME,
status="active",
paused=False,
settings=CloudflareZoneSettings(
waf="off",
),
settings=CloudflareZoneSettings(),
waf_rulesets=[
CloudflareWAFRuleset(
id="entrypoint-id",
name="zone",
kind="zone",
phase="http_request_firewall_managed",
rules=[
CloudflareWAFRulesetRule(
id="rule-1",
name="Execute OWASP Core Ruleset",
action="execute",
enabled=True,
managed_ruleset_id=OWASP_CORE_ID,
),
],
)
],
)
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_cloudflare_provider(),
),
mock.patch(
"prowler.providers.cloudflare.services.zone.zone_waf_enabled.zone_waf_enabled.zone_client",
new=zone_client,
),
):
from prowler.providers.cloudflare.services.zone.zone_waf_enabled.zone_waf_enabled import (
zone_waf_enabled,
)
check = zone_waf_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_zone_waf_fail_only_free_ruleset(self):
"""FAIL when only the Free Managed Ruleset is active (always-on, not configurable)."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
id=ZONE_ID,
name=ZONE_NAME,
status="active",
paused=False,
settings=CloudflareZoneSettings(),
waf_rulesets=[
CloudflareWAFRuleset(
id="entrypoint-id",
name="zone",
kind="zone",
phase="http_request_firewall_managed",
rules=[
CloudflareWAFRulesetRule(
id="rule-1",
name="Execute Free Managed Ruleset",
action="execute",
enabled=True,
managed_ruleset_id=FREE_MANAGED_ID,
),
],
)
],
)
}
@@ -104,7 +189,8 @@ class Test_zone_waf_enabled:
assert result[0].status == "FAIL"
assert "WAF is not enabled" in result[0].status_extended
def test_zone_waf_none(self):
def test_zone_waf_fail_no_rulesets(self):
"""FAIL when no WAF rulesets exist at all."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
@@ -112,9 +198,56 @@ class Test_zone_waf_enabled:
name=ZONE_NAME,
status="active",
paused=False,
settings=CloudflareZoneSettings(
waf=None,
),
settings=CloudflareZoneSettings(),
)
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_cloudflare_provider(),
),
mock.patch(
"prowler.providers.cloudflare.services.zone.zone_waf_enabled.zone_waf_enabled.zone_client",
new=zone_client,
),
):
from prowler.providers.cloudflare.services.zone.zone_waf_enabled.zone_waf_enabled import (
zone_waf_enabled,
)
check = zone_waf_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_zone_waf_fail_paid_ruleset_disabled(self):
"""FAIL when a paid managed ruleset exists but is disabled."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
id=ZONE_ID,
name=ZONE_NAME,
status="active",
paused=False,
settings=CloudflareZoneSettings(),
waf_rulesets=[
CloudflareWAFRuleset(
id="entrypoint-id",
name="zone",
kind="zone",
phase="http_request_firewall_managed",
rules=[
CloudflareWAFRulesetRule(
id="rule-1",
name="Execute Cloudflare Managed Ruleset",
action="execute",
enabled=False,
managed_ruleset_id=CLOUDFLARE_MANAGED_ID,
),
],
)
],
)
}

View File

@@ -2,6 +2,7 @@ from unittest import mock
from prowler.providers.cloudflare.services.zone.zone_service import (
CloudflareWAFRuleset,
CloudflareWAFRulesetRule,
CloudflareZone,
CloudflareZoneSettings,
)
@@ -11,6 +12,9 @@ from tests.providers.cloudflare.cloudflare_fixtures import (
set_mocked_cloudflare_provider,
)
OWASP_CORE_ID = "4814384a9e5d4991b9815dcfc25d2f1f"
CLOUDFLARE_MANAGED_ID = "efb7b8c949ac4650a09736fc376e9aee"
class Test_zone_waf_owasp_ruleset_enabled:
def test_no_zones(self):
@@ -35,7 +39,8 @@ class Test_zone_waf_owasp_ruleset_enabled:
result = check.execute()
assert len(result) == 0
def test_zone_with_owasp_ruleset_by_name(self):
def test_zone_with_owasp_ruleset_enabled(self):
"""PASS when the OWASP Core Ruleset is deployed and enabled."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
@@ -46,12 +51,20 @@ class Test_zone_waf_owasp_ruleset_enabled:
settings=CloudflareZoneSettings(),
waf_rulesets=[
CloudflareWAFRuleset(
id="ruleset-1",
name="Cloudflare OWASP Core Ruleset",
kind="managed",
id="entrypoint-id",
name="zone",
kind="zone",
phase="http_request_firewall_managed",
enabled=True,
),
rules=[
CloudflareWAFRulesetRule(
id="rule-1",
name="Execute OWASP Core Ruleset",
action="execute",
enabled=True,
managed_ruleset_id=OWASP_CORE_ID,
),
],
)
],
)
}
@@ -73,14 +86,11 @@ class Test_zone_waf_owasp_ruleset_enabled:
check = zone_waf_owasp_ruleset_enabled()
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 "has OWASP managed WAF ruleset enabled" in result[0].status_extended
assert "Cloudflare OWASP Core Ruleset" in result[0].status_extended
def test_zone_with_managed_ruleset_without_owasp_name(self):
"""Test that a managed ruleset without 'owasp' in name does NOT pass."""
def test_zone_without_owasp_only_cloudflare_managed(self):
"""FAIL when only Cloudflare Managed Ruleset is deployed (no OWASP)."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
@@ -91,12 +101,20 @@ class Test_zone_waf_owasp_ruleset_enabled:
settings=CloudflareZoneSettings(),
waf_rulesets=[
CloudflareWAFRuleset(
id="ruleset-1",
name="Managed Rules",
kind="managed",
id="entrypoint-id",
name="zone",
kind="zone",
phase="http_request_firewall_managed",
enabled=True,
),
rules=[
CloudflareWAFRulesetRule(
id="rule-1",
name="Execute Cloudflare Managed Ruleset",
action="execute",
enabled=True,
managed_ruleset_id=CLOUDFLARE_MANAGED_ID,
),
],
)
],
)
}
@@ -119,12 +137,9 @@ class Test_zone_waf_owasp_ruleset_enabled:
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
"does not have OWASP managed WAF ruleset enabled"
in result[0].status_extended
)
def test_zone_without_owasp_ruleset(self):
def test_zone_with_owasp_disabled(self):
"""FAIL when OWASP rule exists but is disabled."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
@@ -135,12 +150,20 @@ class Test_zone_waf_owasp_ruleset_enabled:
settings=CloudflareZoneSettings(),
waf_rulesets=[
CloudflareWAFRuleset(
id="ruleset-1",
name="Custom Rules",
kind="custom",
phase="http_request_firewall_custom",
enabled=True,
),
id="entrypoint-id",
name="zone",
kind="zone",
phase="http_request_firewall_managed",
rules=[
CloudflareWAFRulesetRule(
id="rule-1",
name="Execute OWASP Core Ruleset",
action="execute",
enabled=False,
managed_ruleset_id=OWASP_CORE_ID,
),
],
)
],
)
}
@@ -163,12 +186,9 @@ class Test_zone_waf_owasp_ruleset_enabled:
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
"does not have OWASP managed WAF ruleset enabled"
in result[0].status_extended
)
def test_zone_with_no_waf_rulesets(self):
"""FAIL when no WAF rulesets exist."""
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
@@ -199,56 +219,3 @@ class Test_zone_waf_owasp_ruleset_enabled:
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
"does not have OWASP managed WAF ruleset enabled"
in result[0].status_extended
)
def test_zone_with_multiple_owasp_rulesets(self):
zone_client = mock.MagicMock
zone_client.zones = {
ZONE_ID: CloudflareZone(
id=ZONE_ID,
name=ZONE_NAME,
status="active",
paused=False,
settings=CloudflareZoneSettings(),
waf_rulesets=[
CloudflareWAFRuleset(
id="ruleset-1",
name="Cloudflare OWASP Core Ruleset",
kind="managed",
phase="http_request_firewall_managed",
enabled=True,
),
CloudflareWAFRuleset(
id="ruleset-2",
name="Custom OWASP Rules",
kind="managed",
phase="http_request_firewall_managed",
enabled=True,
),
],
)
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_cloudflare_provider(),
),
mock.patch(
"prowler.providers.cloudflare.services.zone.zone_waf_owasp_ruleset_enabled.zone_waf_owasp_ruleset_enabled.zone_client",
new=zone_client,
),
):
from prowler.providers.cloudflare.services.zone.zone_waf_owasp_ruleset_enabled.zone_waf_owasp_ruleset_enabled import (
zone_waf_owasp_ruleset_enabled,
)
check = zone_waf_owasp_ruleset_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert "Cloudflare OWASP Core Ruleset" in result[0].status_extended
assert "Custom OWASP Rules" in result[0].status_extended