Skip to content

Commit 1eda941

Browse files
authored
fix(sdk): use case-insensitive comparison for Azure VM backup checks (#10395)
1 parent ad6368a commit 1eda941

File tree

5 files changed

+185
-2
lines changed

5 files changed

+185
-2
lines changed

prowler/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to the **Prowler SDK** are documented in this file.
44

5+
## [5.21.2] (Prowler UNRELEASED)
6+
7+
### 🐞 Fixed
8+
9+
- Azure `vm_backup_enabled` and `vm_sufficient_daily_backup_retention_period` checks now compare VM names case-insensitively to avoid false negatives when Azure stores backup item names in a different case [(#10373)](https://github.com/prowler-cloud/prowler/pull/10373)
10+
11+
---
12+
513
## [5.21.0] (Prowler v5.21.0)
614

715
### 🚀 Added

prowler/providers/azure/services/vm/vm_backup_enabled/vm_backup_enabled.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def execute(self) -> list[Check_Report_Azure]:
3131
for backup_item in vault.backup_protected_items.values():
3232
if (
3333
backup_item.workload_type == DataSourceType.VM
34-
and backup_item.name.split(";")[-1] == vm.resource_name
34+
and backup_item.name.split(";")[-1].lower()
35+
== vm.resource_name.lower()
3536
):
3637
found = True
3738
found_vault_name = vault.name

prowler/providers/azure/services/vm/vm_sufficient_daily_backup_retention_period/vm_sufficient_daily_backup_retention_period.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def execute(self) -> list[Check_Report_Azure]:
2727
for backup_item in vault.backup_protected_items.values():
2828
if (
2929
backup_item.workload_type == DataSourceType.VM
30-
and backup_item.name.split(";")[-1] == vm.resource_name
30+
and backup_item.name.split(";")[-1].lower()
31+
== vm.resource_name.lower()
3132
):
3233
backup_found = True
3334
policy_id = backup_item.backup_policy_id

tests/providers/azure/services/vm/vm_backup_enabled/vm_backup_enabled_test.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,85 @@ def test_vm_not_protected_by_backup(self):
221221
== f"VM {vm_name} in subscription {AZURE_SUBSCRIPTION_ID} is not protected by Azure Backup."
222222
)
223223

224+
def test_vm_protected_by_backup_case_insensitive(self):
225+
vm_id = str(uuid4())
226+
vm_name = "vmtest"
227+
vault_id = str(uuid4())
228+
vault_name = "vault1"
229+
mock_vm_client = mock.MagicMock()
230+
mock_recovery_client = mock.MagicMock()
231+
with (
232+
mock.patch(
233+
"prowler.providers.common.provider.Provider.get_global_provider",
234+
return_value=set_mocked_azure_provider(),
235+
),
236+
mock.patch(
237+
"prowler.providers.azure.services.vm.vm_backup_enabled.vm_backup_enabled.vm_client",
238+
new=mock_vm_client,
239+
),
240+
mock.patch(
241+
"prowler.providers.azure.services.vm.vm_backup_enabled.vm_backup_enabled.recovery_client",
242+
new=mock_recovery_client,
243+
),
244+
):
245+
from azure.mgmt.recoveryservicesbackup.activestamp.models import (
246+
DataSourceType,
247+
)
248+
249+
from prowler.providers.azure.services.recovery.recovery_service import (
250+
BackupItem,
251+
BackupVault,
252+
)
253+
from prowler.providers.azure.services.vm.vm_backup_enabled.vm_backup_enabled import (
254+
vm_backup_enabled,
255+
)
256+
from prowler.providers.azure.services.vm.vm_service import (
257+
ManagedDiskParameters,
258+
OSDisk,
259+
StorageProfile,
260+
VirtualMachine,
261+
)
262+
263+
vm = VirtualMachine(
264+
resource_id=vm_id,
265+
resource_name=vm_name,
266+
location="eastus",
267+
security_profile=None,
268+
extensions=[],
269+
storage_profile=StorageProfile(
270+
os_disk=OSDisk(
271+
name="os_disk_name",
272+
operating_system_type="Linux",
273+
managed_disk=ManagedDiskParameters(id="managed_disk_id"),
274+
),
275+
data_disks=[],
276+
),
277+
)
278+
backup_item = BackupItem(
279+
id=str(uuid4()),
280+
name="someprefix;VMTEST",
281+
workload_type=DataSourceType.VM,
282+
)
283+
vault = BackupVault(
284+
id=vault_id,
285+
name=vault_name,
286+
location="eastus",
287+
backup_protected_items={backup_item.id: backup_item},
288+
)
289+
mock_vm_client.virtual_machines = {AZURE_SUBSCRIPTION_ID: {vm_id: vm}}
290+
mock_recovery_client.vaults = {AZURE_SUBSCRIPTION_ID: {vault_id: vault}}
291+
check = vm_backup_enabled()
292+
result = check.execute()
293+
assert len(result) == 1
294+
assert result[0].status == "PASS"
295+
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
296+
assert result[0].resource_name == vm_name
297+
assert result[0].resource_id == vm_id
298+
assert (
299+
result[0].status_extended
300+
== f"VM {vm_name} in subscription {AZURE_SUBSCRIPTION_ID} is protected by Azure Backup (vault: {vault_name})."
301+
)
302+
224303
def test_vm_protected_by_backup_non_vm_workload(self):
225304
vm_id = str(uuid4())
226305
vm_name = "VMTest"

tests/providers/azure/services/vm/vm_sufficient_daily_backup_retention_period/vm_sufficient_daily_backup_retention_period_test.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,100 @@ def test_vm_with_sufficient_retention(self):
156156
in result[0].status_extended
157157
)
158158

159+
def test_vm_with_sufficient_retention_case_insensitive(self):
160+
from azure.mgmt.recoveryservicesbackup.activestamp.models import DataSourceType
161+
162+
from prowler.providers.azure.services.recovery.recovery_service import (
163+
BackupItem,
164+
BackupPolicy,
165+
BackupVault,
166+
)
167+
from prowler.providers.azure.services.vm.vm_service import (
168+
ManagedDiskParameters,
169+
OSDisk,
170+
StorageProfile,
171+
VirtualMachine,
172+
)
173+
174+
vm_id = str(uuid4())
175+
vm_name = "vmtest"
176+
vault_id = str(uuid4())
177+
policy_id = str(uuid4())
178+
retention_days = 14
179+
min_retention_days = 7
180+
181+
vm = VirtualMachine(
182+
resource_id=vm_id,
183+
resource_name=vm_name,
184+
location="eastus",
185+
security_profile=None,
186+
extensions=[],
187+
storage_profile=StorageProfile(
188+
os_disk=OSDisk(
189+
name="os_disk_name",
190+
operating_system_type="Linux",
191+
managed_disk=ManagedDiskParameters(id="managed_disk_id"),
192+
),
193+
data_disks=[],
194+
),
195+
)
196+
backup_item = BackupItem(
197+
id=str(uuid4()),
198+
name="someprefix;VMTEST",
199+
workload_type=DataSourceType.VM,
200+
backup_policy_id=policy_id,
201+
)
202+
backup_policy = BackupPolicy(
203+
id=policy_id,
204+
name="policy1",
205+
retention_days=retention_days,
206+
)
207+
vault = BackupVault(
208+
id=vault_id,
209+
name="vault1",
210+
location="eastus",
211+
backup_protected_items={backup_item.id: backup_item},
212+
backup_policies={policy_id: backup_policy},
213+
)
214+
vm_client = mock.MagicMock()
215+
recovery_client = mock.MagicMock()
216+
vm_client.virtual_machines = {AZURE_SUBSCRIPTION_ID: {vm_id: vm}}
217+
recovery_client.vaults = {AZURE_SUBSCRIPTION_ID: {vault_id: vault}}
218+
vm_client.audit_config = {
219+
"vm_backup_min_daily_retention_days": min_retention_days
220+
}
221+
with (
222+
mock.patch(
223+
"prowler.providers.common.provider.Provider.get_global_provider",
224+
return_value=set_mocked_azure_provider(
225+
audit_config=vm_client.audit_config
226+
),
227+
),
228+
mock.patch(
229+
"prowler.providers.azure.services.vm.vm_sufficient_daily_backup_retention_period.vm_sufficient_daily_backup_retention_period.vm_client",
230+
new=vm_client,
231+
),
232+
mock.patch(
233+
"prowler.providers.azure.services.vm.vm_sufficient_daily_backup_retention_period.vm_sufficient_daily_backup_retention_period.recovery_client",
234+
new=recovery_client,
235+
),
236+
):
237+
from prowler.providers.azure.services.vm.vm_sufficient_daily_backup_retention_period.vm_sufficient_daily_backup_retention_period import (
238+
vm_sufficient_daily_backup_retention_period,
239+
)
240+
241+
check = vm_sufficient_daily_backup_retention_period()
242+
result = check.execute()
243+
assert len(result) == 1
244+
assert result[0].status == "PASS"
245+
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
246+
assert result[0].resource_name == vm_name
247+
assert result[0].resource_id == vm_id
248+
assert (
249+
f"has a daily backup retention period of {retention_days} days"
250+
in result[0].status_extended
251+
)
252+
159253
def test_vm_with_insufficient_retention(self):
160254
from azure.mgmt.recoveryservicesbackup.activestamp.models import DataSourceType
161255

0 commit comments

Comments
 (0)