mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
feat(compliance): add Cyber Essentials 3.3 for Azure (#11588)
Co-authored-by: pedrooot <pedromarting3@gmail.com>
This commit is contained in:
co-authored by
pedrooot
parent
c89d900aae
commit
301edea7ce
@@ -0,0 +1 @@
|
||||
NCSC Cyber Essentials 3.3 compliance framework with Azure provider coverage across the five Cyber Essentials themes
|
||||
@@ -0,0 +1,605 @@
|
||||
{
|
||||
"framework": "Cyber-Essentials",
|
||||
"name": "NCSC Cyber Essentials: Requirements for IT Infrastructure",
|
||||
"version": "3.3",
|
||||
"description": "The UK National Cyber Security Centre (NCSC) Cyber Essentials scheme - Requirements for IT Infrastructure v3.3 (April 2026). Cyber Essentials organises its technical requirements into five control themes: Firewalls, Secure Configuration, Security Update Management, User Access Control, and Malware Protection. Cloud services are explicitly in scope for Cyber Essentials and cannot be excluded, so under the shared responsibility model this framework covers the controls the applicant organisation is responsible for implementing on its own cloud infrastructure (Infrastructure/Platform as a Service). Requirements that apply to end-user devices, on-premises network appliances, or organisational process and are not observable from cloud control-plane evidence are included for completeness with an empty check list and 'non-applicable' or 'Manual' attributes.",
|
||||
"icon": "cyber-essentials",
|
||||
"attributes_metadata": [
|
||||
{
|
||||
"key": "Theme",
|
||||
"label": "Technical Control Theme",
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"enum": [
|
||||
"Firewalls",
|
||||
"Secure Configuration",
|
||||
"Security Update Management",
|
||||
"User Access Control",
|
||||
"Malware Protection"
|
||||
],
|
||||
"output_formats": {
|
||||
"csv": true,
|
||||
"ocsf": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "AssessmentStatus",
|
||||
"label": "Assessment Status",
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"enum": [
|
||||
"Automated",
|
||||
"Manual"
|
||||
],
|
||||
"output_formats": {
|
||||
"csv": true,
|
||||
"ocsf": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "CloudApplicability",
|
||||
"label": "Cloud Applicability",
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"enum": [
|
||||
"full",
|
||||
"partial",
|
||||
"non-applicable"
|
||||
],
|
||||
"output_formats": {
|
||||
"csv": true,
|
||||
"ocsf": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "RemediationProcedure",
|
||||
"label": "Remediation Procedure",
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"output_formats": {
|
||||
"csv": true,
|
||||
"ocsf": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "References",
|
||||
"label": "References",
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"output_formats": {
|
||||
"csv": true,
|
||||
"ocsf": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"table_config": {
|
||||
"group_by": "Theme"
|
||||
},
|
||||
"pdf_config": {
|
||||
"language": "en",
|
||||
"primary_color": "#003D54",
|
||||
"secondary_color": "#0072CE",
|
||||
"bg_color": "#F0F4FA",
|
||||
"group_by_field": "Theme",
|
||||
"sections": [
|
||||
"Firewalls",
|
||||
"Secure Configuration",
|
||||
"Security Update Management",
|
||||
"User Access Control",
|
||||
"Malware Protection"
|
||||
],
|
||||
"section_short_names": {
|
||||
"Firewalls": "Firewalls",
|
||||
"Secure Configuration": "Secure Config",
|
||||
"Security Update Management": "Update Mgmt",
|
||||
"User Access Control": "Access Control",
|
||||
"Malware Protection": "Malware"
|
||||
},
|
||||
"charts": [
|
||||
{
|
||||
"id": "theme_compliance",
|
||||
"type": "horizontal_bar",
|
||||
"group_by": "Theme",
|
||||
"title": "Compliance Score by Cyber Essentials Theme",
|
||||
"y_label": "Theme",
|
||||
"x_label": "Compliance %",
|
||||
"value_source": "compliance_percent",
|
||||
"color_mode": "by_value"
|
||||
}
|
||||
],
|
||||
"filter": {
|
||||
"only_failed": true,
|
||||
"include_manual": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
{
|
||||
"id": "CE-FW-01",
|
||||
"name": "Boundary firewall on every in-scope device",
|
||||
"description": "Every device in scope must be protected by a correctly configured firewall or network device with firewall functionality, restricting inbound and outbound network services to those that are secure and necessary.",
|
||||
"attributes": {
|
||||
"Theme": "Firewalls",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Use Network Security Groups (or Azure Firewall) on every subnet/NIC and remove rules that allow unrestricted inbound access from the internet to RDP, SSH, and other management or data services.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"network_rdp_internet_access_restricted",
|
||||
"network_ssh_internet_access_restricted",
|
||||
"network_udp_internet_access_restricted",
|
||||
"network_http_internet_access_restricted"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-FW-02",
|
||||
"name": "Change default administrative passwords or disable remote admin access",
|
||||
"description": "Default administrative passwords on firewalls and network devices must be changed to a strong, unique password, or remote administrative access must be disabled entirely.",
|
||||
"attributes": {
|
||||
"Theme": "Firewalls",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "This requirement applies to on-premises network appliances and home/remote routers, which are outside the scope of cloud control-plane evidence. Manage and document this control as part of your organisation's device estate.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-FW-03",
|
||||
"name": "Restrict access to firewall/network device administrative interfaces",
|
||||
"description": "Access to the administrative interface used to manage firewall or network device configuration must not be possible from the internet, unless there is a documented business need and the interface is protected by MFA or an IP allow list combined with a managed password approach.",
|
||||
"attributes": {
|
||||
"Theme": "Firewalls",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "full",
|
||||
"RemediationProcedure": "Disable public network access on management-plane resources (storage accounts, Key Vaults) or restrict access to trusted networks/IP ranges, and require MFA for any administrative access exposed to the internet.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"storage_account_public_network_access_disabled",
|
||||
"storage_default_network_access_rule_is_denied",
|
||||
"keyvault_access_only_through_private_endpoints",
|
||||
"network_rdp_internet_access_restricted",
|
||||
"network_ssh_internet_access_restricted"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-FW-04",
|
||||
"name": "Block unauthenticated inbound connections by default",
|
||||
"description": "Firewalls and network devices must block unauthenticated inbound connections by default.",
|
||||
"attributes": {
|
||||
"Theme": "Firewalls",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "full",
|
||||
"RemediationProcedure": "Configure Network Security Group rules with a default-deny inbound posture and only allow specific, documented inbound services. Disable public network access on PaaS resources that do not require it.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"network_rdp_internet_access_restricted",
|
||||
"network_ssh_internet_access_restricted",
|
||||
"network_udp_internet_access_restricted",
|
||||
"network_http_internet_access_restricted",
|
||||
"storage_account_public_network_access_disabled"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-FW-05",
|
||||
"name": "Inbound firewall rules approved and documented",
|
||||
"description": "Inbound firewall rules must be approved and documented by an authorised person, including the business need for the rule.",
|
||||
"attributes": {
|
||||
"Theme": "Firewalls",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "Maintain a change-approval record (e.g. change tickets or a network rule register) for every inbound Network Security Group rule, including the business justification and approver.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-FW-06",
|
||||
"name": "Remove or disable unnecessary firewall rules",
|
||||
"description": "Firewall rules that are no longer needed must be removed or disabled.",
|
||||
"attributes": {
|
||||
"Theme": "Firewalls",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "Periodically review firewall and network access-control rules, removing or disabling any rule that no longer has a documented business need.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-FW-07",
|
||||
"name": "Software firewall on devices used on untrusted networks",
|
||||
"description": "Devices that connect to untrusted networks, such as public Wi-Fi hotspots, must use a software firewall.",
|
||||
"attributes": {
|
||||
"Theme": "Firewalls",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "This is an end-user device control (host-based firewall) and has no cloud control-plane equivalent. Enforce via endpoint management policy.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SC-01",
|
||||
"name": "Remove or disable unnecessary user accounts",
|
||||
"description": "Unnecessary user accounts, such as guest accounts and unused administrative accounts, must be removed or disabled.",
|
||||
"attributes": {
|
||||
"Theme": "Secure Configuration",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Restrict guest user invitations and access, and review Microsoft Entra ID and Azure RBAC role assignments to remove unused guest or administrative accounts.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"entra_policy_guest_users_access_restrictions",
|
||||
"entra_policy_guest_invite_only_for_admin_roles",
|
||||
"iam_role_user_access_admin_restricted"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SC-02",
|
||||
"name": "Change default or guessable account passwords",
|
||||
"description": "Default or guessable account passwords must be changed before a device or service is used.",
|
||||
"attributes": {
|
||||
"Theme": "Secure Configuration",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Enable Microsoft Entra ID security defaults (or an equivalent Conditional Access baseline) so that default/weak credentials cannot be used for sign-in.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"entra_security_defaults_enabled"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SC-03",
|
||||
"name": "Remove or disable unnecessary software",
|
||||
"description": "Unnecessary software, including applications, system utilities and network services, must be removed or disabled.",
|
||||
"attributes": {
|
||||
"Theme": "Secure Configuration",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "This is an operating-system level control for devices and servers and has no direct cloud control-plane equivalent. Maintain an approved software baseline and image hardening process for VM images.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SC-04",
|
||||
"name": "Disable auto-run features",
|
||||
"description": "Auto-run features that allow file execution without user authorisation must be disabled.",
|
||||
"attributes": {
|
||||
"Theme": "Secure Configuration",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "This is an operating-system level control and has no cloud control-plane equivalent. Disable AutoRun/AutoPlay via OS configuration or group policy on VM images.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SC-05",
|
||||
"name": "Authenticate users before granting access to organisational data or services",
|
||||
"description": "Users must be authenticated before being allowed access to organisational data or services, including cloud services.",
|
||||
"attributes": {
|
||||
"Theme": "Secure Configuration",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "full",
|
||||
"RemediationProcedure": "Disable anonymous/public access to storage and require authenticated, encrypted (TLS 1.2+) access. Use Azure RBAC for Key Vault data-plane access instead of access policies that allow unauthenticated retrieval.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"storage_secure_transfer_required_is_enabled",
|
||||
"storage_blob_public_access_level_is_disabled",
|
||||
"storage_account_public_network_access_disabled",
|
||||
"storage_ensure_minimum_tls_version_12",
|
||||
"keyvault_rbac_enabled"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SC-06",
|
||||
"name": "Device locking and brute-force protection for unlocking credentials",
|
||||
"description": "Devices that require a user's physical presence must use an unlocking credential (biometric, password or PIN) of at least 6 characters, protected against brute-force guessing by throttling or lockout after no more than 10 attempts.",
|
||||
"attributes": {
|
||||
"Theme": "Secure Configuration",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Enforce key-based SSH authentication on Linux VMs (disabling password authentication) and enable Microsoft Entra ID security defaults to apply baseline sign-in protections.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"vm_linux_enforce_ssh_authentication",
|
||||
"entra_security_defaults_enabled"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SUM-01",
|
||||
"name": "All software licensed and supported",
|
||||
"description": "All software on in-scope devices must be licensed and supported by the vendor.",
|
||||
"attributes": {
|
||||
"Theme": "Security Update Management",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "Maintain a software asset inventory confirming each product is licensed and has an active vendor support commitment, including operating systems and any third-party software running on cloud compute resources.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SUM-02",
|
||||
"name": "Remove unsupported software",
|
||||
"description": "Software must be removed from devices when it becomes unsupported, or isolated into a defined sub-set that prevents all internet traffic.",
|
||||
"attributes": {
|
||||
"Theme": "Security Update Management",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "Track vendor end-of-support dates for operating systems and applications running on cloud compute resources, and decommission or isolate workloads before support ends.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SUM-03",
|
||||
"name": "Automatic updates enabled where possible",
|
||||
"description": "Automatic updates must be enabled on in-scope software where this is possible.",
|
||||
"attributes": {
|
||||
"Theme": "Security Update Management",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Enable automatic updates on in-scope software where possible: configure platform-managed guest patching on cloud virtual machines and instance groups, and enable vendor auto-update mechanisms for any third-party software running on those workloads. Note: no check currently observes whether automatic updates are enabled (vulnerability assessment and security-posture provisioning only prove monitoring and assessment coverage), so this requirement must be verified manually until a dedicated check validating update automation exists.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-SUM-04",
|
||||
"name": "Critical and high-risk updates applied within 14 days",
|
||||
"description": "Updates that fix vulnerabilities described by the vendor as critical or high risk, or that address a CVSS v3 base score of 7 or above (or where severity is unspecified), must be applied within 14 days of release.",
|
||||
"attributes": {
|
||||
"Theme": "Security Update Management",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Enable Microsoft Defender for Cloud system update recommendations and vulnerability assessment, and remediate flagged virtual machines within 14 days of a critical or high-risk update being released.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"defender_ensure_system_updates_are_applied",
|
||||
"defender_auto_provisioning_vulnerabilty_assessments_machines_on"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-UAC-01",
|
||||
"name": "Process to create and approve user accounts",
|
||||
"description": "A documented process must be in place to create and approve user accounts before access is granted.",
|
||||
"attributes": {
|
||||
"Theme": "User Access Control",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "Maintain a joiner/mover/leaver process with documented approval steps for creating identity-provider accounts and assigning cloud roles.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-UAC-02",
|
||||
"name": "Authenticate users with unique credentials",
|
||||
"description": "Users must be authenticated with unique credentials before being granted access to applications or devices.",
|
||||
"attributes": {
|
||||
"Theme": "User Access Control",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Enable Microsoft Entra ID security defaults and prefer Entra ID authentication (over shared keys) for Azure resources such as storage accounts, so every user authenticates with their own unique identity.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"entra_security_defaults_enabled",
|
||||
"storage_default_to_entra_authorization_enabled"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-UAC-03",
|
||||
"name": "Remove or disable user accounts when no longer required",
|
||||
"description": "User accounts must be removed or disabled when they are no longer required, for example when a user leaves the organisation or after a defined period of inactivity.",
|
||||
"attributes": {
|
||||
"Theme": "User Access Control",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "Implement a leaver process and periodic access reviews (e.g. Microsoft Entra ID access reviews) to disable or remove accounts that are no longer required.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-UAC-04",
|
||||
"name": "Multi-factor authentication for cloud services",
|
||||
"description": "Multi-factor authentication must be implemented where available, and authentication to cloud services must always use MFA.",
|
||||
"attributes": {
|
||||
"Theme": "User Access Control",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "full",
|
||||
"RemediationProcedure": "Require multi-factor authentication for all users via Conditional Access policies, covering admin portals, the Azure management API, and users with access to virtual machines.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"entra_privileged_user_has_mfa",
|
||||
"entra_non_privileged_user_has_mfa",
|
||||
"entra_conditional_access_policy_require_mfa_for_admin_portals",
|
||||
"entra_conditional_access_policy_require_mfa_for_management_api",
|
||||
"entra_user_with_vm_access_has_mfa"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-UAC-05",
|
||||
"name": "Separate accounts for administrative activities",
|
||||
"description": "Separate accounts must be used to perform administrative activities only, with no email, web browsing or other standard user activity that could expose administrative privileges to avoidable risk.",
|
||||
"attributes": {
|
||||
"Theme": "User Access Control",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Limit the number of Global Administrator assignments, avoid granting the User Access Administrator or subscription Owner role broadly, and require named administrators to use dedicated privileged accounts for administrative tasks.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"entra_global_admin_in_less_than_five_users",
|
||||
"iam_role_user_access_admin_restricted",
|
||||
"iam_subscription_roles_owner_custom_not_created"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-UAC-06",
|
||||
"name": "Remove or disable special access privileges when no longer required",
|
||||
"description": "Special access privileges must be removed or disabled when they are no longer required, for example when a member of staff changes role.",
|
||||
"attributes": {
|
||||
"Theme": "User Access Control",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Periodically review Microsoft Entra ID directory role assignments and Azure RBAC role assignments, removing privileged roles that are no longer needed for a user's current role.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"entra_global_admin_in_less_than_five_users",
|
||||
"iam_role_user_access_admin_restricted"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-UAC-07",
|
||||
"name": "Protect password-based authentication against brute-force attacks",
|
||||
"description": "Where authentication is carried out using a password, accounts must be protected against brute-force guessing using MFA, attempt throttling, or account lockout.",
|
||||
"attributes": {
|
||||
"Theme": "User Access Control",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Enable Microsoft Entra ID security defaults (which include smart lockout) and require MFA, particularly for privileged accounts, to mitigate brute-force password attacks.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"entra_security_defaults_enabled",
|
||||
"entra_privileged_user_has_mfa"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-UAC-08",
|
||||
"name": "Technical controls to manage password quality",
|
||||
"description": "Technical controls must be used to manage the quality of passwords, using MFA, a minimum password length of at least 12 characters with no maximum length, or a minimum of 8 characters combined with a common-password deny list. Regular forced password expiry and complexity requirements should not be enforced.",
|
||||
"attributes": {
|
||||
"Theme": "User Access Control",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Enable Microsoft Entra ID security defaults and Microsoft Entra ID Password Protection (banned password list), and require MFA so that password length alone is not the only protection.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"entra_security_defaults_enabled",
|
||||
"entra_privileged_user_has_mfa",
|
||||
"entra_non_privileged_user_has_mfa"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-MP-01",
|
||||
"name": "Malware protection mechanism active on all in-scope devices",
|
||||
"description": "A malware protection mechanism must be active on all devices in scope, using anti-malware software, application allow listing, or application sandboxing.",
|
||||
"attributes": {
|
||||
"Theme": "Malware Protection",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Ensure endpoint protection is installed on all virtual machines, enable Microsoft Defender for Endpoint integration, and enable Microsoft Defender for Servers.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"defender_assessments_vm_endpoint_protection_installed",
|
||||
"defender_ensure_wdatp_is_enabled",
|
||||
"defender_ensure_defender_for_server_is_on"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-MP-02",
|
||||
"name": "Anti-malware software configuration",
|
||||
"description": "Anti-malware software must be kept up to date in line with vendor recommendations, prevent malware from running, prevent execution of malicious code, and prevent connections to malicious websites.",
|
||||
"attributes": {
|
||||
"Theme": "Malware Protection",
|
||||
"AssessmentStatus": "Automated",
|
||||
"CloudApplicability": "partial",
|
||||
"RemediationProcedure": "Enable Microsoft Defender for Endpoint, Microsoft Defender for Servers, and Microsoft Defender for Storage so that signatures stay current and malicious files, code execution and connections are blocked.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": [
|
||||
"defender_ensure_wdatp_is_enabled",
|
||||
"defender_ensure_defender_for_server_is_on",
|
||||
"defender_ensure_defender_for_storage_is_on"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CE-MP-03",
|
||||
"name": "Application allow listing",
|
||||
"description": "Where used as an alternative to anti-malware software, only approved applications restricted by code signing must be allowed to execute, with a maintained list of approved applications and no execution of unsigned or invalidly signed applications.",
|
||||
"attributes": {
|
||||
"Theme": "Malware Protection",
|
||||
"AssessmentStatus": "Manual",
|
||||
"CloudApplicability": "non-applicable",
|
||||
"RemediationProcedure": "This is an end-user device control implemented through application control policies (e.g. Microsoft Defender Application Control) and has no cloud control-plane equivalent.",
|
||||
"References": "NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section E"
|
||||
},
|
||||
"checks": {
|
||||
"azure": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -892,6 +892,60 @@ class TestSmokeLoadAllJSONs:
|
||||
assert len(fw.requirements) >= 0
|
||||
|
||||
|
||||
class TestCyberEssentialsFramework:
|
||||
"""Schema and content checks for the Cyber Essentials universal framework."""
|
||||
|
||||
@staticmethod
|
||||
def _path():
|
||||
base = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"prowler",
|
||||
"compliance",
|
||||
"cyber_essentials_3.3.json",
|
||||
)
|
||||
return os.path.normpath(base)
|
||||
|
||||
def test_loads_and_supports_azure(self):
|
||||
fw = load_compliance_framework_universal(self._path())
|
||||
assert fw is not None
|
||||
assert fw.framework == "Cyber-Essentials"
|
||||
assert fw.version == "3.3"
|
||||
assert fw.get_providers() == ["azure"]
|
||||
assert fw.supports_provider("azure")
|
||||
|
||||
def test_covers_all_five_themes(self):
|
||||
fw = load_compliance_framework_universal(self._path())
|
||||
themes = {req.attributes["Theme"] for req in fw.requirements}
|
||||
assert themes == {
|
||||
"Firewalls",
|
||||
"Secure Configuration",
|
||||
"Security Update Management",
|
||||
"User Access Control",
|
||||
"Malware Protection",
|
||||
}
|
||||
|
||||
def test_requirement_ids_are_unique(self):
|
||||
fw = load_compliance_framework_universal(self._path())
|
||||
ids = [req.id for req in fw.requirements]
|
||||
assert len(ids) == len(set(ids))
|
||||
|
||||
def test_attribute_values_conform_to_enums(self):
|
||||
fw = load_compliance_framework_universal(self._path())
|
||||
for req in fw.requirements:
|
||||
assert req.attributes["AssessmentStatus"] in {"Automated", "Manual"}
|
||||
assert req.attributes["CloudApplicability"] in {
|
||||
"full",
|
||||
"partial",
|
||||
"non-applicable",
|
||||
}
|
||||
# Requirements with no checks must not claim to be Automated.
|
||||
if not req.checks.get("azure"):
|
||||
assert req.attributes["AssessmentStatus"] == "Manual"
|
||||
|
||||
|
||||
class TestBackwardCompat:
|
||||
"""Ensure Compliance.get_bulk still returns Compliance objects."""
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
NCSC Cyber Essentials 3.3 compliance support with its dedicated mapper, details panel, and icon
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Requirement } from "@/types/compliance";
|
||||
|
||||
import {
|
||||
ComplianceBadge,
|
||||
ComplianceBadgeContainer,
|
||||
ComplianceDetailContainer,
|
||||
ComplianceDetailSection,
|
||||
ComplianceDetailText,
|
||||
} from "./shared-components";
|
||||
|
||||
interface CyberEssentialsDetailsProps {
|
||||
requirement: Requirement;
|
||||
}
|
||||
|
||||
export const CyberEssentialsCustomDetails = ({
|
||||
requirement,
|
||||
}: CyberEssentialsDetailsProps) => {
|
||||
return (
|
||||
<ComplianceDetailContainer>
|
||||
{requirement.description && (
|
||||
<ComplianceDetailSection title="Description">
|
||||
<ComplianceDetailText>{requirement.description}</ComplianceDetailText>
|
||||
</ComplianceDetailSection>
|
||||
)}
|
||||
|
||||
<ComplianceBadgeContainer>
|
||||
{requirement.theme && (
|
||||
<ComplianceBadge
|
||||
label="Theme"
|
||||
value={requirement.theme as string}
|
||||
variant="tag"
|
||||
/>
|
||||
)}
|
||||
{requirement.assessment_status && (
|
||||
<ComplianceBadge
|
||||
label="Assessment Status"
|
||||
value={requirement.assessment_status as string}
|
||||
variant="info"
|
||||
/>
|
||||
)}
|
||||
{requirement.cloud_applicability && (
|
||||
<ComplianceBadge
|
||||
label="Cloud Applicability"
|
||||
value={requirement.cloud_applicability as string}
|
||||
variant="secondary"
|
||||
/>
|
||||
)}
|
||||
</ComplianceBadgeContainer>
|
||||
|
||||
{requirement.remediation_procedure && (
|
||||
<ComplianceDetailSection title="Remediation Procedure">
|
||||
<ComplianceDetailText>
|
||||
{requirement.remediation_procedure as string}
|
||||
</ComplianceDetailText>
|
||||
</ComplianceDetailSection>
|
||||
)}
|
||||
|
||||
{requirement.references && (
|
||||
<ComplianceDetailSection title="References">
|
||||
<ComplianceDetailText>
|
||||
{requirement.references as string}
|
||||
</ComplianceDetailText>
|
||||
</ComplianceDetailSection>
|
||||
)}
|
||||
</ComplianceDetailContainer>
|
||||
);
|
||||
};
|
||||
@@ -70,6 +70,20 @@ describe("getComplianceIcon", () => {
|
||||
expect(getComplianceIcon("asd_essential_eight_aws")).toBe(essentialLogo);
|
||||
});
|
||||
|
||||
it("resolves Cyber Essentials distinctly from ASD Essential Eight", () => {
|
||||
// `essentials` (plural) must win over the `essential` keyword, otherwise
|
||||
// NCSC Cyber Essentials would collapse to the ASD Essential Eight logo.
|
||||
const cyberEssentialsLogo = getComplianceIcon("cyber_essentials_3.3");
|
||||
const asdEssentialEightLogo = getComplianceIcon(
|
||||
"asd_essential_eight_aws",
|
||||
);
|
||||
expect(cyberEssentialsLogo).toBeDefined();
|
||||
expect(cyberEssentialsLogo).not.toBe(asdEssentialEightLogo);
|
||||
expect(getComplianceIcon("NCSC Cyber Essentials")).toBe(
|
||||
cyberEssentialsLogo,
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves NIS2 distinctly from NIST", () => {
|
||||
const nis2Logo = getComplianceIcon("NIS2");
|
||||
const nistLogo = getComplianceIcon("NIST-800-53");
|
||||
|
||||
@@ -7,6 +7,7 @@ import CISLogo from "./cis.svg";
|
||||
import CISALogo from "./cisa.svg";
|
||||
import CMMCLogo from "./cmmc.svg";
|
||||
import CSALogo from "./csa.svg";
|
||||
import CyberEssentialsLogo from "./cyber-essentials.svg";
|
||||
import DORALogo from "./dora.svg";
|
||||
import ENSLogo from "./ens.png";
|
||||
import FedRAMPLogo from "./fedramp.svg";
|
||||
@@ -41,6 +42,14 @@ import SOC2Logo from "./soc2.svg";
|
||||
// Best Practices, Account Security Onboarding, Foundational Technical Review)
|
||||
// fall through to it because they expose no other matching keyword.
|
||||
const COMPLIANCE_LOGOS = [
|
||||
// `essentials` (plural) MUST come before `essential` (singular). NCSC Cyber
|
||||
// Essentials ids/names contain `essentials` (e.g. `cyber_essentials_3.3`,
|
||||
// "NCSC Cyber Essentials"), whereas ASD Essential Eight is `essential_eight`
|
||||
// (no trailing `s`). Without this ordering the `essential` keyword below
|
||||
// would shadow Cyber Essentials and resolve it to the ASD Essential Eight
|
||||
// logo. `cyber` alone is avoided because it also matches
|
||||
// `rbi_cyber_security_framework`.
|
||||
["essentials", CyberEssentialsLogo],
|
||||
["essential", ASDEssentialEightLogo],
|
||||
["cisa", CISALogo],
|
||||
["cis", CISLogo],
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 430 130" fill="none">
|
||||
<!-- Mark: navy disc with a green leaf and a light-blue check swoosh -->
|
||||
<circle cx="62" cy="65" r="52" fill="#20204E"/>
|
||||
<path d="M44,46 Q62,60 80,46 Q74,79 62,98 Q50,79 44,46 Z" fill="#8CC63F"/>
|
||||
<path d="M55,96 C64,78 80,61 99,45 C111,34 123,25 132,18 L147,32 C135,45 117,62 99,77 C85,88 71,93 55,96 Z" fill="#7CB9E2"/>
|
||||
<!-- Wordmark -->
|
||||
<text x="132" y="58" font-family="Helvetica, Arial, sans-serif" font-size="38" font-weight="700" fill="#7CB9E2" letter-spacing="3">CYBER</text>
|
||||
<text x="132" y="104" font-family="Helvetica, Arial, sans-serif" font-size="38" font-weight="700" fill="#8CC63F" letter-spacing="1">ESSENTIALS</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 729 B |
@@ -46,6 +46,10 @@ vi.mock(
|
||||
"@/components/compliance/compliance-custom-details/csa-details",
|
||||
() => ({ CSACustomDetails: stubFactory("CSAStub") }),
|
||||
);
|
||||
vi.mock(
|
||||
"@/components/compliance/compliance-custom-details/cyber-essentials-details",
|
||||
() => ({ CyberEssentialsCustomDetails: stubFactory("CyberEssentialsStub") }),
|
||||
);
|
||||
vi.mock(
|
||||
"@/components/compliance/compliance-custom-details/ens-details",
|
||||
() => ({ ENSCustomDetails: stubFactory("ENSStub") }),
|
||||
@@ -154,6 +158,7 @@ describe("getComplianceMapper", () => {
|
||||
{ framework: "CSA-CCM", expected: "CSAStub" },
|
||||
{ framework: "CMMC", expected: "CMMCStub" },
|
||||
{ framework: "Okta-IDaaS-STIG", expected: "OktaIDaaSStigStub" },
|
||||
{ framework: "Cyber-Essentials", expected: "CyberEssentialsStub" },
|
||||
];
|
||||
|
||||
for (const { framework, expected } of wiring) {
|
||||
@@ -200,6 +205,7 @@ describe("getComplianceMapper", () => {
|
||||
"CSA-CCM",
|
||||
"CMMC",
|
||||
"Okta-IDaaS-STIG",
|
||||
"Cyber-Essentials",
|
||||
]) {
|
||||
const mapper = getComplianceMapper(framework);
|
||||
expect(Object.keys(mapper).sort(), framework).toEqual(expectedKeys);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { CISControlsCustomDetails } from "@/components/compliance/compliance-cus
|
||||
import { CISCustomDetails } from "@/components/compliance/compliance-custom-details/cis-details";
|
||||
import { CMMCCustomDetails } from "@/components/compliance/compliance-custom-details/cmmc-details";
|
||||
import { CSACustomDetails } from "@/components/compliance/compliance-custom-details/csa-details";
|
||||
import { CyberEssentialsCustomDetails } from "@/components/compliance/compliance-custom-details/cyber-essentials-details";
|
||||
import { DORACustomDetails } from "@/components/compliance/compliance-custom-details/dora-details";
|
||||
import { ENSCustomDetails } from "@/components/compliance/compliance-custom-details/ens-details";
|
||||
import { GenericCustomDetails } from "@/components/compliance/compliance-custom-details/generic-details";
|
||||
@@ -59,6 +60,10 @@ import {
|
||||
mapComplianceData as mapCSAComplianceData,
|
||||
toAccordionItems as toCSAAccordionItems,
|
||||
} from "./csa";
|
||||
import {
|
||||
mapComplianceData as mapCyberEssentialsComplianceData,
|
||||
toAccordionItems as toCyberEssentialsAccordionItems,
|
||||
} from "./cyber-essentials";
|
||||
import {
|
||||
mapComplianceData as mapDORAComplianceData,
|
||||
toAccordionItems as toDORAAccordionItems,
|
||||
@@ -264,6 +269,20 @@ const getComplianceMappers = (): Record<string, ComplianceMapper> => ({
|
||||
getDetailsComponent: (requirement: Requirement) =>
|
||||
createElement(DORACustomDetails, { requirement }),
|
||||
},
|
||||
// Cyber Essentials v3.3 — universal framework keyed by the `framework` field
|
||||
// of `prowler/compliance/cyber_essentials_3.3.json` ("Cyber-Essentials").
|
||||
// Groups by Theme (the 5 NCSC control themes) and surfaces Theme /
|
||||
// AssessmentStatus / CloudApplicability / RemediationProcedure / References in
|
||||
// the requirement detail drawer.
|
||||
"Cyber-Essentials": {
|
||||
mapComplianceData: mapCyberEssentialsComplianceData,
|
||||
toAccordionItems: toCyberEssentialsAccordionItems,
|
||||
getTopFailedSections,
|
||||
calculateCategoryHeatmapData: (data: Framework[]) =>
|
||||
calculateCategoryHeatmapData(data),
|
||||
getDetailsComponent: (requirement: Requirement) =>
|
||||
createElement(CyberEssentialsCustomDetails, { requirement }),
|
||||
},
|
||||
// CMMC 2.0 — universal framework keyed by the `framework` field of
|
||||
// `prowler/compliance/cmmc_2.0.json` ("CMMC"). Groups by Domain (14 NIST
|
||||
// 800-171 families) and surfaces Domain / Level / Source Requirement in the
|
||||
|
||||
@@ -0,0 +1,521 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
// `cyber-essentials.tsx` re-exports `toAccordionItems`, which builds JSX
|
||||
// referencing the client-side accordion components. Those components
|
||||
// transitively import server-only code (next-auth → next/server) and would
|
||||
// crash vitest at load time. Mocking the JSX deps lets us load the module and
|
||||
// exercise the real `mapComplianceData` and `toAccordionItems` functions,
|
||||
// which are what we actually want to test.
|
||||
vi.mock(
|
||||
"@/components/compliance/compliance-accordion/client-accordion-content",
|
||||
() => ({
|
||||
ClientAccordionContent: () => null,
|
||||
}),
|
||||
);
|
||||
vi.mock(
|
||||
"@/components/compliance/compliance-accordion/compliance-accordion-requeriment-title",
|
||||
() => ({
|
||||
ComplianceAccordionRequirementTitle: () => null,
|
||||
}),
|
||||
);
|
||||
vi.mock(
|
||||
"@/components/compliance/compliance-accordion/compliance-accordion-title",
|
||||
() => ({
|
||||
ComplianceAccordionTitle: () => null,
|
||||
}),
|
||||
);
|
||||
|
||||
import {
|
||||
AttributesData,
|
||||
AttributesItemData,
|
||||
CyberEssentialsAttributesMetadata,
|
||||
CyberEssentialsTheme,
|
||||
REQUIREMENT_STATUS,
|
||||
RequirementItemData,
|
||||
RequirementsData,
|
||||
RequirementStatus,
|
||||
} from "@/types/compliance";
|
||||
|
||||
import {
|
||||
CYBER_ESSENTIALS_THEME_ORDER,
|
||||
mapComplianceData,
|
||||
toAccordionItems,
|
||||
} from "./cyber-essentials";
|
||||
|
||||
const FRAMEWORK = "Cyber-Essentials";
|
||||
|
||||
const baseMetadata = (
|
||||
overrides: Partial<CyberEssentialsAttributesMetadata> = {},
|
||||
): CyberEssentialsAttributesMetadata => ({
|
||||
Theme: "Firewalls",
|
||||
AssessmentStatus: "Automated",
|
||||
CloudApplicability: "full",
|
||||
RemediationProcedure: "Steps to remediate.",
|
||||
References: "https://example.com/a",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const buildAttribute = (
|
||||
id: string,
|
||||
metadata: CyberEssentialsAttributesMetadata,
|
||||
{
|
||||
name,
|
||||
description = "Canonical Cyber Essentials clause text.",
|
||||
checks = ["check_one"],
|
||||
}: { name?: string; description?: string; checks?: string[] } = {},
|
||||
): AttributesItemData => ({
|
||||
type: "compliance-requirements-attributes",
|
||||
id,
|
||||
attributes: {
|
||||
framework_description: "NCSC Cyber Essentials",
|
||||
framework: FRAMEWORK,
|
||||
...(name !== undefined ? { name } : {}),
|
||||
version: "3.3",
|
||||
description,
|
||||
attributes: {
|
||||
metadata: [metadata],
|
||||
check_ids: checks,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const buildRequirement = (
|
||||
id: string,
|
||||
status: RequirementStatus = REQUIREMENT_STATUS.PASS,
|
||||
): RequirementItemData => ({
|
||||
type: "compliance-requirements-details",
|
||||
id,
|
||||
attributes: {
|
||||
framework: FRAMEWORK,
|
||||
version: "3.3",
|
||||
description: "Canonical clause text.",
|
||||
status,
|
||||
},
|
||||
});
|
||||
|
||||
const buildInputs = (
|
||||
pairs: Array<{
|
||||
attribute: AttributesItemData;
|
||||
requirement: RequirementItemData;
|
||||
}>,
|
||||
): { attributesData: AttributesData; requirementsData: RequirementsData } => ({
|
||||
attributesData: { data: pairs.map((p) => p.attribute) },
|
||||
requirementsData: { data: pairs.map((p) => p.requirement) },
|
||||
});
|
||||
|
||||
// One requirement per theme, intentionally supplied OUT of canonical order so
|
||||
// the sort under test has something to reorder.
|
||||
const oneRequirementPerThemeUnordered = (): Array<{
|
||||
attribute: AttributesItemData;
|
||||
requirement: RequirementItemData;
|
||||
}> => {
|
||||
const themesOutOfOrder: CyberEssentialsTheme[] = [
|
||||
"Malware Protection",
|
||||
"User Access Control",
|
||||
"Firewalls",
|
||||
"Security Update Management",
|
||||
"Secure Configuration",
|
||||
];
|
||||
return themesOutOfOrder.map((theme, index) => {
|
||||
const id = `CE-${index}`;
|
||||
return {
|
||||
attribute: buildAttribute(id, baseMetadata({ Theme: theme })),
|
||||
requirement: buildRequirement(id),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
describe("mapComplianceData (Cyber Essentials)", () => {
|
||||
it("returns an empty list when there are no attributes", () => {
|
||||
const { attributesData, requirementsData } = buildInputs([]);
|
||||
expect(mapComplianceData(attributesData, requirementsData)).toEqual([]);
|
||||
});
|
||||
|
||||
describe("five-theme grouping and order", () => {
|
||||
it("orders the five themes into the canonical reading order regardless of API order", () => {
|
||||
const { attributesData, requirementsData } = buildInputs(
|
||||
oneRequirementPerThemeUnordered(),
|
||||
);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
|
||||
expect(framework.categories.map((c) => c.name)).toEqual([
|
||||
...CYBER_ESSENTIALS_THEME_ORDER,
|
||||
]);
|
||||
});
|
||||
|
||||
it("groups every requirement sharing a Theme under a single category", () => {
|
||||
const pairs = [
|
||||
{
|
||||
attribute: buildAttribute(
|
||||
"CE-FW-1",
|
||||
baseMetadata({ Theme: "Firewalls" }),
|
||||
),
|
||||
requirement: buildRequirement("CE-FW-1"),
|
||||
},
|
||||
{
|
||||
attribute: buildAttribute(
|
||||
"CE-FW-2",
|
||||
baseMetadata({ Theme: "Firewalls" }),
|
||||
),
|
||||
requirement: buildRequirement("CE-FW-2"),
|
||||
},
|
||||
{
|
||||
attribute: buildAttribute(
|
||||
"CE-MP-1",
|
||||
baseMetadata({ Theme: "Malware Protection" }),
|
||||
),
|
||||
requirement: buildRequirement("CE-MP-1"),
|
||||
},
|
||||
];
|
||||
const { attributesData, requirementsData } = buildInputs(pairs);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
|
||||
const firewalls = framework.categories.find(
|
||||
(c) => c.name === "Firewalls",
|
||||
);
|
||||
const malware = framework.categories.find(
|
||||
(c) => c.name === "Malware Protection",
|
||||
);
|
||||
|
||||
expect(framework.categories).toHaveLength(2);
|
||||
// Flat 2-level structure: theme → single control → requirements.
|
||||
expect(firewalls?.controls).toHaveLength(1);
|
||||
expect(firewalls?.controls[0].requirements).toHaveLength(2);
|
||||
expect(malware?.controls[0].requirements).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("sinks an unknown theme below the five canonical themes", () => {
|
||||
const pairs = [
|
||||
{
|
||||
attribute: buildAttribute("CE-X-1", {
|
||||
...baseMetadata(),
|
||||
Theme: "Some Future Theme" as CyberEssentialsTheme,
|
||||
}),
|
||||
requirement: buildRequirement("CE-X-1"),
|
||||
},
|
||||
{
|
||||
attribute: buildAttribute(
|
||||
"CE-MP-1",
|
||||
baseMetadata({ Theme: "Malware Protection" }),
|
||||
),
|
||||
requirement: buildRequirement("CE-MP-1"),
|
||||
},
|
||||
];
|
||||
const { attributesData, requirementsData } = buildInputs(pairs);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
|
||||
expect(framework.categories.map((c) => c.name)).toEqual([
|
||||
"Malware Protection",
|
||||
"Some Future Theme",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("status counters", () => {
|
||||
it("derives per-requirement counters from RequirementStatus", () => {
|
||||
const cases: Array<{
|
||||
status: RequirementStatus;
|
||||
expected: "pass" | "fail" | "manual";
|
||||
}> = [
|
||||
{ status: REQUIREMENT_STATUS.PASS, expected: "pass" },
|
||||
{ status: REQUIREMENT_STATUS.FAIL, expected: "fail" },
|
||||
{ status: REQUIREMENT_STATUS.MANUAL, expected: "manual" },
|
||||
];
|
||||
|
||||
for (const { status, expected } of cases) {
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute(`CE-${status}`, baseMetadata()),
|
||||
requirement: buildRequirement(`CE-${status}`, status),
|
||||
},
|
||||
]);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
const requirementOut =
|
||||
framework.categories[0].controls[0].requirements[0];
|
||||
|
||||
expect(requirementOut.pass).toBe(expected === "pass" ? 1 : 0);
|
||||
expect(requirementOut.fail).toBe(expected === "fail" ? 1 : 0);
|
||||
expect(requirementOut.manual).toBe(expected === "manual" ? 1 : 0);
|
||||
}
|
||||
});
|
||||
|
||||
it("aggregates counters up through category and framework levels", () => {
|
||||
const pairs = [
|
||||
{
|
||||
attribute: buildAttribute(
|
||||
"CE-FW-1",
|
||||
baseMetadata({ Theme: "Firewalls" }),
|
||||
),
|
||||
requirement: buildRequirement("CE-FW-1", REQUIREMENT_STATUS.PASS),
|
||||
},
|
||||
{
|
||||
attribute: buildAttribute(
|
||||
"CE-FW-2",
|
||||
baseMetadata({ Theme: "Firewalls" }),
|
||||
),
|
||||
requirement: buildRequirement("CE-FW-2", REQUIREMENT_STATUS.FAIL),
|
||||
},
|
||||
{
|
||||
attribute: buildAttribute(
|
||||
"CE-MP-1",
|
||||
baseMetadata({ Theme: "Malware Protection" }),
|
||||
),
|
||||
requirement: buildRequirement("CE-MP-1", REQUIREMENT_STATUS.MANUAL),
|
||||
},
|
||||
];
|
||||
const { attributesData, requirementsData } = buildInputs(pairs);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
|
||||
const firewalls = framework.categories.find(
|
||||
(c) => c.name === "Firewalls",
|
||||
)!;
|
||||
expect(firewalls.pass).toBe(1);
|
||||
expect(firewalls.fail).toBe(1);
|
||||
expect(firewalls.manual).toBe(0);
|
||||
|
||||
expect(framework.pass).toBe(1);
|
||||
expect(framework.fail).toBe(1);
|
||||
expect(framework.manual).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("manual requirements with empty check lists", () => {
|
||||
it("carries through a MANUAL requirement that has no checks", () => {
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute(
|
||||
"CE-SUM-03",
|
||||
baseMetadata({
|
||||
Theme: "Security Update Management",
|
||||
AssessmentStatus: "Manual",
|
||||
CloudApplicability: "partial",
|
||||
}),
|
||||
{ name: "Automatic updates enabled where possible", checks: [] },
|
||||
),
|
||||
requirement: buildRequirement("CE-SUM-03", REQUIREMENT_STATUS.MANUAL),
|
||||
},
|
||||
]);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
const requirementOut =
|
||||
framework.categories[0].controls[0].requirements[0];
|
||||
|
||||
expect(requirementOut.check_ids).toEqual([]);
|
||||
expect(requirementOut.manual).toBe(1);
|
||||
expect(requirementOut.assessment_status).toBe("Manual");
|
||||
});
|
||||
|
||||
it("defaults check_ids to an empty array when the attribute omits them", () => {
|
||||
const attribute = buildAttribute("CE-SUM-01", baseMetadata());
|
||||
// Drop check_ids entirely to mimic an API payload without the field.
|
||||
delete (attribute.attributes.attributes as { check_ids?: string[] })
|
||||
.check_ids;
|
||||
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{ attribute, requirement: buildRequirement("CE-SUM-01") },
|
||||
]);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
expect(
|
||||
framework.categories[0].controls[0].requirements[0].check_ids,
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps findings enabled for a manual requirement with no checks", () => {
|
||||
// A MANUAL requirement carries a manual count of 1, so the accordion
|
||||
// still surfaces its (manual) finding even though it has no checks —
|
||||
// findings are only disabled when there is nothing to show at all
|
||||
// (empty checks AND no manual count).
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute("CE-SUM-03", baseMetadata(), {
|
||||
checks: [],
|
||||
}),
|
||||
requirement: buildRequirement("CE-SUM-03", REQUIREMENT_STATUS.MANUAL),
|
||||
},
|
||||
]);
|
||||
|
||||
const frameworks = mapComplianceData(attributesData, requirementsData);
|
||||
const items = toAccordionItems(frameworks, "scan-1");
|
||||
const content = items[0].items![0].content as {
|
||||
props: { disableFindings: boolean };
|
||||
};
|
||||
|
||||
expect(content.props.disableFindings).toBe(false);
|
||||
});
|
||||
|
||||
it("disables findings for a non-manual requirement that has no checks", () => {
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute("CE-SUM-01", baseMetadata(), {
|
||||
checks: [],
|
||||
}),
|
||||
requirement: buildRequirement("CE-SUM-01", REQUIREMENT_STATUS.PASS),
|
||||
},
|
||||
]);
|
||||
|
||||
const frameworks = mapComplianceData(attributesData, requirementsData);
|
||||
const items = toAccordionItems(frameworks, "scan-1");
|
||||
const content = items[0].items![0].content as {
|
||||
props: { disableFindings: boolean };
|
||||
};
|
||||
|
||||
expect(content.props.disableFindings).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("preservation of Cyber Essentials metadata fields", () => {
|
||||
it("propagates Theme, AssessmentStatus, CloudApplicability, RemediationProcedure and References", () => {
|
||||
const metadata = baseMetadata({
|
||||
Theme: "User Access Control",
|
||||
AssessmentStatus: "Manual",
|
||||
CloudApplicability: "partial",
|
||||
RemediationProcedure: "Remediate the access control gap.",
|
||||
References:
|
||||
"NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section D",
|
||||
});
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute("CE-UAC-1", metadata),
|
||||
requirement: buildRequirement("CE-UAC-1"),
|
||||
},
|
||||
]);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
const requirementOut =
|
||||
framework.categories[0].controls[0].requirements[0];
|
||||
|
||||
expect(requirementOut.theme).toBe("User Access Control");
|
||||
expect(requirementOut.assessment_status).toBe("Manual");
|
||||
expect(requirementOut.cloud_applicability).toBe("partial");
|
||||
expect(requirementOut.remediation_procedure).toBe(
|
||||
"Remediate the access control gap.",
|
||||
);
|
||||
expect(requirementOut.references).toBe(
|
||||
"NCSC Cyber Essentials: Requirements for IT Infrastructure v3.3 (April 2026), Section D",
|
||||
);
|
||||
});
|
||||
|
||||
it("prefixes the requirement name with its id when a name is present", () => {
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute("CE-FW-1", baseMetadata(), {
|
||||
name: "Boundary firewalls in place",
|
||||
}),
|
||||
requirement: buildRequirement("CE-FW-1"),
|
||||
},
|
||||
]);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
expect(framework.categories[0].controls[0].requirements[0].name).toBe(
|
||||
"CE-FW-1 - Boundary firewalls in place",
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to the bare id when no name is supplied", () => {
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute("CE-FW-1", baseMetadata()),
|
||||
requirement: buildRequirement("CE-FW-1"),
|
||||
},
|
||||
]);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
expect(framework.categories[0].controls[0].requirements[0].name).toBe(
|
||||
"CE-FW-1",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses the literal API description for the requirement description", () => {
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute("CE-FW-1", baseMetadata(), {
|
||||
description: "Boundary firewalls must be configured.",
|
||||
}),
|
||||
requirement: buildRequirement("CE-FW-1"),
|
||||
},
|
||||
]);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
expect(
|
||||
framework.categories[0].controls[0].requirements[0].description,
|
||||
).toBe("Boundary firewalls must be configured.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("skipping malformed entries", () => {
|
||||
it("skips attribute items whose metadata is missing", () => {
|
||||
const valid = buildAttribute("CE-FW-1", baseMetadata());
|
||||
const broken = buildAttribute("CE-FW-2", baseMetadata());
|
||||
broken.attributes.attributes.metadata = [];
|
||||
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{ attribute: valid, requirement: buildRequirement("CE-FW-1") },
|
||||
{ attribute: broken, requirement: buildRequirement("CE-FW-2") },
|
||||
]);
|
||||
|
||||
const [framework] = mapComplianceData(attributesData, requirementsData);
|
||||
expect(framework.categories[0].controls[0].requirements).toHaveLength(1);
|
||||
expect(framework.categories[0].controls[0].requirements[0].name).toBe(
|
||||
"CE-FW-1",
|
||||
);
|
||||
});
|
||||
|
||||
it("skips attribute items without a matching requirement entry", () => {
|
||||
const result = mapComplianceData(
|
||||
{
|
||||
data: [
|
||||
buildAttribute("CE-FW-1", baseMetadata()),
|
||||
buildAttribute("CE-FW-2", baseMetadata()),
|
||||
],
|
||||
},
|
||||
{ data: [buildRequirement("CE-FW-1")] },
|
||||
);
|
||||
|
||||
expect(result[0].categories[0].controls[0].requirements).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("toAccordionItems (Cyber Essentials)", () => {
|
||||
it("produces one accordion item per theme, in canonical order", () => {
|
||||
const { attributesData, requirementsData } = buildInputs(
|
||||
oneRequirementPerThemeUnordered(),
|
||||
);
|
||||
|
||||
const frameworks = mapComplianceData(attributesData, requirementsData);
|
||||
const items = toAccordionItems(frameworks, "scan-1");
|
||||
|
||||
expect(items.map((item) => item.key)).toEqual(
|
||||
CYBER_ESSENTIALS_THEME_ORDER.map((theme) => `${FRAMEWORK}-${theme}`),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns an empty list when given no frameworks", () => {
|
||||
expect(toAccordionItems([], "scan-1")).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps findings enabled for an automated requirement that has checks", () => {
|
||||
const { attributesData, requirementsData } = buildInputs([
|
||||
{
|
||||
attribute: buildAttribute("CE-FW-1", baseMetadata(), {
|
||||
checks: ["check_one"],
|
||||
}),
|
||||
requirement: buildRequirement("CE-FW-1"),
|
||||
},
|
||||
]);
|
||||
|
||||
const frameworks = mapComplianceData(attributesData, requirementsData);
|
||||
const items = toAccordionItems(frameworks, "scan-1");
|
||||
const content = items[0].items![0].content as {
|
||||
props: { disableFindings: boolean };
|
||||
};
|
||||
|
||||
expect(content.props.disableFindings).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,158 @@
|
||||
import { ClientAccordionContent } from "@/components/compliance/compliance-accordion/client-accordion-content";
|
||||
import { ComplianceAccordionRequirementTitle } from "@/components/compliance/compliance-accordion/compliance-accordion-requeriment-title";
|
||||
import { ComplianceAccordionTitle } from "@/components/compliance/compliance-accordion/compliance-accordion-title";
|
||||
import { AccordionItemProps } from "@/components/shadcn/accordion/Accordion";
|
||||
import { FindingStatus } from "@/components/shadcn/table/status-finding-badge";
|
||||
import {
|
||||
AttributesData,
|
||||
CyberEssentialsAttributesMetadata,
|
||||
Framework,
|
||||
Requirement,
|
||||
REQUIREMENT_STATUS,
|
||||
RequirementsData,
|
||||
RequirementStatus,
|
||||
} from "@/types/compliance";
|
||||
|
||||
import {
|
||||
calculateFrameworkCounters,
|
||||
createRequirementsMap,
|
||||
findOrCreateCategory,
|
||||
findOrCreateControl,
|
||||
findOrCreateFramework,
|
||||
} from "./commons";
|
||||
|
||||
// Display order for the five Cyber Essentials control themes in the accordion
|
||||
// and any grouped chart. Mirrors the order declared in
|
||||
// `prowler/compliance/cyber_essentials_3.3.json` so the UI always renders
|
||||
// themes in the canonical reading order regardless of API response order.
|
||||
export const CYBER_ESSENTIALS_THEME_ORDER: readonly string[] = [
|
||||
"Firewalls",
|
||||
"Secure Configuration",
|
||||
"Security Update Management",
|
||||
"User Access Control",
|
||||
"Malware Protection",
|
||||
];
|
||||
|
||||
const getStatusCounters = (status: RequirementStatus) => ({
|
||||
pass: status === REQUIREMENT_STATUS.PASS ? 1 : 0,
|
||||
fail: status === REQUIREMENT_STATUS.FAIL ? 1 : 0,
|
||||
manual: status === REQUIREMENT_STATUS.MANUAL ? 1 : 0,
|
||||
});
|
||||
|
||||
export const mapComplianceData = (
|
||||
attributesData: AttributesData,
|
||||
requirementsData: RequirementsData,
|
||||
): Framework[] => {
|
||||
const attributes = attributesData?.data || [];
|
||||
const requirementsMap = createRequirementsMap(requirementsData);
|
||||
const frameworks: Framework[] = [];
|
||||
|
||||
for (const attributeItem of attributes) {
|
||||
const id = attributeItem.id;
|
||||
const metadataArray = attributeItem.attributes?.attributes
|
||||
?.metadata as unknown as CyberEssentialsAttributesMetadata[];
|
||||
const attrs = metadataArray?.[0];
|
||||
if (!attrs) continue;
|
||||
|
||||
const requirementData = requirementsMap.get(id);
|
||||
if (!requirementData) continue;
|
||||
|
||||
const frameworkName = attributeItem.attributes.framework;
|
||||
// Group by Theme (top-level accordion section). The remaining attributes
|
||||
// live inside the requirement so they show up on the detail drawer.
|
||||
const categoryName = attrs.Theme;
|
||||
const requirementName = attributeItem.attributes.name || "";
|
||||
const description = attributeItem.attributes.description;
|
||||
const status = requirementData.attributes.status || "";
|
||||
const checks = attributeItem.attributes.attributes.check_ids || [];
|
||||
|
||||
const framework = findOrCreateFramework(frameworks, frameworkName);
|
||||
const category = findOrCreateCategory(framework.categories, categoryName);
|
||||
// Flat 2-level structure: theme → requirements (no intermediate control).
|
||||
const control = findOrCreateControl(category.controls, categoryName);
|
||||
|
||||
const finalStatus: RequirementStatus = status as RequirementStatus;
|
||||
const requirement: Requirement = {
|
||||
name: requirementName ? `${id} - ${requirementName}` : id,
|
||||
description,
|
||||
status: finalStatus,
|
||||
check_ids: checks,
|
||||
invalid_config: requirementData.attributes.invalid_config || false,
|
||||
...getStatusCounters(finalStatus),
|
||||
theme: attrs.Theme,
|
||||
assessment_status: attrs.AssessmentStatus,
|
||||
cloud_applicability: attrs.CloudApplicability,
|
||||
remediation_procedure: attrs.RemediationProcedure,
|
||||
references: attrs.References,
|
||||
};
|
||||
|
||||
control.requirements.push(requirement);
|
||||
}
|
||||
|
||||
// Sort categories by canonical theme order so the framework always reads from
|
||||
// "Firewalls" down to "Malware Protection", regardless of map insertion order
|
||||
// driven by the API response.
|
||||
for (const framework of frameworks) {
|
||||
framework.categories.sort((a, b) => {
|
||||
const ia = CYBER_ESSENTIALS_THEME_ORDER.indexOf(a.name);
|
||||
const ib = CYBER_ESSENTIALS_THEME_ORDER.indexOf(b.name);
|
||||
// Unknown themes (defensive — shouldn't happen) sink to the bottom.
|
||||
const orderA = ia === -1 ? CYBER_ESSENTIALS_THEME_ORDER.length : ia;
|
||||
const orderB = ib === -1 ? CYBER_ESSENTIALS_THEME_ORDER.length : ib;
|
||||
return orderA - orderB;
|
||||
});
|
||||
}
|
||||
|
||||
calculateFrameworkCounters(frameworks);
|
||||
|
||||
return frameworks;
|
||||
};
|
||||
|
||||
export const toAccordionItems = (
|
||||
data: Framework[],
|
||||
scanId: string | undefined,
|
||||
): AccordionItemProps[] => {
|
||||
const safeId = scanId || "";
|
||||
|
||||
return data.flatMap((framework) =>
|
||||
framework.categories.map((category) => ({
|
||||
key: `${framework.name}-${category.name}`,
|
||||
title: (
|
||||
<ComplianceAccordionTitle
|
||||
label={category.name}
|
||||
pass={category.pass}
|
||||
fail={category.fail}
|
||||
manual={category.manual}
|
||||
isParentLevel={true}
|
||||
/>
|
||||
),
|
||||
content: "",
|
||||
// Theme → requirements (flat, no intermediate "control" level).
|
||||
items: category.controls.flatMap((control) =>
|
||||
control.requirements.map((requirement, reqIndex) => ({
|
||||
key: `${framework.name}-${category.name}-req-${reqIndex}`,
|
||||
title: (
|
||||
<ComplianceAccordionRequirementTitle
|
||||
type=""
|
||||
name={requirement.name}
|
||||
status={requirement.status as FindingStatus}
|
||||
invalidConfig={requirement.invalid_config}
|
||||
/>
|
||||
),
|
||||
content: (
|
||||
<ClientAccordionContent
|
||||
key={`content-${framework.name}-${category.name}-req-${reqIndex}`}
|
||||
requirement={requirement}
|
||||
scanId={safeId}
|
||||
framework={framework.name}
|
||||
disableFindings={
|
||||
requirement.check_ids.length === 0 && requirement.manual === 0
|
||||
}
|
||||
/>
|
||||
),
|
||||
items: [],
|
||||
})),
|
||||
),
|
||||
})),
|
||||
);
|
||||
};
|
||||
@@ -427,6 +427,36 @@ export interface CISControlsRequirement extends Requirement {
|
||||
implementation_groups?: string[];
|
||||
}
|
||||
|
||||
// Universal framework — flat attributes dict with Theme/AssessmentStatus/
|
||||
// CloudApplicability/RemediationProcedure/References. `Theme` is the canonical
|
||||
// grouping key for tables and PDF; the enum mirrors the five Cyber Essentials
|
||||
// control themes declared in `prowler/compliance/cyber_essentials_3.3.json`.
|
||||
export const CYBER_ESSENTIALS_THEME = {
|
||||
FIREWALLS: "Firewalls",
|
||||
SECURE_CONFIGURATION: "Secure Configuration",
|
||||
SECURITY_UPDATE_MANAGEMENT: "Security Update Management",
|
||||
USER_ACCESS_CONTROL: "User Access Control",
|
||||
MALWARE_PROTECTION: "Malware Protection",
|
||||
} as const;
|
||||
export type CyberEssentialsTheme =
|
||||
(typeof CYBER_ESSENTIALS_THEME)[keyof typeof CYBER_ESSENTIALS_THEME];
|
||||
|
||||
export interface CyberEssentialsAttributesMetadata {
|
||||
Theme: CyberEssentialsTheme;
|
||||
AssessmentStatus: string; // "Automated" or "Manual"
|
||||
CloudApplicability: string; // "full", "partial" or "non-applicable"
|
||||
RemediationProcedure: string;
|
||||
References: string;
|
||||
}
|
||||
|
||||
export interface CyberEssentialsRequirement extends Requirement {
|
||||
theme: CyberEssentialsAttributesMetadata["Theme"];
|
||||
assessment_status: CyberEssentialsAttributesMetadata["AssessmentStatus"];
|
||||
cloud_applicability: CyberEssentialsAttributesMetadata["CloudApplicability"];
|
||||
remediation_procedure: CyberEssentialsAttributesMetadata["RemediationProcedure"];
|
||||
references: CyberEssentialsAttributesMetadata["References"];
|
||||
}
|
||||
|
||||
// CMMC 2.0 (Cybersecurity Maturity Model Certification, 32 CFR Part 170).
|
||||
// Universal framework — flat attributes dict with Domain/Level/SourceRequirement.
|
||||
// `Domain` is the grouping key; `Level` (1/2/3) and `SourceRequirement` are
|
||||
@@ -469,6 +499,7 @@ export interface AttributesItemData {
|
||||
| OktaIDaaSStigAttributesMetadata[]
|
||||
| DORAAttributesMetadata[]
|
||||
| CISControlsAttributesMetadata[]
|
||||
| CyberEssentialsAttributesMetadata[]
|
||||
| CMMCAttributesMetadata[]
|
||||
| GenericAttributesMetadata[];
|
||||
check_ids: string[];
|
||||
|
||||
Reference in New Issue
Block a user