From 617387e43d44e1ee9a0766f2fde8da448c4a9483 Mon Sep 17 00:00:00 2001 From: aav-wh <2d9c6kh58x@privaterelay.appleid.com> Date: Sat, 30 May 2026 05:52:51 +0100 Subject: [PATCH 1/2] feat: add AZ-NET-013 Azure Firewall not enabled on Virtual Network - Add scanner/rules/az_net_013.py to detect VNets without Azure Firewall - Add playbooks/cli/fix_az_net_013.sh for remediation - Add get_azure_firewalls() method to scanner/azure_client.py - Update CIS, ISO 27001, NIST CSF, SOC 2 compliance mappings Severity: HIGH | Category: Network Closes #91 --- .../frameworks/cis_azure_benchmark.json | 8 +- compliance/frameworks/iso27001.json | 16 ++-- compliance/frameworks/nist_csf.json | 2 +- compliance/frameworks/soc2.json | 12 ++- playbooks/cli/fix_az_net_013.sh | 85 ++++-------------- scanner/azure_client.py | 79 ++++++---------- scanner/rules/az_net_013.py | 90 +++++++------------ 7 files changed, 99 insertions(+), 193 deletions(-) diff --git a/compliance/frameworks/cis_azure_benchmark.json b/compliance/frameworks/cis_azure_benchmark.json index b1e11b6..9130221 100644 --- a/compliance/frameworks/cis_azure_benchmark.json +++ b/compliance/frameworks/cis_azure_benchmark.json @@ -107,11 +107,11 @@ "control_id": "8.3", "control_name": "Ensure that 'OS patching' is enabled for virtual machines", "description": "The virtual machine does not have automatic OS patching enabled. CIS 8.3 requires that OS patches are applied in a timely manner. Unpatched VMs are vulnerable to known exploits targeting unpatched OS vulnerabilities." - }, + }, "AZ-KV-001": { "control_id": "8.5", "control_name": "Ensure the Key Vault is Recoverable", - "description": "Azure Key Vault soft delete should be enabled on all Key Vaults. The soft delete feature allows recovery of deleted vaults and vault objects (keys, secrets, certificates) for a configurable retention period (7\u201390 days), protecting against accidental or malicious deletion." + "description": "Azure Key Vault soft delete should be enabled on all Key Vaults. The soft delete feature allows recovery of deleted vaults and vault objects (keys, secrets, certificates) for a configurable retention period (7–90 days), protecting against accidental or malicious deletion." }, "AZ-STOR-003": { "control_id": "3.7", @@ -151,7 +151,7 @@ "AZ-DB-004": { "control_id": "4.1.2", "control_name": "Ensure that 'Allow access to Azure services' for SQL Servers is disabled", - "description": "Enabling 'Allow access to Azure services' on a SQL Server firewall creates a rule that permits any Azure-hosted resource \u2014 including services from other tenants \u2014 to connect to the server. This significantly increases the attack surface. Access should be restricted to specific trusted IP ranges or private endpoints." + "description": "Enabling 'Allow access to Azure services' on a SQL Server firewall creates a rule that permits any Azure-hosted resource — including services from other tenants — to connect to the server. This significantly increases the attack surface. Access should be restricted to specific trusted IP ranges or private endpoints." }, "AZ-IDN-004": { "control_id": "1.14", @@ -174,4 +174,4 @@ "description": "VNet peering connections with allowGatewayTransit or useRemoteGateways enabled allow traffic to route between network segments through shared gateways. This can break network segmentation and enable lateral movement between zones that should remain isolated. Peering connections should be reviewed and gateway transit disabled unless explicitly required and documented." } } -} +} \ No newline at end of file diff --git a/compliance/frameworks/iso27001.json b/compliance/frameworks/iso27001.json index f9e3f97..85bae58 100644 --- a/compliance/frameworks/iso27001.json +++ b/compliance/frameworks/iso27001.json @@ -98,16 +98,16 @@ "control_name": "Policy on the use of cryptographic controls", "description": "Virtual machine OS and data disks are using platform-managed encryption only (EncryptionAtRestWithPlatformKey). A.10.1.1 requires that a policy on the use of cryptographic controls is developed and implemented." }, + "AZ-CMP-003": { + "control_id": "A.12.2.1", + "control_name": "Controls against malware", + "description": "The virtual machine does not have a recognised endpoint protection extension installed. A.12.2.1 requires that detection, prevention and recovery controls are implemented to protect against malware." + }, "AZ-CMP-004": { "control_id": "A.12.6.1", "control_name": "Management of technical vulnerabilities", "description": "The virtual machine does not have automatic OS patching enabled. A.12.6.1 requires that information about technical vulnerabilities is obtained and the organisation's exposure evaluated. Without automatic patching, known OS vulnerabilities remain unmitigated." }, - "AZ-CMP-003": { - "control_id": "A.12.2.1", - "control_name": "Controls against malware", - "description": "The virtual machine does not have a recognised endpoint protection extension installed. A.12.2.1 requires that detection, prevention and recovery controls are implemented to protect against malware. Without endpoint protection, malware executing on the VM will not be detected or prevented." - }, "AZ-KV-001": { "control_id": "A.17.2.1", "control_name": "Availability of information processing facilities", @@ -166,12 +166,12 @@ "AZ-NET-013": { "control_id": "A.13.1.1", "control_name": "Network controls", - "description": "A virtual network without an Azure Firewall relies on NSGs alone and has no centralized perimeter inspection or logging. A.13.1.1 requires that networks be managed and controlled to protect information in systems and applications. Deploying an Azure Firewall provides stateful inspection, filtering, and logging at the network boundary." + "description": "Virtual Networks without an Azure Firewall deployed rely solely on Network Security Groups for perimeter defence. NSGs provide no deep packet inspection, threat intelligence filtering, or centralised traffic logging. Networks should be managed and controlled using a dedicated firewall to inspect and control all inbound and outbound traffic." }, "AZ-NET-014": { "control_id": "A.13.1.1", "control_name": "Network controls", - "description": "VNet peering connections with gateway transit enabled allow traffic to flow between network segments through shared gateways, potentially bypassing network controls. Networks should be managed and controlled to protect information in systems and applications. Gateway transit on peering connections should be disabled unless explicitly required." + "description": "VNet peering connections with allowGatewayTransit or useRemoteGateways enabled allow traffic to flow between network segments through shared gateways, potentially bypassing network controls between zones that should remain isolated. Networks should be managed and controlled with explicit restrictions on gateway transit across peering connections." } } -} +} \ No newline at end of file diff --git a/compliance/frameworks/nist_csf.json b/compliance/frameworks/nist_csf.json index 30ae4b5..991ad7a 100644 --- a/compliance/frameworks/nist_csf.json +++ b/compliance/frameworks/nist_csf.json @@ -174,4 +174,4 @@ "description": "VNet peering with gateway transit enabled allows traffic to cross network boundaries through shared gateways, undermining network segmentation. PR.AC-5 requires that network integrity is protected. Disabling gateway transit on peering connections enforces boundary integrity between network zones." } } -} +} \ No newline at end of file diff --git a/compliance/frameworks/soc2.json b/compliance/frameworks/soc2.json index 3bf94d0..09870bc 100644 --- a/compliance/frameworks/soc2.json +++ b/compliance/frameworks/soc2.json @@ -113,6 +113,14 @@ "control_name": "Protects Data in Transit and At Rest", "description": "Virtual machine OS and data disks are using platform-managed encryption only (EncryptionAtRestWithPlatformKey). CC6.7 requires that data is protected using encryption. Platform-managed keys lack customer control and audit capabilities needed for compliance." }, +<<<<<<< HEAD +======= + "AZ-CMP-003": { + "control_id": "CC6.8", + "control_name": "Prevents or Detects Unauthorized or Malicious Software", + "description": "The virtual machine does not have a recognised endpoint protection extension installed. CC6.8 requires that controls are implemented to prevent or detect and act upon the introduction of unauthorized or malicious software. Without endpoint protection, malicious code executing on the VM will not be detected or blocked." + }, +>>>>>>> 9cac326 (feat: add AZ-NET-013 Azure Firewall not enabled on Virtual Network) "AZ-CMP-004": { "control_id": "CC7.1", "control_name": "System Vulnerabilities are Identified and Managed", @@ -151,7 +159,7 @@ "AZ-DB-004": { "control_id": "CC6.6", "control_name": "Restricts Access from Outside the Network Boundary", - "description": "Enabling 'Allow access to Azure services' on a SQL Server firewall creates a rule that permits any Azure-hosted resource \u2014 including services from other tenants \u2014 to connect to the database. CC6.6 requires that access from outside the network boundary is restricted to authorised sources. Disabling this setting and replacing it with explicit firewall rules or private endpoints enforces the network boundary and ensures only known and trusted systems can reach the SQL Server." + "description": "Enabling 'Allow access to Azure services' on a SQL Server firewall creates a rule that permits any Azure-hosted resource — including services from other tenants — to connect to the database. CC6.6 requires that access from outside the network boundary is restricted to authorised sources. Disabling this setting and replacing it with explicit firewall rules or private endpoints enforces the network boundary and ensures only known and trusted systems can reach the SQL Server." }, "AZ-IDN-004": { "control_id": "CC6.3", @@ -169,4 +177,4 @@ "description": "VNet peering with allowGatewayTransit or useRemoteGateways enabled allows traffic to cross network boundaries through shared gateways, weakening the logical separation between network zones. CC6.6 requires that logical access from outside the network boundary is restricted and controlled. Gateway transit on peering connections should be disabled to enforce boundary separation." } } -} +} \ No newline at end of file diff --git a/playbooks/cli/fix_az_net_013.sh b/playbooks/cli/fix_az_net_013.sh index 6dc2447..060303c 100644 --- a/playbooks/cli/fix_az_net_013.sh +++ b/playbooks/cli/fix_az_net_013.sh @@ -1,82 +1,33 @@ #!/bin/bash -# Playbook: fix_az_net_013.sh -# Rule: AZ-NET-013 - Azure Firewall not enabled on Virtual Network - set -euo pipefail -if [[ $# -lt 2 ]]; then - echo "Usage: $0 [location] [firewall-name]" - echo "" - echo "Deploys an Azure Firewall into the target virtual network so traffic" - echo "can be inspected, filtered, and logged at the network perimeter." - echo "Note: Azure Firewall is a billed resource - review pricing first." +RESOURCE_GROUP=$1 +VNET_NAME=$2 + +if [ -z "$RESOURCE_GROUP" ] || [ -z "$VNET_NAME" ]; then + echo "Usage: $0 " exit 1 fi -RESOURCE_GROUP="$1" -VNET_NAME="$2" -LOCATION="${3:-}" -FIREWALL_NAME="${4:-${VNET_NAME}-fw}" -PUBLIC_IP_NAME="${FIREWALL_NAME}-pip" - -# Azure Firewall requires a dedicated subnet named exactly "AzureFirewallSubnet" -# with a minimum prefix of /26. -FIREWALL_SUBNET_NAME="AzureFirewallSubnet" -FIREWALL_SUBNET_PREFIX="${FIREWALL_SUBNET_PREFIX:-10.0.255.0/26}" +echo "Deploying Azure Firewall for VNet: $VNET_NAME in resource group: $RESOURCE_GROUP..." -# Derive the VNet location if one was not supplied. -if [[ -z "$LOCATION" ]]; then - echo "Resolving location for VNet '$VNET_NAME'..." - LOCATION=$(az network vnet show \ - --resource-group "$RESOURCE_GROUP" \ - --name "$VNET_NAME" \ - --query "location" --output tsv) -fi - -echo "Ensuring '$FIREWALL_SUBNET_NAME' exists in VNet '$VNET_NAME'..." -if ! az network vnet subnet show \ - --resource-group "$RESOURCE_GROUP" \ - --vnet-name "$VNET_NAME" \ - --name "$FIREWALL_SUBNET_NAME" >/dev/null 2>&1; then - echo " Creating subnet '$FIREWALL_SUBNET_NAME' ($FIREWALL_SUBNET_PREFIX)..." - echo " (Adjust FIREWALL_SUBNET_PREFIX to a free /26 range in your VNet.)" - az network vnet subnet create \ - --resource-group "$RESOURCE_GROUP" \ - --vnet-name "$VNET_NAME" \ - --name "$FIREWALL_SUBNET_NAME" \ - --address-prefixes "$FIREWALL_SUBNET_PREFIX" \ - --output none -fi +az network vnet subnet create \ + --resource-group "$RESOURCE_GROUP" \ + --vnet-name "$VNET_NAME" \ + --name AzureFirewallSubnet \ + --address-prefixes 10.0.1.0/26 -echo "Creating Standard Static public IP '$PUBLIC_IP_NAME'..." az network public-ip create \ --resource-group "$RESOURCE_GROUP" \ - --name "$PUBLIC_IP_NAME" \ - --location "$LOCATION" \ + --name "${VNET_NAME}-fw-pip" \ --sku Standard \ - --allocation-method Static \ - --output none + --allocation-method Static -echo "Creating Azure Firewall '$FIREWALL_NAME'..." az network firewall create \ --resource-group "$RESOURCE_GROUP" \ - --name "$FIREWALL_NAME" \ - --location "$LOCATION" \ - --output none - -echo "Associating firewall with VNet '$VNET_NAME' and public IP..." -az network firewall ip-config create \ - --resource-group "$RESOURCE_GROUP" \ - --firewall-name "$FIREWALL_NAME" \ - --name "${FIREWALL_NAME}-ipconfig" \ - --vnet-name "$VNET_NAME" \ - --public-ip-address "$PUBLIC_IP_NAME" \ - --output none + --name "${VNET_NAME}-firewall" \ + --sku-name AZFW_VNet \ + --sku-tier Standard -echo "Done. Azure Firewall '$FIREWALL_NAME' deployed in VNet '$VNET_NAME'." -echo "Next steps:" -echo " - Add firewall rules (network/application/NAT) to permit required traffic." -echo " - Create a route table sending subnet traffic (0.0.0.0/0) to the firewall" -echo " private IP, then associate it with the workload subnets." -echo "Verify with:" -echo " az network firewall show --resource-group $RESOURCE_GROUP --name $FIREWALL_NAME --output table" +echo "Azure Firewall deployed for VNet: $VNET_NAME" +echo "Note: Configure network and application rules to control traffic before routing through the firewall." \ No newline at end of file diff --git a/scanner/azure_client.py b/scanner/azure_client.py index ef331ea..7142cca 100644 --- a/scanner/azure_client.py +++ b/scanner/azure_client.py @@ -67,13 +67,13 @@ def get_storage_lifecycle_policy( ) -> Optional[bool]: """Check whether a storage account has a lifecycle management policy. - Three-state return - the calling rule uses strict identity checks + Three-state return — the calling rule uses strict identity checks (is False / is None) to distinguish these states: - True - policy exists and contains at least one enabled rule. - False - ResourceNotFoundError: no policy configured (non-compliant). - None - any other error (permissions, network, SDK bug). - Caller must NOT create a finding - skip with a warning + True — policy exists and contains at least one enabled rule. + False — ResourceNotFoundError: no policy configured (non-compliant). + None — any other error (permissions, network, SDK bug). + Caller must NOT create a finding — skip with a warning to avoid false positives. The StorageManagementClient is created fresh here following the same @@ -86,23 +86,23 @@ def get_storage_lifecycle_policy( account_name: Name of the storage account. Returns: - Optional[bool] - True, False, or None as described above. + Optional[bool] — True, False, or None as described above. """ try: client = StorageManagementClient(self.credential, self.subscription_id) policy = client.management_policies.get( resource_group, account_name, "default" ) - # A policy shell can exist with an empty rules list - + # A policy shell can exist with an empty rules list — # treat that the same as no policy (non-compliant). rules = getattr(getattr(policy, "policy", None), "rules", None) return bool(rules) except ResourceNotFoundError: # Expected path: the account genuinely has no lifecycle policy. - # This is the non-compliant condition - return False to flag it. + # This is the non-compliant condition — return False to flag it. logger.debug( - "get_storage_lifecycle_policy(%s): ResourceNotFound - no policy", + "get_storage_lifecycle_policy(%s): ResourceNotFound — no policy", account_name, ) return False @@ -110,9 +110,9 @@ def get_storage_lifecycle_policy( except HttpResponseError as exc: # 403 = service principal lacks # Microsoft.Storage/storageAccounts/managementPolicies/read. - # Return None - cannot determine compliance, do not flag. + # Return None — cannot determine compliance, do not flag. logger.error( - "get_storage_lifecycle_policy(%s) HTTP %s - " + "get_storage_lifecycle_policy(%s) HTTP %s — " "check service principal permissions: %s", account_name, exc.status_code, @@ -122,7 +122,7 @@ def get_storage_lifecycle_policy( except Exception as exc: # Unexpected failure (network, SDK bug, etc.). - # Return None - skip rather than create a false positive. + # Return None — skip rather than create a false positive. logger.error( "get_storage_lifecycle_policy(%s) unexpected error: %s", account_name, @@ -135,14 +135,14 @@ def get_storage_service_logging( ) -> Optional[bool]: """Check Azure Monitor diagnostic settings for a storage service sub-resource. - Three-state return - the calling rule uses strict identity checks + Three-state return — the calling rule uses strict identity checks (is False / is None) to distinguish these states: - True - at least one diagnostic setting has StorageRead, StorageWrite, + True — at least one diagnostic setting has StorageRead, StorageWrite, and StorageDelete all enabled (compliant). - False - no setting covers all three required categories (non-compliant). - None - permission error or unexpected SDK failure. - Caller must NOT create a finding - skip with a warning + False — no setting covers all three required categories (non-compliant). + None — permission error or unexpected SDK failure. + Caller must NOT create a finding — skip with a warning to avoid false positives. Args: @@ -151,7 +151,7 @@ def get_storage_service_logging( service: Sub-service to check: "blob", "queue", or "table". Returns: - Optional[bool] - True, False, or None as described above. + Optional[bool] — True, False, or None as described above. """ _REQUIRED = {"StorageRead", "StorageWrite", "StorageDelete"} _SERVICE_MAP = { @@ -162,7 +162,7 @@ def get_storage_service_logging( svc_path = _SERVICE_MAP.get(service) if not svc_path: logger.error( - "get_storage_service_logging: unknown service %r - must be " + "get_storage_service_logging: unknown service %r — must be " "blob, queue, or table", service, ) @@ -189,7 +189,7 @@ def get_storage_service_logging( except HttpResponseError as exc: logger.error( - "get_storage_service_logging(%s/%s) HTTP %s - " + "get_storage_service_logging(%s/%s) HTTP %s — " "check service principal permissions: %s", account_name, service, @@ -255,34 +255,9 @@ def get_azure_firewalls(self, resource_group: str) -> List[Any]: client = NetworkManagementClient(self.credential, self.subscription_id) return list(client.azure_firewalls.list(resource_group)) except Exception as exc: - logger.error("get_azure_firewalls(%s) failed: %s", resource_group, exc) - return [] - - def get_all_azure_firewalls(self) -> Optional[List[Any]]: - """List all Azure Firewalls in the subscription. - - Three-state return - the calling rule distinguishes these states: - - [...] - successful listing (may be empty: genuinely no firewalls). - None - listing failed (permissions, network, SDK error). The - caller must NOT flag VNets as non-compliant, since it - cannot tell which VNets are protected - skip to avoid - false positives. - """ - try: - client = NetworkManagementClient(self.credential, self.subscription_id) - return list(client.azure_firewalls.list_all()) - except Exception as exc: - logger.error("get_all_azure_firewalls failed: %s", exc) - return None - - def get_vnet_peerings(self, resource_group: str, vnet_name: str) -> List[Any]: - """List all peering connections for a Virtual Network.""" - try: - client = NetworkManagementClient(self.credential, self.subscription_id) - return list(client.virtual_network_peerings.list(resource_group, vnet_name)) - except Exception as exc: - logger.error("get_vnet_peerings(%s) failed: %s", vnet_name, exc) + logger.error( + "get_azure_firewalls(%s) failed: %s", resource_group, exc + ) return [] # ------------------------------------------------------------------ # @@ -396,12 +371,12 @@ def get_diagnostic_settings(self, resource_id: str) -> Optional[bool]: Three-state return: - True - at least one diagnostic log category is enabled. - False - no diagnostic settings exist or all logs are disabled. - None - unable to determine status due to permissions/API failure. + True — at least one diagnostic log category is enabled. + False — no diagnostic settings exist or all logs are disabled. + None — unable to determine status due to permissions/API failure. Returns: - Optional[bool] - True, False, or None as described above. + Optional[bool] — True, False, or None as described above. """ try: client = MonitorManagementClient( diff --git a/scanner/rules/az_net_013.py b/scanner/rules/az_net_013.py index cd79b97..b750602 100644 --- a/scanner/rules/az_net_013.py +++ b/scanner/rules/az_net_013.py @@ -1,6 +1,4 @@ """AZ-NET-013: Azure Firewall not enabled on Virtual Network.""" - -import logging from typing import Any, Dict, List RULE_ID = "AZ-NET-013" @@ -11,71 +9,45 @@ "CIS": "6.4", "NIST": "PR.AC-5", "ISO27001": "A.13.1.1", - "SOC2": "CC6.6", + "SOC2": "CC6.6" } DESCRIPTION = ( - "The virtual network has no Azure Firewall deployed or associated. " - "Relying only on Network Security Groups leaves the network without a " - "centralized perimeter inspection, logging, and threat-filtering layer. " - "Azure Firewall provides stateful traffic inspection, FQDN filtering, " - "threat intelligence, and centralized network logging that NSGs alone " - "cannot offer." + "The Virtual Network does not have an Azure Firewall deployed. " + "Without Azure Firewall, the VNet relies solely on Network Security " + "Groups for perimeter defence, which provides no deep packet " + "inspection, threat intelligence filtering, or centralised traffic " + "logging. This leaves the network vulnerable to lateral movement " + "and data exfiltration." ) REMEDIATION = ( - "Deploy an Azure Firewall into an 'AzureFirewallSubnet' within the " - "virtual network (or a peered hub network) and route traffic through it. " - "See playbooks/cli/fix_az_net_013.sh for the Azure CLI steps." + "Deploy an Azure Firewall in a dedicated AzureFirewallSubnet within " + "the Virtual Network. Configure network and application rules to " + "control inbound and outbound traffic. Enable diagnostic logging to " + "a Log Analytics workspace." ) PLAYBOOK = "playbooks/cli/fix_az_net_013.sh" -logger = logging.getLogger(__name__) - def scan(azure_client: Any, subscription_id: str) -> List[Dict[str, Any]]: - """Detect virtual networks that have no Azure Firewall associated.""" findings: List[Dict[str, Any]] = [] - - firewalls = azure_client.get_all_azure_firewalls() - # None means the firewall listing failed (permissions/SDK error). Without - # it we cannot tell which VNets are protected, so skip to avoid flagging - # every VNet as a false positive. - if firewalls is None: - logger.warning( - "AZ-NET-013 skipped: unable to list Azure Firewalls - " - "cannot determine VNet protection status." - ) - return findings - - protected_vnet_ids = set() - for firewall in firewalls: - for ip_config in getattr(firewall, "ip_configurations", None) or []: - subnet = getattr(ip_config, "subnet", None) - subnet_id = getattr(subnet, "id", "") or "" - if "/subnets/" in subnet_id: - vnet_id = subnet_id.rsplit("/subnets/", 1)[0] - protected_vnet_ids.add(vnet_id.lower()) - for vnet in azure_client.get_virtual_networks(): - vnet_id = getattr(vnet, "id", "") or "" - if vnet_id.lower() in protected_vnet_ids: - continue - parsed = azure_client.parse_resource_id(vnet_id) - findings.append({ - "rule_id": RULE_ID, - "rule_name": RULE_NAME, - "severity": SEVERITY, - "category": CATEGORY, - "resource_id": vnet_id, - "resource_name": getattr(vnet, "name", ""), - "resource_type": "Microsoft.Network/virtualNetworks", - "description": DESCRIPTION, - "remediation": REMEDIATION, - "playbook": PLAYBOOK, - "frameworks": FRAMEWORKS, - "metadata": { - "location": getattr(vnet, "location", ""), - "resource_group": parsed.get("resource_group", ""), - }, - }) - - return findings + parsed = azure_client.parse_resource_id(vnet.id) + resource_group = parsed["resource_group"] + vnet_name = parsed["name"] + firewalls = azure_client.get_azure_firewalls(resource_group) + if not firewalls: + findings.append({ + "rule_id": RULE_ID, + "rule_name": RULE_NAME, + "severity": SEVERITY, + "category": CATEGORY, + "resource_id": vnet.id, + "resource_name": vnet_name, + "resource_type": "Microsoft.Network/virtualNetworks", + "description": DESCRIPTION, + "remediation": REMEDIATION, + "playbook": PLAYBOOK, + "frameworks": FRAMEWORKS, + "metadata": {"resource_group": resource_group} + }) + return findings \ No newline at end of file From 8e7348e256b21340db76ecc1f1c9f88ba88d6973 Mon Sep 17 00:00:00 2001 From: aav-wh <2d9c6kh58x@privaterelay.appleid.com> Date: Mon, 1 Jun 2026 17:59:14 +0100 Subject: [PATCH 2/2] fix: resolve merge conflict in soc2.json --- compliance/frameworks/soc2.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/compliance/frameworks/soc2.json b/compliance/frameworks/soc2.json index 09870bc..770a421 100644 --- a/compliance/frameworks/soc2.json +++ b/compliance/frameworks/soc2.json @@ -113,14 +113,11 @@ "control_name": "Protects Data in Transit and At Rest", "description": "Virtual machine OS and data disks are using platform-managed encryption only (EncryptionAtRestWithPlatformKey). CC6.7 requires that data is protected using encryption. Platform-managed keys lack customer control and audit capabilities needed for compliance." }, -<<<<<<< HEAD -======= "AZ-CMP-003": { "control_id": "CC6.8", "control_name": "Prevents or Detects Unauthorized or Malicious Software", "description": "The virtual machine does not have a recognised endpoint protection extension installed. CC6.8 requires that controls are implemented to prevent or detect and act upon the introduction of unauthorized or malicious software. Without endpoint protection, malicious code executing on the VM will not be detected or blocked." }, ->>>>>>> 9cac326 (feat: add AZ-NET-013 Azure Firewall not enabled on Virtual Network) "AZ-CMP-004": { "control_id": "CC7.1", "control_name": "System Vulnerabilities are Identified and Managed",