mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-04-06 02:58:15 +00:00
Merge branch 'PROWLER-386-add-cloudflare-provider-to-cli' into cloudflare-pr2-tls-email-checks
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"Provider": "cloudflare",
|
||||
"CheckID": "zones_ssl_strict",
|
||||
"CheckTitle": "SSL/TLS encryption mode is set to Full, or Full Strict",
|
||||
"CheckTitle": "SSL/TLS encryption mode is set to Full (Strict)",
|
||||
"CheckType": [],
|
||||
"ServiceName": "zones",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Zone",
|
||||
"Description": "**Cloudflare zones** are assessed for **SSL/TLS encryption mode** by checking if the mode is set to `Full` to ensure **end-to-end encryption** with certificate validation.",
|
||||
"Description": "**Cloudflare zones** are assessed for **SSL/TLS encryption mode** by checking if the mode is set to `Full (Strict)` to ensure **end-to-end encryption** with certificate validation.",
|
||||
"Risk": "Without **strict SSL mode**, traffic between Cloudflare and origin may use unvalidated or unencrypted connections.\n- **Confidentiality**: sensitive data can be intercepted in transit via man-in-the-middle attacks\n- **Integrity**: responses can be modified without detection between Cloudflare and origin\n- **Compliance**: may violate PCI-DSS, HIPAA, and other regulatory requirements for encrypted transport",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
|
||||
@@ -3,10 +3,10 @@ from prowler.providers.cloudflare.services.zones.zones_client import zones_clien
|
||||
|
||||
|
||||
class zones_ssl_strict(Check):
|
||||
"""Ensure that SSL/TLS encryption mode is set to strict for Cloudflare zones.
|
||||
"""Ensure that SSL/TLS encryption mode is set to Full (Strict) for Cloudflare zones.
|
||||
|
||||
The SSL/TLS encryption mode determines how Cloudflare connects to the origin
|
||||
server. In 'strict' or 'full' mode, Cloudflare validates the origin
|
||||
server. In 'strict' mode, Cloudflare validates the origin
|
||||
server's SSL certificate, ensuring end-to-end encryption with certificate
|
||||
verification. Lower modes (off, flexible, full) are vulnerable to
|
||||
man-in-the-middle attacks between Cloudflare and the origin.
|
||||
@@ -16,13 +16,13 @@ class zones_ssl_strict(Check):
|
||||
"""Execute the SSL strict mode check.
|
||||
|
||||
Iterates through all Cloudflare zones and verifies that the SSL/TLS
|
||||
encryption mode is set to 'strict' or 'full'. These modes
|
||||
require a valid SSL certificate on the origin server and provide
|
||||
encryption mode is set to 'strict'. This mode
|
||||
requires a valid SSL certificate on the origin server and provides
|
||||
full end-to-end encryption with certificate validation.
|
||||
|
||||
Returns:
|
||||
A list of CheckReportCloudflare objects with PASS status if
|
||||
SSL mode is 'strict' or 'full', or FAIL status if using
|
||||
SSL mode is 'strict', or FAIL status if using
|
||||
less secure modes like 'off', 'flexible', or 'full'.
|
||||
"""
|
||||
findings = []
|
||||
@@ -32,11 +32,11 @@ class zones_ssl_strict(Check):
|
||||
resource=zone,
|
||||
)
|
||||
ssl_mode = (zone.settings.ssl_encryption_mode or "").lower()
|
||||
if ssl_mode in ["strict", "full"]:
|
||||
if ssl_mode == "strict":
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"SSL/TLS encryption mode is set to {ssl_mode} for zone {zone.name}."
|
||||
report.status_extended = f"SSL/TLS encryption mode is set to Full (Strict) for zone {zone.name}."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"SSL/TLS encryption mode is set to {ssl_mode} for zone {zone.name}, which is not strict or full."
|
||||
report.status_extended = f"SSL/TLS encryption mode is set to {ssl_mode.capitalize()} for zone {zone.name}, which is not Full (Strict)."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
@@ -70,7 +70,7 @@ class Test_zones_ssl_strict:
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"SSL/TLS encryption mode is set to strict for zone {ZONE_NAME}."
|
||||
== f"SSL/TLS encryption mode is set to Full (Strict) for zone {ZONE_NAME}."
|
||||
)
|
||||
|
||||
def test_zone_ssl_full_mode(self):
|
||||
@@ -104,10 +104,10 @@ class Test_zones_ssl_strict:
|
||||
check = zones_ssl_strict()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"SSL/TLS encryption mode is set to full for zone {ZONE_NAME}."
|
||||
== f"SSL/TLS encryption mode is set to Full for zone {ZONE_NAME}, which is not Full (Strict)."
|
||||
)
|
||||
|
||||
def test_zone_ssl_flexible_mode(self):
|
||||
@@ -144,7 +144,7 @@ class Test_zones_ssl_strict:
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"SSL/TLS encryption mode is set to flexible for zone {ZONE_NAME}, which is not strict or full."
|
||||
== f"SSL/TLS encryption mode is set to Flexible for zone {ZONE_NAME}, which is not Full (Strict)."
|
||||
)
|
||||
|
||||
def test_zone_ssl_off_mode(self):
|
||||
@@ -181,5 +181,5 @@ class Test_zones_ssl_strict:
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"SSL/TLS encryption mode is set to off for zone {ZONE_NAME}, which is not strict or full."
|
||||
== f"SSL/TLS encryption mode is set to Off for zone {ZONE_NAME}, which is not Full (Strict)."
|
||||
)
|
||||
|
||||
@@ -48,6 +48,88 @@ export const getProviders = async ({
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches all providers by iterating through all pages.
|
||||
* This is useful when you need the complete list of providers without pagination limits,
|
||||
* such as for dropdown menus or selection lists.
|
||||
*/
|
||||
export const getAllProviders = async ({
|
||||
query = "",
|
||||
sort = "",
|
||||
filters = {},
|
||||
}: {
|
||||
query?: string;
|
||||
sort?: string;
|
||||
filters?: Record<string, unknown>;
|
||||
} = {}): Promise<ProvidersApiResponse | undefined> => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
const pageSize = 100; // Use larger page size to minimize API calls
|
||||
let currentPage = 1;
|
||||
let allProviders: ProvidersApiResponse["data"] = [];
|
||||
let lastResponse: ProvidersApiResponse | undefined;
|
||||
let hasMorePages = true;
|
||||
|
||||
try {
|
||||
while (hasMorePages) {
|
||||
const url = new URL(`${apiBaseUrl}/providers?include=provider_groups`);
|
||||
url.searchParams.append("page[number]", currentPage.toString());
|
||||
url.searchParams.append("page[size]", pageSize.toString());
|
||||
|
||||
if (query) url.searchParams.append("filter[search]", query);
|
||||
if (sort) url.searchParams.append("sort", sort);
|
||||
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (key !== "filter[search]") {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
|
||||
const response = await fetch(url.toString(), { headers });
|
||||
const data = (await handleApiResponse(response)) as
|
||||
| ProvidersApiResponse
|
||||
| undefined;
|
||||
|
||||
if (!data?.data || data.data.length === 0) {
|
||||
hasMorePages = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
allProviders = [...allProviders, ...data.data];
|
||||
lastResponse = data;
|
||||
|
||||
// Check if we've fetched all pages
|
||||
const totalPages = data.meta?.pagination?.pages || 1;
|
||||
if (currentPage >= totalPages) {
|
||||
hasMorePages = false;
|
||||
} else {
|
||||
currentPage++;
|
||||
}
|
||||
}
|
||||
|
||||
// Return combined response with all providers
|
||||
if (lastResponse) {
|
||||
return {
|
||||
...lastResponse,
|
||||
data: allProviders,
|
||||
meta: {
|
||||
...lastResponse.meta,
|
||||
pagination: {
|
||||
...lastResponse.meta?.pagination,
|
||||
page: 1,
|
||||
pages: 1,
|
||||
count: allProviders.length,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
} catch (error) {
|
||||
console.error("Error fetching all providers:", error);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const getProvider = async (formData: FormData) => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
const providerId = formData.get("id");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getProviders } from "@/actions/providers";
|
||||
import { getAllProviders } from "@/actions/providers";
|
||||
import { getScans, getScansByState } from "@/actions/scans";
|
||||
import { auth } from "@/auth.config";
|
||||
import { MutedFindingsConfigButton } from "@/components/providers";
|
||||
@@ -33,9 +33,7 @@ export default async function Scans({
|
||||
const filteredParams = { ...resolvedSearchParams };
|
||||
delete filteredParams.scanId;
|
||||
|
||||
const providersData = await getProviders({
|
||||
pageSize: 50,
|
||||
});
|
||||
const providersData = await getAllProviders();
|
||||
|
||||
const providerInfo =
|
||||
providersData?.data
|
||||
|
||||
Reference in New Issue
Block a user