Repository: prowler-cloud/prowler
Pull request #7221: fix(vm): handle NoneType accessing security_profile
Author: login: andoniaf
Labels: name: provider/azure
description: Issues/PRs related with the Azure provider
name: was-backported
description: The PR was successfully backported to the target branch
name: backport-to-v5.4
description: Backport PR to the v5.4 branch
Created: 2025-03-13T10:10:23Z | Merged: 2025-03-25T08:33:00Z

### Description
Handle NoneType accessing security_profile attributes as `vm.security_profile` can be `None`.

https://learn.microsoft.com/en-us/python/api/azure-mgmt-computefleet/azure.mgmt.computefleet.models.basevirtualmachineprofile?view=azure-python#azure-mgmt-computefleet-models-basevirtualmachineprofile-security-profile

### Checklist

- Are there new checks included in this PR? Yes / No
    - If so, do we need to update permissions for the provider? Please review this carefully.
- [ ] Review if the code is being covered by tests.
- [ ] Review if code is being documented following this specification https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
- [ ] Review if backport is needed.
- [ ] Review if is needed to change the [Readme.md](https://github.com/prowler-cloud/prowler/blob/master/README.md)

#### API
- [ ] Verify if API specs need to be regenerated.
- [ ] Check if version updates are required (e.g., specs, Poetry, etc.).
- [ ] Ensure new entries are added to [CHANGELOG.md](https://github.com/prowler-cloud/prowler/blob/master/api/CHANGELOG.md), if applicable.

### License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Diff:
diff --git a/prowler/providers/azure/services/vm/vm_service.py b/prowler/providers/azure/services/vm/vm_service.py
index 4700f81ecad..2ef3c9e30a7 100644
--- a/prowler/providers/azure/services/vm/vm_service.py
+++ b/prowler/providers/azure/services/vm/vm_service.py
@@ -50,7 +50,7 @@ def _get_virtual_machines(self):
                                     else None
                                 ),
                                 location=vm.location,
-                                security_profile=vm.security_profile,
+                                security_profile=getattr(vm, "security_profile", None),
                                 extensions=[
                                     VirtualMachineExtension(id=extension.id)
                                     for extension in getattr(vm, "resources", [])
@@ -142,7 +142,7 @@ class VirtualMachine:
     resource_id: str
     resource_name: str
     location: str
-    security_profile: SecurityProfile
+    security_profile: Optional[SecurityProfile]
     extensions: list[VirtualMachineExtension]
     storage_profile: Optional[StorageProfile] = None
 
diff --git a/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.py b/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.py
index e4a4d9d0825..d4896b70ec4 100644
--- a/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.py
+++ b/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.py
@@ -14,7 +14,8 @@ def execute(self) -> Check_Report_Azure:
                 report.status_extended = f"VM {vm.resource_name} has trusted launch disabled in subscription {subscription_name}"
 
                 if (
-                    vm.security_profile.security_type == "TrustedLaunch"
+                    vm.security_profile
+                    and vm.security_profile.security_type == "TrustedLaunch"
                     and vm.security_profile.uefi_settings.secure_boot_enabled
                     and vm.security_profile.uefi_settings.v_tpm_enabled
                 ):
diff --git a/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py b/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py
index 8f81756561c..05e96a5c1dd 100644
--- a/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py
+++ b/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py
@@ -158,3 +158,52 @@ def test_vm_trusted_launch_disabled(self):
                 result[0].status_extended
                 == f"VM VMTest has trusted launch disabled in subscription {AZURE_SUBSCRIPTION_ID}"
             )
+
+    def test_vm_no_security_profile(self):
+        vm_id = str(uuid4())
+        vm_client = mock.MagicMock
+        with (
+            mock.patch(
+                "prowler.providers.common.provider.Provider.get_global_provider",
+                return_value=set_mocked_azure_provider(),
+            ),
+            mock.patch(
+                "prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client",
+                new=vm_client,
+            ),
+        ):
+            from prowler.providers.azure.services.vm.vm_service import VirtualMachine
+            from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import (
+                vm_trusted_launch_enabled,
+            )
+
+            vm_client.virtual_machines = {
+                AZURE_SUBSCRIPTION_ID: {
+                    vm_id: VirtualMachine(
+                        resource_id=vm_id,
+                        resource_name="VMTest",
+                        location="location",
+                        security_profile=None,
+                        extensions=[],
+                        storage_profile=mock.MagicMock(
+                            os_disk=mock.MagicMock(
+                                create_option="FromImage",
+                                managed_disk=mock.MagicMock(id="managed_disk_id"),
+                            ),
+                            data_disks=[],
+                        ),
+                    )
+                }
+            }
+
+            check = vm_trusted_launch_enabled()
+            result = check.execute()
+            assert len(result) == 1
+            assert result[0].status == "FAIL"
+            assert result[0].subscription == AZURE_SUBSCRIPTION_ID
+            assert result[0].resource_name == "VMTest"
+            assert result[0].resource_id == vm_id
+            assert (
+                result[0].status_extended
+                == f"VM VMTest has trusted launch disabled in subscription {AZURE_SUBSCRIPTION_ID}"
+            )


Reviews:
user: login: MrCloudSec
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T11:44:58Z
user: login: andoniaf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-24T14:23:26Z
user: login: MrCloudSec
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T08:32:48Z

Review comments:
diff_hunk: @@ -50,7 +50,7 @@ def _get_virtual_machines(self):
                                     else None
                                 ),
                                 location=vm.location,
-                                security_profile=vm.security_profile,
+                                security_profile=vm.get("security_profile", None),
path: prowler/providers/azure/services/vm/vm_service.py
user: login: MrCloudSec
body: Is not `.get` only for dictionaries? I think you should use `getattr`.
created_at: 2025-03-24T11:44:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,7 +50,7 @@ def _get_virtual_machines(self):
                                     else None
                                 ),
                                 location=vm.location,
-                                security_profile=vm.security_profile,
+                                security_profile=vm.get("security_profile", None),
path: prowler/providers/azure/services/vm/vm_service.py
user: login: andoniaf
body: You're right, fixed.
created_at: 2025-03-24T14:23:26Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-03-13T10:53:13Z
body: ## [Codecov](https://app.codecov.io/gh/prowler-cloud/prowler/pull/7221?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 88.65%. Comparing base [(`db79db4`)](https://app.codecov.io/gh/prowler-cloud/prowler/commit/db79db47863b7bcbe60580a9d88a9cd8a79a7381?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud) to head [(`743dfec`)](https://app.codecov.io/gh/prowler-cloud/prowler/commit/743dfecdd00878f4275d0376982c1e346c1bec04?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud).
> Report is 529 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #7221      +/-   ##
==========================================
- Coverage   88.66%   88.65%   -0.02%     
==========================================
  Files        1219     1219              
  Lines       35549    35616      +67     
==========================================
+ Hits        31520    31574      +54     
- Misses       4029     4042      +13     
```

| [Flag](https://app.codecov.io/gh/prowler-cloud/prowler/pull/7221/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud) | Coverage Δ | |
|---|---|---|
| [prowler](https://app.codecov.io/gh/prowler-cloud/prowler/pull/7221/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud) | `88.65% <100.00%> (-0.02%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Components](https://app.codecov.io/gh/prowler-cloud/prowler/pull/7221/components?src=pr&el=components&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud) | Coverage Δ | |
|---|---|---|
| [prowler](https://app.codecov.io/gh/prowler-cloud/prowler/pull/7221/components?src=pr&el=component&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud) | `88.65% <100.00%> (-0.02%)` | :arrow_down: |
| [api](https://app.codecov.io/gh/prowler-cloud/prowler/pull/7221/components?src=pr&el=component&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=prowler-cloud) | `∅ <ø> (∅)` | |

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: prowler-bot
created_at: 2025-03-25T08:34:00Z
body: ## 💚 All backports created successfully

| Status | Branch | Result |
|:------:|:------:|:------|
|✅|v5.4|[<img src="https://img.shields.io/github/pulls/detail/state/prowler-cloud/prowler/7373">](https://github.com/prowler-cloud/prowler/pull/7373)|

### Questions ?
Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) and see the [Github Action logs](https://github.com/prowler-cloud/prowler/actions/runs/14055026368) for details

<!--- Backport version: 9.5.1 -->
author_association: COLLABORATOR