chore(ccc): update with latest version and improve mapping (#10625)

This commit is contained in:
Pedro Martín
2026-04-09 15:27:18 +02:00
committed by GitHub
parent b0d8534907
commit 56c370d3a4
12 changed files with 18378 additions and 12846 deletions
+3 -3
View File
@@ -97,7 +97,7 @@ COMPLIANCE_CLASS_MAP = {
(lambda name: name.startswith("iso27001_"), AWSISO27001),
(lambda name: name.startswith("kisa"), AWSKISAISMSP),
(lambda name: name == "prowler_threatscore_aws", ProwlerThreatScoreAWS),
(lambda name: name == "ccc_aws", CCC_AWS),
(lambda name: name.startswith("ccc_"), CCC_AWS),
(lambda name: name.startswith("c5_"), AWSC5),
(lambda name: name.startswith("csa_"), AWSCSA),
],
@@ -106,7 +106,7 @@ COMPLIANCE_CLASS_MAP = {
(lambda name: name == "mitre_attack_azure", AzureMitreAttack),
(lambda name: name.startswith("ens_"), AzureENS),
(lambda name: name.startswith("iso27001_"), AzureISO27001),
(lambda name: name == "ccc_azure", CCC_Azure),
(lambda name: name.startswith("ccc_"), CCC_Azure),
(lambda name: name == "prowler_threatscore_azure", ProwlerThreatScoreAzure),
(lambda name: name == "c5_azure", AzureC5),
(lambda name: name.startswith("csa_"), AzureCSA),
@@ -117,7 +117,7 @@ COMPLIANCE_CLASS_MAP = {
(lambda name: name.startswith("ens_"), GCPENS),
(lambda name: name.startswith("iso27001_"), GCPISO27001),
(lambda name: name == "prowler_threatscore_gcp", ProwlerThreatScoreGCP),
(lambda name: name == "ccc_gcp", CCC_GCP),
(lambda name: name.startswith("ccc_"), CCC_GCP),
(lambda name: name == "c5_gcp", GCPC5),
(lambda name: name.startswith("csa_"), GCPCSA),
],
+1
View File
@@ -21,6 +21,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- `entra_conditional_access_policy_device_registration_mfa_required` check and `entra_intune_enrollment_sign_in_frequency_every_time` enhancement for M365 provider [(#10222)](https://github.com/prowler-cloud/prowler/pull/10222)
- `entra_conditional_access_policy_block_elevated_insider_risk` check for M365 provider [(#10234)](https://github.com/prowler-cloud/prowler/pull/10234)
- `Vercel` provider support with 30 checks [(#10189)](https://github.com/prowler-cloud/prowler/pull/10189)
- CCC improvements with the latest checks and new mappings [(#10625)](https://github.com/prowler-cloud/prowler/pull/10625)
### 🔄 Changed
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
from colorama import Fore, Style
from tabulate import tabulate
from prowler.config.config import orange_color
def get_ccc_table(
findings: list,
bulk_checks_metadata: dict,
compliance_framework: str,
output_filename: str,
output_directory: str,
compliance_overview: bool,
):
section_table = {
"Provider": [],
"Section": [],
"Status": [],
"Muted": [],
}
pass_count = []
fail_count = []
muted_count = []
sections = {}
for index, finding in enumerate(findings):
check = bulk_checks_metadata[finding.check_metadata.CheckID]
check_compliances = check.Compliance
for compliance in check_compliances:
if compliance.Framework == "CCC":
for requirement in compliance.Requirements:
for attribute in requirement.Attributes:
section = attribute.Section
if section not in sections:
sections[section] = {"FAIL": 0, "PASS": 0, "Muted": 0}
if finding.muted:
if index not in muted_count:
muted_count.append(index)
sections[section]["Muted"] += 1
else:
if finding.status == "FAIL" and index not in fail_count:
fail_count.append(index)
sections[section]["FAIL"] += 1
elif finding.status == "PASS" and index not in pass_count:
pass_count.append(index)
sections[section]["PASS"] += 1
sections = dict(sorted(sections.items()))
for section in sections:
section_table["Provider"].append(compliance.Provider)
section_table["Section"].append(section)
if sections[section]["FAIL"] > 0:
section_table["Status"].append(
f"{Fore.RED}FAIL({sections[section]['FAIL']}){Style.RESET_ALL}"
)
else:
if sections[section]["PASS"] > 0:
section_table["Status"].append(
f"{Fore.GREEN}PASS({sections[section]['PASS']}){Style.RESET_ALL}"
)
else:
section_table["Status"].append(f"{Fore.GREEN}PASS{Style.RESET_ALL}")
section_table["Muted"].append(
f"{orange_color}{sections[section]['Muted']}{Style.RESET_ALL}"
)
if (
len(fail_count) + len(pass_count) + len(muted_count) > 1
): # If there are no resources, don't print the compliance table
print(
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
)
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
overview_table = [
[
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
]
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
if not compliance_overview:
if len(fail_count) > 0 and len(section_table["Section"]) > 0:
print(
f"\nFramework {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Results:"
)
print(
tabulate(
section_table,
tablefmt="rounded_grid",
headers="keys",
)
)
print(f"\nDetailed results of {compliance_framework.upper()} are in:")
print(
f" - CSV: {output_directory}/compliance/{output_filename}_{compliance_framework}.csv\n"
)
@@ -3,6 +3,7 @@ import sys
from prowler.lib.check.models import Check_Report
from prowler.lib.logger import logger
from prowler.lib.outputs.compliance.c5.c5 import get_c5_table
from prowler.lib.outputs.compliance.ccc.ccc import get_ccc_table
from prowler.lib.outputs.compliance.cis.cis import get_cis_table
from prowler.lib.outputs.compliance.csa.csa import get_csa_table
from prowler.lib.outputs.compliance.ens.ens import get_ens_table
@@ -104,6 +105,15 @@ def display_compliance_table(
output_directory,
compliance_overview,
)
elif compliance_framework.startswith("ccc_"):
get_ccc_table(
findings,
bulk_checks_metadata,
compliance_framework,
output_filename,
output_directory,
compliance_overview,
)
else:
get_generic_compliance_table(
findings,
@@ -0,0 +1,138 @@
from io import StringIO
from unittest import mock
from freezegun import freeze_time
from mock import patch
from prowler.lib.outputs.compliance.ccc.ccc_aws import CCC_AWS
from prowler.lib.outputs.compliance.ccc.models import CCC_AWSModel
from tests.lib.outputs.compliance.fixtures import CCC_AWS_FIXTURE
from tests.lib.outputs.fixtures.fixtures import generate_finding_output
from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_EU_WEST_1
class TestAWSCCC:
def test_output_transform_evaluated_requirement(self):
findings = [
generate_finding_output(compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"})
]
output = CCC_AWS(findings, CCC_AWS_FIXTURE)
output_data = output.data[0]
assert isinstance(output_data, CCC_AWSModel)
assert output_data.Provider == "aws"
assert output_data.AccountId == AWS_ACCOUNT_NUMBER
assert output_data.Region == AWS_REGION_EU_WEST_1
assert output_data.Description == CCC_AWS_FIXTURE.Description
assert output_data.Requirements_Id == CCC_AWS_FIXTURE.Requirements[0].Id
assert (
output_data.Requirements_Description
== CCC_AWS_FIXTURE.Requirements[0].Description
)
attribute = CCC_AWS_FIXTURE.Requirements[0].Attributes[0]
assert output_data.Requirements_Attributes_FamilyName == attribute.FamilyName
assert (
output_data.Requirements_Attributes_FamilyDescription
== attribute.FamilyDescription
)
assert output_data.Requirements_Attributes_Section == attribute.Section
assert output_data.Requirements_Attributes_SubSection == attribute.SubSection
assert (
output_data.Requirements_Attributes_SubSectionObjective
== attribute.SubSectionObjective
)
assert (
output_data.Requirements_Attributes_Applicability == attribute.Applicability
)
assert (
output_data.Requirements_Attributes_Recommendation
== attribute.Recommendation
)
assert (
output_data.Requirements_Attributes_SectionThreatMappings
== attribute.SectionThreatMappings
)
assert (
output_data.Requirements_Attributes_SectionGuidelineMappings
== attribute.SectionGuidelineMappings
)
assert output_data.Status == "PASS"
assert output_data.StatusExtended == ""
assert output_data.ResourceId == ""
assert output_data.ResourceName == ""
assert output_data.CheckId == "service_test_check_id"
assert output_data.Muted is False
def test_output_transform_manual_requirement(self):
# Use a finding for the evaluated requirement so the manual one is appended
# by the manual-loop branch (Checks=[]).
findings = [
generate_finding_output(compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"})
]
output = CCC_AWS(findings, CCC_AWS_FIXTURE)
# data[0] is the evaluated PASS row, data[1] is the manual row
manual_row = output.data[1]
assert isinstance(manual_row, CCC_AWSModel)
assert manual_row.Provider == "aws"
assert manual_row.AccountId == ""
assert manual_row.Region == ""
assert manual_row.Description == CCC_AWS_FIXTURE.Description
assert manual_row.Requirements_Id == CCC_AWS_FIXTURE.Requirements[1].Id
manual_attribute = CCC_AWS_FIXTURE.Requirements[1].Attributes[0]
assert (
manual_row.Requirements_Attributes_FamilyName == manual_attribute.FamilyName
)
assert manual_row.Requirements_Attributes_Section == manual_attribute.Section
assert manual_row.Status == "MANUAL"
assert manual_row.StatusExtended == "Manual check"
assert manual_row.ResourceId == "manual_check"
assert manual_row.ResourceName == "Manual check"
assert manual_row.CheckId == "manual"
assert manual_row.Muted is False
@freeze_time("2025-01-01 00:00:00")
@mock.patch(
"prowler.lib.outputs.compliance.ccc.ccc_aws.timestamp",
"2025-01-01 00:00:00",
)
def test_batch_write_data_to_file(self):
mock_file = StringIO()
findings = [
generate_finding_output(compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"})
]
output = CCC_AWS(findings, CCC_AWS_FIXTURE)
output._file_descriptor = mock_file
with patch.object(mock_file, "close", return_value=None):
output.batch_write_data_to_file()
mock_file.seek(0)
content = mock_file.read()
# Header check: AWS-specific columns must be present
header = content.split("\r\n", 1)[0]
assert "ACCOUNTID" in header
assert "REGION" in header
assert "REQUIREMENTS_ATTRIBUTES_FAMILYNAME" in header
assert "REQUIREMENTS_ATTRIBUTES_SECTION" in header
assert "REQUIREMENTS_ATTRIBUTES_APPLICABILITY" in header
assert "REQUIREMENTS_ATTRIBUTES_SECTIONTHREATMAPPINGS" in header
# Header should NOT contain Azure or GCP-only columns
assert "SUBSCRIPTIONID" not in header
assert "PROJECTID" not in header
# Body checks: evaluated row + manual row
rows = [r for r in content.split("\r\n") if r]
assert len(rows) == 3 # header + evaluated + manual
assert "CCC.Core.CN01.AR01" in rows[1]
assert "PASS" in rows[1]
assert AWS_ACCOUNT_NUMBER in rows[1]
assert AWS_REGION_EU_WEST_1 in rows[1]
assert "CCC.IAM.CN01.AR01" in rows[2]
assert "MANUAL" in rows[2]
assert "manual_check" in rows[2]
# The frozen timestamp should appear
assert "2025-01-01 00:00:00" in rows[1]
@@ -0,0 +1,99 @@
from io import StringIO
from unittest import mock
from freezegun import freeze_time
from mock import patch
from prowler.lib.outputs.compliance.ccc.ccc_azure import CCC_Azure
from prowler.lib.outputs.compliance.ccc.models import CCC_AzureModel
from tests.lib.outputs.compliance.fixtures import CCC_AZURE_FIXTURE
from tests.lib.outputs.fixtures.fixtures import generate_finding_output
from tests.providers.azure.azure_fixtures import AZURE_SUBSCRIPTION_ID
AZURE_LOCATION = "westeurope"
class TestAzureCCC:
def test_output_transform_evaluated_requirement(self):
findings = [
generate_finding_output(
provider="azure",
compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"},
account_uid=AZURE_SUBSCRIPTION_ID,
region=AZURE_LOCATION,
)
]
output = CCC_Azure(findings, CCC_AZURE_FIXTURE)
output_data = output.data[0]
assert isinstance(output_data, CCC_AzureModel)
assert output_data.Provider == "azure"
assert output_data.SubscriptionId == AZURE_SUBSCRIPTION_ID
assert output_data.Location == AZURE_LOCATION
assert output_data.Description == CCC_AZURE_FIXTURE.Description
assert output_data.Requirements_Id == CCC_AZURE_FIXTURE.Requirements[0].Id
attribute = CCC_AZURE_FIXTURE.Requirements[0].Attributes[0]
assert output_data.Requirements_Attributes_FamilyName == attribute.FamilyName
assert output_data.Requirements_Attributes_Section == attribute.Section
assert (
output_data.Requirements_Attributes_Applicability == attribute.Applicability
)
assert output_data.Status == "PASS"
assert output_data.CheckId == "service_test_check_id"
def test_output_transform_manual_requirement(self):
findings = [
generate_finding_output(
provider="azure",
compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"},
account_uid=AZURE_SUBSCRIPTION_ID,
region=AZURE_LOCATION,
)
]
output = CCC_Azure(findings, CCC_AZURE_FIXTURE)
manual_row = output.data[1]
assert isinstance(manual_row, CCC_AzureModel)
assert manual_row.Provider == "azure"
assert manual_row.SubscriptionId == ""
assert manual_row.Location == ""
assert manual_row.Requirements_Id == CCC_AZURE_FIXTURE.Requirements[1].Id
assert manual_row.Status == "MANUAL"
assert manual_row.CheckId == "manual"
@freeze_time("2025-01-01 00:00:00")
@mock.patch(
"prowler.lib.outputs.compliance.ccc.ccc_azure.timestamp",
"2025-01-01 00:00:00",
)
def test_batch_write_data_to_file(self):
mock_file = StringIO()
findings = [
generate_finding_output(
provider="azure",
compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"},
account_uid=AZURE_SUBSCRIPTION_ID,
region=AZURE_LOCATION,
)
]
output = CCC_Azure(findings, CCC_AZURE_FIXTURE)
output._file_descriptor = mock_file
with patch.object(mock_file, "close", return_value=None):
output.batch_write_data_to_file()
mock_file.seek(0)
content = mock_file.read()
header = content.split("\r\n", 1)[0]
assert "SUBSCRIPTIONID" in header
assert "LOCATION" in header
assert "ACCOUNTID" not in header
assert "PROJECTID" not in header
assert "REGION" not in header
rows = [r for r in content.split("\r\n") if r]
assert len(rows) == 3
assert "CCC.Core.CN01.AR01" in rows[1]
assert AZURE_SUBSCRIPTION_ID in rows[1]
assert "CCC.IAM.CN01.AR01" in rows[2]
assert "MANUAL" in rows[2]
@@ -0,0 +1,99 @@
from io import StringIO
from unittest import mock
from freezegun import freeze_time
from mock import patch
from prowler.lib.outputs.compliance.ccc.ccc_gcp import CCC_GCP
from prowler.lib.outputs.compliance.ccc.models import CCC_GCPModel
from tests.lib.outputs.compliance.fixtures import CCC_GCP_FIXTURE
from tests.lib.outputs.fixtures.fixtures import generate_finding_output
GCP_PROJECT_ID = "test-project"
GCP_LOCATION = "europe-west1"
class TestGCPCCC:
def test_output_transform_evaluated_requirement(self):
findings = [
generate_finding_output(
provider="gcp",
compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"},
account_uid=GCP_PROJECT_ID,
region=GCP_LOCATION,
)
]
output = CCC_GCP(findings, CCC_GCP_FIXTURE)
output_data = output.data[0]
assert isinstance(output_data, CCC_GCPModel)
assert output_data.Provider == "gcp"
assert output_data.ProjectId == GCP_PROJECT_ID
assert output_data.Location == GCP_LOCATION
assert output_data.Description == CCC_GCP_FIXTURE.Description
assert output_data.Requirements_Id == CCC_GCP_FIXTURE.Requirements[0].Id
attribute = CCC_GCP_FIXTURE.Requirements[0].Attributes[0]
assert output_data.Requirements_Attributes_FamilyName == attribute.FamilyName
assert output_data.Requirements_Attributes_Section == attribute.Section
assert (
output_data.Requirements_Attributes_Applicability == attribute.Applicability
)
assert output_data.Status == "PASS"
assert output_data.CheckId == "service_test_check_id"
def test_output_transform_manual_requirement(self):
findings = [
generate_finding_output(
provider="gcp",
compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"},
account_uid=GCP_PROJECT_ID,
region=GCP_LOCATION,
)
]
output = CCC_GCP(findings, CCC_GCP_FIXTURE)
manual_row = output.data[1]
assert isinstance(manual_row, CCC_GCPModel)
assert manual_row.Provider == "gcp"
assert manual_row.ProjectId == ""
assert manual_row.Location == ""
assert manual_row.Requirements_Id == CCC_GCP_FIXTURE.Requirements[1].Id
assert manual_row.Status == "MANUAL"
assert manual_row.CheckId == "manual"
@freeze_time("2025-01-01 00:00:00")
@mock.patch(
"prowler.lib.outputs.compliance.ccc.ccc_gcp.timestamp",
"2025-01-01 00:00:00",
)
def test_batch_write_data_to_file(self):
mock_file = StringIO()
findings = [
generate_finding_output(
provider="gcp",
compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"},
account_uid=GCP_PROJECT_ID,
region=GCP_LOCATION,
)
]
output = CCC_GCP(findings, CCC_GCP_FIXTURE)
output._file_descriptor = mock_file
with patch.object(mock_file, "close", return_value=None):
output.batch_write_data_to_file()
mock_file.seek(0)
content = mock_file.read()
header = content.split("\r\n", 1)[0]
assert "PROJECTID" in header
assert "LOCATION" in header
assert "ACCOUNTID" not in header
assert "SUBSCRIPTIONID" not in header
assert "REGION" not in header
rows = [r for r in content.split("\r\n") if r]
assert len(rows) == 3
assert "CCC.Core.CN01.AR01" in rows[1]
assert GCP_PROJECT_ID in rows[1]
assert "CCC.IAM.CN01.AR01" in rows[2]
assert "MANUAL" in rows[2]
+167
View File
@@ -1,5 +1,6 @@
from prowler.lib.check.compliance_models import (
AWS_Well_Architected_Requirement_Attribute,
CCC_Requirement_Attribute,
CIS_Requirement_Attribute,
Compliance,
Compliance_Requirement,
@@ -1022,3 +1023,169 @@ PROWLER_THREATSCORE_M365 = Compliance(
),
],
)
# CCC fixtures cover the three providers Prowler ships catalogs for. Each
# fixture has one auto-evaluated requirement (with Checks) and one manual
# requirement (Checks=[]) so test suites can exercise both paths.
CCC_AWS_FIXTURE = Compliance(
Framework="CCC",
Name="Common Cloud Controls Catalog (CCC)",
Provider="AWS",
Version="v2025.10",
Description="Common Cloud Controls Catalog (CCC) for AWS",
Requirements=[
Compliance_Requirement(
Checks=["service_test_check_id"],
Id="CCC.Core.CN01.AR01",
Description="When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.",
Attributes=[
CCC_Requirement_Attribute(
FamilyName="Data",
FamilyDescription="The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.",
Section="CCC.Core.CN01 Encrypt Data for Transmission",
SubSection="",
SubSectionObjective="Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.",
Applicability=["tlp-green", "tlp-amber", "tlp-red"],
Recommendation="Most cloud services enable TLS 1.3 by default.",
SectionThreatMappings=[
{"ReferenceId": "CCC", "Identifiers": ["CCC.Core.TH02"]}
],
SectionGuidelineMappings=[
{"ReferenceId": "CCM", "Identifiers": ["CEK-03", "CEK-04"]}
],
)
],
),
Compliance_Requirement(
Checks=[],
Id="CCC.IAM.CN01.AR01",
Description="When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials or generating temporary session tokens.",
Attributes=[
CCC_Requirement_Attribute(
FamilyName="Identity and Access Management",
FamilyDescription="Controls that restrict who can access and modify IAM resources.",
Section="CCC.IAM.CN01 Restrict IAM User Credentials Creation",
SubSection="",
SubSectionObjective="Prevent non-administrative principals from creating new long-lived credentials.",
Applicability=["tlp-clear", "tlp-green", "tlp-amber", "tlp-red"],
Recommendation="",
SectionThreatMappings=[
{"ReferenceId": "CCC", "Identifiers": ["CCC.IAM.TH03"]}
],
SectionGuidelineMappings=[
{"ReferenceId": "NIST-CSF", "Identifiers": ["PR.AA-05"]}
],
)
],
),
],
)
CCC_AZURE_FIXTURE = Compliance(
Framework="CCC",
Name="Common Cloud Controls Catalog (CCC)",
Provider="Azure",
Version="v2025.10",
Description="Common Cloud Controls Catalog (CCC) for Azure",
Requirements=[
Compliance_Requirement(
Checks=["service_test_check_id"],
Id="CCC.Core.CN01.AR01",
Description="When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.",
Attributes=[
CCC_Requirement_Attribute(
FamilyName="Data",
FamilyDescription="The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.",
Section="CCC.Core.CN01 Encrypt Data for Transmission",
SubSection="",
SubSectionObjective="Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.",
Applicability=["tlp-green", "tlp-amber", "tlp-red"],
Recommendation="Most cloud services enable TLS 1.3 by default.",
SectionThreatMappings=[
{"ReferenceId": "CCC", "Identifiers": ["CCC.Core.TH02"]}
],
SectionGuidelineMappings=[
{"ReferenceId": "CCM", "Identifiers": ["CEK-03", "CEK-04"]}
],
)
],
),
Compliance_Requirement(
Checks=[],
Id="CCC.IAM.CN01.AR01",
Description="When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials.",
Attributes=[
CCC_Requirement_Attribute(
FamilyName="Identity and Access Management",
FamilyDescription="Controls that restrict who can access and modify IAM resources.",
Section="CCC.IAM.CN01 Restrict IAM User Credentials Creation",
SubSection="",
SubSectionObjective="Prevent non-administrative principals from creating new long-lived credentials.",
Applicability=["tlp-clear", "tlp-green", "tlp-amber", "tlp-red"],
Recommendation="",
SectionThreatMappings=[
{"ReferenceId": "CCC", "Identifiers": ["CCC.IAM.TH03"]}
],
SectionGuidelineMappings=[
{"ReferenceId": "NIST-CSF", "Identifiers": ["PR.AA-05"]}
],
)
],
),
],
)
CCC_GCP_FIXTURE = Compliance(
Framework="CCC",
Name="Common Cloud Controls Catalog (CCC)",
Provider="GCP",
Version="v2025.10",
Description="Common Cloud Controls Catalog (CCC) for GCP",
Requirements=[
Compliance_Requirement(
Checks=["service_test_check_id"],
Id="CCC.Core.CN01.AR01",
Description="When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.",
Attributes=[
CCC_Requirement_Attribute(
FamilyName="Data",
FamilyDescription="The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.",
Section="CCC.Core.CN01 Encrypt Data for Transmission",
SubSection="",
SubSectionObjective="Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.",
Applicability=["tlp-green", "tlp-amber", "tlp-red"],
Recommendation="Most cloud services enable TLS 1.3 by default.",
SectionThreatMappings=[
{"ReferenceId": "CCC", "Identifiers": ["CCC.Core.TH02"]}
],
SectionGuidelineMappings=[
{"ReferenceId": "CCM", "Identifiers": ["CEK-03", "CEK-04"]}
],
)
],
),
Compliance_Requirement(
Checks=[],
Id="CCC.IAM.CN01.AR01",
Description="When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials.",
Attributes=[
CCC_Requirement_Attribute(
FamilyName="Identity and Access Management",
FamilyDescription="Controls that restrict who can access and modify IAM resources.",
Section="CCC.IAM.CN01 Restrict IAM User Credentials Creation",
SubSection="",
SubSectionObjective="Prevent non-administrative principals from creating new long-lived credentials.",
Applicability=["tlp-clear", "tlp-green", "tlp-amber", "tlp-red"],
Recommendation="",
SectionThreatMappings=[
{"ReferenceId": "CCC", "Identifiers": ["CCC.IAM.TH03"]}
],
SectionGuidelineMappings=[
{"ReferenceId": "NIST-CSF", "Identifiers": ["PR.AA-05"]}
],
)
],
),
],
)