feat(openstack): add image service with 6 checks (#10096)

This commit is contained in:
Daniel Barranquero
2026-03-02 12:47:49 +01:00
committed by GitHub
parent 8eddb48b16
commit b21ded6d46
29 changed files with 2845 additions and 3 deletions

View File

@@ -0,0 +1,231 @@
"""Tests for image_hw_mem_encryption_enabled check."""
from unittest import mock
from prowler.providers.openstack.services.image.image_service import ImageResource
from tests.providers.openstack.openstack_fixtures import (
OPENSTACK_PROJECT_ID,
OPENSTACK_REGION,
set_mocked_openstack_provider,
)
class Test_image_hw_mem_encryption_enabled:
def test_no_images(self):
"""Test when no images exist."""
image_client = mock.MagicMock()
image_client.images = []
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled import (
image_hw_mem_encryption_enabled,
)
check = image_hw_mem_encryption_enabled()
result = check.execute()
assert len(result) == 0
def test_image_hw_mem_encryption_enabled(self):
"""Test PASS when hw_mem_encryption is True."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-1",
name="encrypted-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=True,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled import (
image_hw_mem_encryption_enabled,
)
check = image_hw_mem_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image encrypted-image (img-1) has hardware memory encryption enabled."
)
assert result[0].resource_id == "img-1"
assert result[0].resource_name == "encrypted-image"
assert result[0].region == OPENSTACK_REGION
def test_image_encryption_not_set(self):
"""Test FAIL when hw_mem_encryption is None."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-2",
name="unencrypted-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled import (
image_hw_mem_encryption_enabled,
)
check = image_hw_mem_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Image unencrypted-image (img-2) does not have hardware memory encryption enabled."
)
def test_image_encryption_false(self):
"""Test FAIL when hw_mem_encryption is False."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-3",
name="no-encrypt-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=False,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled import (
image_hw_mem_encryption_enabled,
)
check = image_hw_mem_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_multiple_images_mixed(self):
"""Test mixed results with encrypted and unencrypted images."""
image_client = mock.MagicMock()
base = dict(
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
image_client.images = [
ImageResource(
id="img-enc", name="encrypted", hw_mem_encryption=True, **base
),
ImageResource(
id="img-noenc", name="unencrypted", hw_mem_encryption=None, **base
),
ImageResource(
id="img-false", name="false-enc", hw_mem_encryption=False, **base
),
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_hw_mem_encryption_enabled.image_hw_mem_encryption_enabled import (
image_hw_mem_encryption_enabled,
)
check = image_hw_mem_encryption_enabled()
result = check.execute()
assert len(result) == 3
assert result[0].status == "PASS"
assert result[1].status == "FAIL"
assert result[2].status == "FAIL"

View File

@@ -0,0 +1,192 @@
"""Tests for image_not_publicly_visible check."""
from unittest import mock
from prowler.providers.openstack.services.image.image_service import ImageResource
from tests.providers.openstack.openstack_fixtures import (
OPENSTACK_PROJECT_ID,
OPENSTACK_REGION,
set_mocked_openstack_provider,
)
class Test_image_not_publicly_visible:
def test_no_images(self):
"""Test when no images exist."""
image_client = mock.MagicMock()
image_client.images = []
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_publicly_visible.image_not_publicly_visible.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_publicly_visible.image_not_publicly_visible import (
image_not_publicly_visible,
)
check = image_not_publicly_visible()
result = check.execute()
assert len(result) == 0
def test_image_private(self):
"""Test PASS when image is private."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-1",
name="private-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_publicly_visible.image_not_publicly_visible.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_publicly_visible.image_not_publicly_visible import (
image_not_publicly_visible,
)
check = image_not_publicly_visible()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image private-image (img-1) is not publicly visible (visibility=private)."
)
assert result[0].resource_id == "img-1"
assert result[0].resource_name == "private-image"
assert result[0].region == OPENSTACK_REGION
def test_image_public(self):
"""Test FAIL when image is public."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-2",
name="public-image",
status="active",
visibility="public",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_publicly_visible.image_not_publicly_visible.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_publicly_visible.image_not_publicly_visible import (
image_not_publicly_visible,
)
check = image_not_publicly_visible()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Image public-image (img-2) is publicly visible to all tenants."
)
assert result[0].resource_id == "img-2"
assert result[0].resource_name == "public-image"
assert result[0].region == OPENSTACK_REGION
def test_multiple_images_mixed(self):
"""Test mixed results with public, private, shared, and community images."""
image_client = mock.MagicMock()
base = dict(
status="active",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
image_client.images = [
ImageResource(id="img-pub", name="public-img", visibility="public", **base),
ImageResource(
id="img-priv", name="private-img", visibility="private", **base
),
ImageResource(
id="img-shared", name="shared-img", visibility="shared", **base
),
ImageResource(
id="img-comm", name="community-img", visibility="community", **base
),
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_publicly_visible.image_not_publicly_visible.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_publicly_visible.image_not_publicly_visible import (
image_not_publicly_visible,
)
check = image_not_publicly_visible()
result = check.execute()
assert len(result) == 4
assert result[0].status == "FAIL" # public
assert result[1].status == "PASS" # private
assert result[2].status == "PASS" # shared
assert result[3].status == "PASS" # community

View File

@@ -0,0 +1,417 @@
"""Tests for image_not_shared_with_multiple_projects check."""
from unittest import mock
from prowler.providers.openstack.services.image.image_service import (
ImageMember,
ImageResource,
)
from tests.providers.openstack.openstack_fixtures import (
OPENSTACK_PROJECT_ID,
OPENSTACK_REGION,
set_mocked_openstack_provider,
)
class Test_image_not_shared_with_multiple_projects:
def test_no_images(self):
"""Test when no images exist."""
image_client = mock.MagicMock()
image_client.images = []
image_client.audit_config = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects import (
image_not_shared_with_multiple_projects,
)
check = image_not_shared_with_multiple_projects()
result = check.execute()
assert len(result) == 0
def test_image_not_shared(self):
"""Test PASS when image is not shared."""
image_client = mock.MagicMock()
image_client.audit_config = {}
image_client.images = [
ImageResource(
id="img-1",
name="private-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects import (
image_not_shared_with_multiple_projects,
)
check = image_not_shared_with_multiple_projects()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image private-image (img-1) is not shared (visibility=private)."
)
assert result[0].resource_id == "img-1"
def test_image_shared_within_threshold(self):
"""Test PASS when shared image has accepted members within threshold."""
image_client = mock.MagicMock()
image_client.audit_config = {}
members = [
ImageMember(member_id=f"project-{i}", status="accepted") for i in range(3)
]
image_client.images = [
ImageResource(
id="img-2",
name="shared-image",
status="active",
visibility="shared",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=members,
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects import (
image_not_shared_with_multiple_projects,
)
check = image_not_shared_with_multiple_projects()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image shared-image (img-2) is shared with 3 accepted projects, within the threshold of 5."
)
def test_image_shared_at_threshold(self):
"""Test PASS when accepted members exactly equal threshold."""
image_client = mock.MagicMock()
image_client.audit_config = {}
members = [
ImageMember(member_id=f"project-{i}", status="accepted") for i in range(5)
]
image_client.images = [
ImageResource(
id="img-3",
name="threshold-image",
status="active",
visibility="shared",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=members,
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects import (
image_not_shared_with_multiple_projects,
)
check = image_not_shared_with_multiple_projects()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image threshold-image (img-3) is shared with 5 accepted projects, within the threshold of 5."
)
def test_image_shared_above_threshold(self):
"""Test FAIL when accepted members exceed threshold."""
image_client = mock.MagicMock()
image_client.audit_config = {}
members = [
ImageMember(member_id=f"project-{i}", status="accepted") for i in range(8)
]
image_client.images = [
ImageResource(
id="img-4",
name="overshared-image",
status="active",
visibility="shared",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=members,
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects import (
image_not_shared_with_multiple_projects,
)
check = image_not_shared_with_multiple_projects()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Image overshared-image (img-4) is shared with 8 accepted projects, exceeding the threshold of 5."
)
def test_pending_members_not_counted(self):
"""Test that pending and rejected members are not counted."""
image_client = mock.MagicMock()
image_client.audit_config = {}
members = [
ImageMember(member_id="project-1", status="accepted"),
ImageMember(member_id="project-2", status="pending"),
ImageMember(member_id="project-3", status="rejected"),
ImageMember(member_id="project-4", status="pending"),
ImageMember(member_id="project-5", status="accepted"),
ImageMember(member_id="project-6", status="pending"),
ImageMember(member_id="project-7", status="pending"),
ImageMember(member_id="project-8", status="pending"),
ImageMember(member_id="project-9", status="pending"),
ImageMember(member_id="project-10", status="pending"),
]
image_client.images = [
ImageResource(
id="img-5",
name="pending-members-image",
status="active",
visibility="shared",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=members,
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects import (
image_not_shared_with_multiple_projects,
)
check = image_not_shared_with_multiple_projects()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image pending-members-image (img-5) is shared with 2 accepted projects, within the threshold of 5."
)
def test_custom_threshold_via_audit_config(self):
"""Test custom threshold from audit_config."""
image_client = mock.MagicMock()
image_client.audit_config = {"image_sharing_threshold": 2}
members = [
ImageMember(member_id=f"project-{i}", status="accepted") for i in range(3)
]
image_client.images = [
ImageResource(
id="img-6",
name="custom-threshold-image",
status="active",
visibility="shared",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=members,
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects import (
image_not_shared_with_multiple_projects,
)
check = image_not_shared_with_multiple_projects()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Image custom-threshold-image (img-6) is shared with 3 accepted projects, exceeding the threshold of 2."
)
def test_multiple_images_mixed(self):
"""Test mixed results with shared and non-shared images."""
image_client = mock.MagicMock()
image_client.audit_config = {}
base = dict(
status="active",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
image_client.images = [
ImageResource(
id="img-priv",
name="private",
visibility="private",
members=[],
**base,
),
ImageResource(
id="img-over",
name="overshared",
visibility="shared",
members=[
ImageMember(member_id=f"p-{i}", status="accepted") for i in range(6)
],
**base,
),
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_not_shared_with_multiple_projects.image_not_shared_with_multiple_projects import (
image_not_shared_with_multiple_projects,
)
check = image_not_shared_with_multiple_projects()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS" # private
assert result[1].status == "FAIL" # overshared

View File

@@ -0,0 +1,180 @@
"""Tests for image_protected_status_enabled check."""
from unittest import mock
from prowler.providers.openstack.services.image.image_service import ImageResource
from tests.providers.openstack.openstack_fixtures import (
OPENSTACK_PROJECT_ID,
OPENSTACK_REGION,
set_mocked_openstack_provider,
)
class Test_image_protected_status_enabled:
def test_no_images(self):
"""Test when no images exist."""
image_client = mock.MagicMock()
image_client.images = []
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_protected_status_enabled.image_protected_status_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_protected_status_enabled.image_protected_status_enabled import (
image_protected_status_enabled,
)
check = image_protected_status_enabled()
result = check.execute()
assert len(result) == 0
def test_image_protected(self):
"""Test PASS when image is protected."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-1",
name="protected-image",
status="active",
visibility="private",
protected=True,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_protected_status_enabled.image_protected_status_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_protected_status_enabled.image_protected_status_enabled import (
image_protected_status_enabled,
)
check = image_protected_status_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image protected-image (img-1) has deletion protection enabled."
)
assert result[0].resource_id == "img-1"
assert result[0].resource_name == "protected-image"
assert result[0].region == OPENSTACK_REGION
def test_image_not_protected(self):
"""Test FAIL when image is not protected."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-2",
name="unprotected-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_protected_status_enabled.image_protected_status_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_protected_status_enabled.image_protected_status_enabled import (
image_protected_status_enabled,
)
check = image_protected_status_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Image unprotected-image (img-2) does not have deletion protection enabled."
)
assert result[0].resource_id == "img-2"
def test_multiple_images_mixed(self):
"""Test mixed results with protected and unprotected images."""
image_client = mock.MagicMock()
base = dict(
status="active",
visibility="private",
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
image_client.images = [
ImageResource(id="img-p", name="protected", protected=True, **base),
ImageResource(id="img-u", name="unprotected", protected=False, **base),
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_protected_status_enabled.image_protected_status_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_protected_status_enabled.image_protected_status_enabled import (
image_protected_status_enabled,
)
check = image_protected_status_enabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert result[1].status == "FAIL"

View File

@@ -0,0 +1,277 @@
"""Tests for image_secure_boot_enabled check."""
from unittest import mock
from prowler.providers.openstack.services.image.image_service import ImageResource
from tests.providers.openstack.openstack_fixtures import (
OPENSTACK_PROJECT_ID,
OPENSTACK_REGION,
set_mocked_openstack_provider,
)
class Test_image_secure_boot_enabled:
def test_no_images(self):
"""Test when no images exist."""
image_client = mock.MagicMock()
image_client.images = []
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled import (
image_secure_boot_enabled,
)
check = image_secure_boot_enabled()
result = check.execute()
assert len(result) == 0
def test_image_secure_boot_required(self):
"""Test PASS when os_secure_boot is 'required'."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-1",
name="secure-boot-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot="required",
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled import (
image_secure_boot_enabled,
)
check = image_secure_boot_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image secure-boot-image (img-1) has Secure Boot set to required."
)
assert result[0].resource_id == "img-1"
assert result[0].resource_name == "secure-boot-image"
assert result[0].region == OPENSTACK_REGION
def test_image_secure_boot_not_set(self):
"""Test FAIL when os_secure_boot is None."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-2",
name="no-secureboot-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled import (
image_secure_boot_enabled,
)
check = image_secure_boot_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Image no-secureboot-image (img-2) does not have Secure Boot set to required (os_secure_boot=None)."
)
def test_image_secure_boot_optional(self):
"""Test FAIL when os_secure_boot is 'optional'."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-3",
name="optional-secureboot",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot="optional",
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled import (
image_secure_boot_enabled,
)
check = image_secure_boot_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_image_secure_boot_disabled(self):
"""Test FAIL when os_secure_boot is 'disabled'."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-4",
name="disabled-secureboot",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot="disabled",
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled import (
image_secure_boot_enabled,
)
check = image_secure_boot_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_multiple_images_mixed(self):
"""Test mixed results with various secure boot settings."""
image_client = mock.MagicMock()
base = dict(
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
image_client.images = [
ImageResource(
id="img-req", name="required", os_secure_boot="required", **base
),
ImageResource(
id="img-opt", name="optional", os_secure_boot="optional", **base
),
ImageResource(
id="img-dis", name="disabled", os_secure_boot="disabled", **base
),
ImageResource(id="img-none", name="none-set", os_secure_boot=None, **base),
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_secure_boot_enabled.image_secure_boot_enabled import (
image_secure_boot_enabled,
)
check = image_secure_boot_enabled()
result = check.execute()
assert len(result) == 4
assert result[0].status == "PASS" # required
assert result[1].status == "FAIL" # optional
assert result[2].status == "FAIL" # disabled
assert result[3].status == "FAIL" # None

View File

@@ -0,0 +1,593 @@
"""Tests for OpenStack Image service."""
from unittest.mock import MagicMock, patch
from openstack import exceptions as openstack_exceptions
from prowler.providers.openstack.services.image.image_service import (
Image,
ImageMember,
ImageResource,
)
from tests.providers.openstack.openstack_fixtures import (
OPENSTACK_PROJECT_ID,
OPENSTACK_REGION,
set_mocked_openstack_provider,
)
class TestImageService:
"""Test suite for Image service."""
def test_image_service_initialization(self):
"""Test Image service initializes correctly."""
provider = set_mocked_openstack_provider()
with patch.object(Image, "_list_images", return_value=[]):
image_service = Image(provider)
assert image_service.service_name == "Image"
assert image_service.provider == provider
assert image_service.connection == provider.connection
assert image_service.regional_connections == provider.regional_connections
assert image_service.audited_regions == [OPENSTACK_REGION]
assert image_service.region == OPENSTACK_REGION
assert image_service.project_id == OPENSTACK_PROJECT_ID
assert image_service.images == []
def test_image_list_images_success(self):
"""Test listing images successfully."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-1"
mock_img.name = "ubuntu-22.04"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = True
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = ["production"]
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
provider.connection.image.images.return_value = [mock_img]
image_service = Image(provider)
assert len(image_service.images) == 1
assert isinstance(image_service.images[0], ImageResource)
assert image_service.images[0].id == "img-1"
assert image_service.images[0].name == "ubuntu-22.04"
assert image_service.images[0].status == "active"
assert image_service.images[0].visibility == "private"
assert image_service.images[0].protected is True
assert image_service.images[0].tags == ["production"]
assert image_service.images[0].members == []
def test_image_list_images_with_signature(self):
"""Test listing images with signature properties."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-signed"
mock_img.name = "signed-image"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = "abc123sig"
mock_img.img_signature_hash_method = "SHA-256"
mock_img.img_signature_key_type = "RSA-PSS"
mock_img.img_signature_certificate_uuid = "cert-uuid-123"
mock_img.hw_mem_encryption = True
mock_img.needs_secure_boot = "required"
mock_img.os_secure_boot = "required"
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
provider.connection.image.images.return_value = [mock_img]
image_service = Image(provider)
assert len(image_service.images) == 1
img = image_service.images[0]
assert img.img_signature == "abc123sig"
assert img.img_signature_hash_method == "SHA-256"
assert img.img_signature_key_type == "RSA-PSS"
assert img.img_signature_certificate_uuid == "cert-uuid-123"
assert img.hw_mem_encryption is True
assert img.os_secure_boot == "required"
def test_image_list_images_shared_with_members(self):
"""Test listing shared images fetches members."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-shared"
mock_img.name = "shared-image"
mock_img.status = "active"
mock_img.visibility = "shared"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
mock_member = MagicMock()
mock_member.member_id = "project-2"
mock_member.id = "project-2"
mock_member.status = "accepted"
provider.connection.image.images.return_value = [mock_img]
provider.connection.image.members.return_value = [mock_member]
image_service = Image(provider)
assert len(image_service.images) == 1
assert len(image_service.images[0].members) == 1
assert isinstance(image_service.images[0].members[0], ImageMember)
assert image_service.images[0].members[0].member_id == "project-2"
assert image_service.images[0].members[0].status == "accepted"
provider.connection.image.members.assert_called_once_with("img-shared")
def test_image_list_images_private_no_member_fetch(self):
"""Test that private images do not trigger member listing."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-private"
mock_img.name = "private-image"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
provider.connection.image.images.return_value = [mock_img]
image_service = Image(provider)
assert len(image_service.images) == 1
assert image_service.images[0].members == []
provider.connection.image.members.assert_not_called()
def test_image_list_images_empty(self):
"""Test listing images when none exist."""
provider = set_mocked_openstack_provider()
provider.connection.image.images.return_value = []
image_service = Image(provider)
assert image_service.images == []
def test_image_list_images_sdk_exception(self):
"""Test handling SDKException when listing images."""
provider = set_mocked_openstack_provider()
provider.connection.image.images.side_effect = (
openstack_exceptions.SDKException("API error")
)
image_service = Image(provider)
assert image_service.images == []
def test_image_list_images_generic_exception(self):
"""Test handling generic Exception when listing images."""
provider = set_mocked_openstack_provider()
provider.connection.image.images.side_effect = Exception("Unexpected error")
image_service = Image(provider)
assert image_service.images == []
def test_image_list_image_members_sdk_exception(self):
"""Test handling SDKException when listing image members."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-shared-err"
mock_img.name = "shared-error-image"
mock_img.status = "active"
mock_img.visibility = "shared"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
provider.connection.image.images.return_value = [mock_img]
provider.connection.image.members.side_effect = (
openstack_exceptions.SDKException("Members API error")
)
image_service = Image(provider)
assert len(image_service.images) == 1
assert image_service.images[0].members == []
def test_image_hw_mem_encryption_false_not_overridden_by_properties(self):
"""Test that hw_mem_encryption=False is preserved, not overridden by properties dict."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-enc-false"
mock_img.name = "encryption-false-image"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = False
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {"hw_mem_encryption": "true"}
provider.connection.image.images.return_value = [mock_img]
image_service = Image(provider)
assert len(image_service.images) == 1
assert image_service.images[0].hw_mem_encryption is False
def test_image_os_secure_boot_disabled_not_overridden_by_properties(self):
"""Test that os_secure_boot='disabled' is preserved, not overridden by properties dict."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-boot-disabled"
mock_img.name = "boot-disabled-image"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = "disabled"
mock_img.os_secure_boot = "disabled"
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {"os_secure_boot": "required"}
provider.connection.image.images.return_value = [mock_img]
image_service = Image(provider)
assert len(image_service.images) == 1
assert image_service.images[0].os_secure_boot == "disabled"
def test_image_signature_empty_string_not_overridden_by_properties(self):
"""Test that empty string signature attrs are preserved, not overridden by properties."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-sig-empty"
mock_img.name = "sig-empty-image"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = ""
mock_img.img_signature_hash_method = ""
mock_img.img_signature_key_type = ""
mock_img.img_signature_certificate_uuid = ""
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {
"img_signature": "should-not-override",
"img_signature_hash_method": "should-not-override",
"img_signature_key_type": "should-not-override",
"img_signature_certificate_uuid": "should-not-override",
}
provider.connection.image.images.return_value = [mock_img]
image_service = Image(provider)
assert len(image_service.images) == 1
img = image_service.images[0]
assert img.img_signature == ""
assert img.img_signature_hash_method == ""
assert img.img_signature_key_type == ""
assert img.img_signature_certificate_uuid == ""
def test_image_properties_fallback_when_attrs_are_none(self):
"""Test that properties dict is used as fallback when image attrs are None."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-fallback"
mock_img.name = "fallback-image"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {
"img_signature": "prop-sig",
"img_signature_hash_method": "SHA-256",
"img_signature_key_type": "RSA-PSS",
"img_signature_certificate_uuid": "cert-from-props",
"hw_mem_encryption": "true",
"os_secure_boot": "required",
}
provider.connection.image.images.return_value = [mock_img]
image_service = Image(provider)
assert len(image_service.images) == 1
img = image_service.images[0]
assert img.img_signature == "prop-sig"
assert img.img_signature_hash_method == "SHA-256"
assert img.img_signature_key_type == "RSA-PSS"
assert img.img_signature_certificate_uuid == "cert-from-props"
assert img.hw_mem_encryption is True
assert img.os_secure_boot == "required"
def test_image_needs_secure_boot_sdk_attr_resolved(self):
"""Test that needs_secure_boot (SDK attr) is used when os_secure_boot is absent."""
provider = set_mocked_openstack_provider()
mock_img = MagicMock()
mock_img.id = "img-sdk-boot"
mock_img.name = "sdk-boot-image"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = "required"
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
provider.connection.image.images.return_value = [mock_img]
image_service = Image(provider)
assert len(image_service.images) == 1
assert image_service.images[0].os_secure_boot == "required"
def test_image_list_images_multi_region(self):
"""Test listing images across multiple regions."""
provider = set_mocked_openstack_provider()
mock_conn_uk1 = MagicMock()
mock_conn_de1 = MagicMock()
provider.regional_connections = {"UK1": mock_conn_uk1, "DE1": mock_conn_de1}
mock_img_uk = MagicMock()
mock_img_uk.id = "img-uk"
mock_img_uk.name = "ubuntu-uk"
mock_img_uk.status = "active"
mock_img_uk.visibility = "private"
mock_img_uk.is_protected = False
mock_img_uk.owner_id = OPENSTACK_PROJECT_ID
mock_img_uk.owner = OPENSTACK_PROJECT_ID
mock_img_uk.img_signature = None
mock_img_uk.img_signature_hash_method = None
mock_img_uk.img_signature_key_type = None
mock_img_uk.img_signature_certificate_uuid = None
mock_img_uk.hw_mem_encryption = None
mock_img_uk.needs_secure_boot = None
mock_img_uk.os_secure_boot = None
mock_img_uk.tags = []
mock_img_uk.project_id = OPENSTACK_PROJECT_ID
mock_img_uk.properties = {}
mock_img_de = MagicMock()
mock_img_de.id = "img-de"
mock_img_de.name = "ubuntu-de"
mock_img_de.status = "active"
mock_img_de.visibility = "private"
mock_img_de.is_protected = False
mock_img_de.owner_id = OPENSTACK_PROJECT_ID
mock_img_de.owner = OPENSTACK_PROJECT_ID
mock_img_de.img_signature = None
mock_img_de.img_signature_hash_method = None
mock_img_de.img_signature_key_type = None
mock_img_de.img_signature_certificate_uuid = None
mock_img_de.hw_mem_encryption = None
mock_img_de.needs_secure_boot = None
mock_img_de.os_secure_boot = None
mock_img_de.tags = []
mock_img_de.project_id = OPENSTACK_PROJECT_ID
mock_img_de.properties = {}
mock_conn_uk1.image.images.return_value = [mock_img_uk]
mock_conn_de1.image.images.return_value = [mock_img_de]
image_service = Image(provider)
assert len(image_service.images) == 2
uk_img = next(i for i in image_service.images if i.id == "img-uk")
de_img = next(i for i in image_service.images if i.id == "img-de")
assert uk_img.region == "UK1"
assert de_img.region == "DE1"
def test_image_list_images_multi_region_partial_failure(self):
"""Test that a failing region doesn't prevent other regions from being listed."""
provider = set_mocked_openstack_provider()
mock_conn_ok = MagicMock()
mock_conn_fail = MagicMock()
provider.regional_connections = {"UK1": mock_conn_ok, "DE1": mock_conn_fail}
mock_img = MagicMock()
mock_img.id = "img-uk"
mock_img.name = "ubuntu-uk"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
mock_conn_ok.image.images.return_value = [mock_img]
mock_conn_fail.image.images.side_effect = openstack_exceptions.SDKException(
"API error in DE1"
)
image_service = Image(provider)
assert len(image_service.images) == 1
assert image_service.images[0].id == "img-uk"
assert image_service.images[0].region == "UK1"
def test_image_list_images_multi_region_one_empty(self):
"""Test multi-region where one region has images and the other is empty."""
provider = set_mocked_openstack_provider()
mock_conn_uk1 = MagicMock()
mock_conn_de1 = MagicMock()
provider.regional_connections = {"UK1": mock_conn_uk1, "DE1": mock_conn_de1}
mock_img = MagicMock()
mock_img.id = "img-uk"
mock_img.name = "ubuntu-uk"
mock_img.status = "active"
mock_img.visibility = "private"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
mock_conn_uk1.image.images.return_value = [mock_img]
mock_conn_de1.image.images.return_value = []
image_service = Image(provider)
assert len(image_service.images) == 1
assert image_service.images[0].id == "img-uk"
assert image_service.images[0].region == "UK1"
def test_image_list_images_multi_region_shared_with_members(self):
"""Test listing shared images fetches members using the correct regional connection."""
provider = set_mocked_openstack_provider()
mock_conn_uk1 = MagicMock()
mock_conn_de1 = MagicMock()
provider.regional_connections = {"UK1": mock_conn_uk1, "DE1": mock_conn_de1}
mock_img = MagicMock()
mock_img.id = "img-shared-uk"
mock_img.name = "shared-uk"
mock_img.status = "active"
mock_img.visibility = "shared"
mock_img.is_protected = False
mock_img.owner_id = OPENSTACK_PROJECT_ID
mock_img.owner = OPENSTACK_PROJECT_ID
mock_img.img_signature = None
mock_img.img_signature_hash_method = None
mock_img.img_signature_key_type = None
mock_img.img_signature_certificate_uuid = None
mock_img.hw_mem_encryption = None
mock_img.needs_secure_boot = None
mock_img.os_secure_boot = None
mock_img.tags = []
mock_img.project_id = OPENSTACK_PROJECT_ID
mock_img.properties = {}
mock_member = MagicMock()
mock_member.member_id = "project-2"
mock_member.id = "project-2"
mock_member.status = "accepted"
mock_conn_uk1.image.images.return_value = [mock_img]
mock_conn_uk1.image.members.return_value = [mock_member]
mock_conn_de1.image.images.return_value = []
image_service = Image(provider)
assert len(image_service.images) == 1
assert image_service.images[0].region == "UK1"
assert len(image_service.images[0].members) == 1
assert image_service.images[0].members[0].member_id == "project-2"
mock_conn_uk1.image.members.assert_called_once_with("img-shared-uk")

View File

@@ -0,0 +1,280 @@
"""Tests for image_signature_verification_enabled check."""
from unittest import mock
from prowler.providers.openstack.services.image.image_service import ImageResource
from tests.providers.openstack.openstack_fixtures import (
OPENSTACK_PROJECT_ID,
OPENSTACK_REGION,
set_mocked_openstack_provider,
)
class Test_image_signature_verification_enabled:
def test_no_images(self):
"""Test when no images exist."""
image_client = mock.MagicMock()
image_client.images = []
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled import (
image_signature_verification_enabled,
)
check = image_signature_verification_enabled()
result = check.execute()
assert len(result) == 0
def test_image_fully_signed(self):
"""Test PASS when all four signature properties are set."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-1",
name="signed-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature="abc123sig",
img_signature_hash_method="SHA-256",
img_signature_key_type="RSA-PSS",
img_signature_certificate_uuid="cert-uuid-123",
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled import (
image_signature_verification_enabled,
)
check = image_signature_verification_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Image signed-image (img-1) has all signature verification properties configured."
)
assert result[0].resource_id == "img-1"
assert result[0].resource_name == "signed-image"
assert result[0].region == OPENSTACK_REGION
def test_image_no_signatures(self):
"""Test FAIL when no signature properties are set."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-2",
name="unsigned-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled import (
image_signature_verification_enabled,
)
check = image_signature_verification_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Image unsigned-image (img-2) does not have all signature verification properties configured."
)
def test_image_partial_signatures(self):
"""Test FAIL when only some signature properties are set."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-3",
name="partial-sig-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature="abc123sig",
img_signature_hash_method="SHA-256",
img_signature_key_type=None,
img_signature_certificate_uuid=None,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled import (
image_signature_verification_enabled,
)
check = image_signature_verification_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_image_empty_string_signatures(self):
"""Test FAIL when signature properties are empty strings."""
image_client = mock.MagicMock()
image_client.images = [
ImageResource(
id="img-4",
name="empty-sig-image",
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
img_signature="",
img_signature_hash_method="",
img_signature_key_type="",
img_signature_certificate_uuid="",
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled import (
image_signature_verification_enabled,
)
check = image_signature_verification_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_multiple_images_mixed(self):
"""Test mixed results with signed and unsigned images."""
image_client = mock.MagicMock()
base = dict(
status="active",
visibility="private",
protected=False,
owner=OPENSTACK_PROJECT_ID,
hw_mem_encryption=None,
os_secure_boot=None,
members=[],
tags=[],
project_id=OPENSTACK_PROJECT_ID,
region=OPENSTACK_REGION,
)
image_client.images = [
ImageResource(
id="img-signed",
name="signed",
img_signature="sig",
img_signature_hash_method="SHA-256",
img_signature_key_type="RSA-PSS",
img_signature_certificate_uuid="cert-uuid",
**base,
),
ImageResource(
id="img-unsigned",
name="unsigned",
img_signature=None,
img_signature_hash_method=None,
img_signature_key_type=None,
img_signature_certificate_uuid=None,
**base,
),
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_openstack_provider(),
),
mock.patch(
"prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled.image_client",
new=image_client,
),
):
from prowler.providers.openstack.services.image.image_signature_verification_enabled.image_signature_verification_enabled import (
image_signature_verification_enabled,
)
check = image_signature_verification_enabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert result[1].status == "FAIL"