mcpField
P
prowler-test-sdk

prowler-test-sdk

github.com/prowler-cloud测试与质量15.1k66 次安装今天更新
代码仓库

关于 prowler-test-sdk

prowler-test-sdk 是用于编写符合 Prowler SDK 规范的测试的技能,涵盖按提供方选择模拟方式、测试目录结构、必测场景、断言和运行命令。

主要功能

  1. 01AWS 使用 moto,其他提供方使用 MagicMock
  2. 02遵循各提供方的 fixture 模式
  3. 03组织服务与检查测试文件
  4. 04覆盖合规、不合规及无资源结果
  5. 05断言状态和资源信息
  6. 06运行 SDK、提供方或单项检查测试

适用场景

  1. 01为新的 Prowler 安全检查编写测试
  2. 02按提供方选择正确的模拟方式
  3. 03验证没有资源时的行为
  4. 04运行某个提供方或单项检查的测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/prowler-cloud/prowler --skill prowler-test-sdk

安装后在 Claude Code 中输入/prowler-test-sdk确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
aws_test.py
azure_test.py
gcp_test.py
references/
testing-docs.md

详细介绍

通用模式:关于基础 pytest 模式(fixtures、模拟、参数化、标记),请参阅 pytest 技能。 本技能仅介绍 Prowler 专属约定。

完整文档:docs/developer-guide/unit-testing.mdx

关键:针对不同云服务提供商的测试

云服务提供商模拟方式装饰器
AWSmoto 库@mock_aws
Azure、GCP、K8s 等MagicMock无

绝对不要对非 AWS 云服务提供商使用 moto。绝对不要对 AWS 使用 MagicMock。


AWS 检查测试模式

from unittest import mock
from boto3 import client
from moto import mock_aws
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider


class Test_{check_name}:
    @mock_aws
    def test_no_resources(self):
        from prowler.providers.aws.services.{service}.{service}_service import {ServiceClass}

        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])

        with mock.patch(
            "prowler.providers.common.provider.Provider.get_global_provider",
            return_value=aws_provider,
        ):
            with mock.patch(
                "prowler.providers.aws.services.{service}.{check_name}.{check_name}.{service}_client",
                new={ServiceClass}(aws_provider),
            ):
                from prowler.providers.aws.services.{service}.{check_name}.{check_name} import (
                    {check_name},
                )

                check = {check_name}()
                result = check.execute()

                assert len(result) == 0

    @mock_aws
    def test_{check_name}_pass(self):
        # Setup AWS resources with moto
        {service}_client = client("{service}", region_name=AWS_REGION_US_EAST_1)
        # Create compliant resource...

        from prowler.providers.aws.services.{service}.{service}_service import {ServiceClass}

        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])

        with mock.patch(
            "prowler.providers.common.provider.Provider.get_global_provider",
            return_value=aws_provider,
        ):
            with mock.patch(
                "prowler.providers.aws.services.{service}.{check_name}.{check_name}.{service}_client",
                new={ServiceClass}(aws_provider),
            ):
                from prowler.providers.aws.services.{service}.{check_name}.{check_name} import (
                    {check_name},
                )

                check = {check_name}()
                result = check.execute()

                assert len(result) == 1
                assert result[0].status == "PASS"

    @mock_aws
    def test_{check_name}_fail(self):
        # Setup AWS resources with moto
        {service}_client = client("{service}", region_name=AWS_REGION_US_EAST_1)
        # Create non-compliant resource...

        from prowler.providers.aws.services.{service}.{service}_service import {ServiceClass}

        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])

        with mock.patch(
            "prowler.providers.common.provider.Provider.get_global_provider",
            return_value=aws_provider,
        ):
            with mock.patch(
                "prowler.providers.aws.services.{service}.{check_name}.{check_name}.{service}_client",
                new={ServiceClass}(aws_provider),
            ):
                from prowler.providers.aws.services.{service}.{check_name}.{check_name} import (
                    {check_name},
                )

                check = {check_name}()
                result = check.execute()

                assert len(result) == 1
                assert result[0].status == "FAIL"

重要:始终在 mock.patch 上下文中导入检查项,以确保正确模拟客户端。


Azure 检查测试模式

不要使用 moto 装饰器。使用 MagicMock 直接模拟服务客户端。

from unittest import mock
from uuid import uuid4

from prowler.providers.azure.services.{service}.{service}_service import {ResourceModel}
from tests.providers.azure.azure_fixtures import (
    AZURE_SUBSCRIPTION_ID,
    set_mocked_azure_provider,
)


class Test_{check_name}:
    def test_no_resources(self):
        {service}_client = mock.MagicMock
        {service}_client.{resources} = {}

        with (
            mock.patch(
                "prowler.providers.common.provider.Provider.get_global_provider",
                return_value=set_mocked_azure_provider(),
            ),
            mock.patch(
                "prowler.providers.azure.services.{service}.{check_name}.{check_name}.{service}_client",
                new={service}_client,
            ),
        ):
            from prowler.providers.azure.services.{service}.{check_name}.{check_name} import (
                {check_name},
            )

            check = {check_name}()
            result = check.execute()
            assert len(result) == 0

    def test_{check_name}_pass(self):
        resource_id = str(uuid4())
        resource_name = "Test Resource"

        {service}_client = mock.MagicMock
        {service}_client.{resources} = {
            AZURE_SUBSCRIPTION_ID: {
                resource_id: {ResourceModel}(
                    id=resource_id,
                    name=resource_name,
                    location="westeurope",
                    # ... compliant attributes
                )
            }
        }

        with (
            mock.patch(
                "prowler.providers.common.provider.Provider.get_global_provider",
                return_value=set_mocked_azure_provider(),
            ),
            mock.patch(
                "prowler.providers.azure.services.{service}.{check_name}.{check_name}.{service}_client",
                new={service}_client,
            ),
        ):
            from prowler.providers.azure.services.{service}.{check_name}.{check_name} import (
                {check_name},
            )

            check = {check_name}()
            result = check.execute()

            assert len(result) == 1
            assert result[0].status == "PASS"
            assert result[0].subscription == AZURE_SUBSCRIPTION_ID
            assert result[0].resource_name == resource_name

    def test_{check_name}_fail(self):
        resource_id = str(uuid4())
        resource_name = "Test Resource"

        {service}_client = mock.MagicMock
        {service}_client.{resources} = {
            AZURE_SUBSCRIPTION_ID: {
                resource_id: {ResourceModel}(
                    id=resource_id,
                    name=resource_name,
                    location="westeurope",
                    # ... non-compliant attributes
                )
            }
        }

        with (
            mock.patch(
                "prowler.providers.common.provider.Provider.get_global_provider",
                return_value=set_mocked_azure_provider(),
            ),
            mock.patch(
                "prowler.providers.azure.services.{service}.{check_name}.{check_name}.{service}_client",
                new={service}_client,
            ),
        ):
            from prowler.providers.azure.services.{service}.{check_name}.{check_name} import (
                {check_name},
            )

            check = {check_name}()
            result = check.execute()

            assert len(result) == 1
            assert result[0].status == "FAIL"

GCP/Kubernetes/其他云服务提供商

遵循与 Azure 相同的 MagicMock 模式:

from tests.providers.gcp.gcp_fixtures import set_mocked_gcp_provider, GCP_PROJECT_ID
from tests.providers.kubernetes.kubernetes_fixtures import set_mocked_kubernetes_provider

主要区别:每个云服务提供商都有自己的 fixtures 文件,其中包含 set_mocked_{provider}_provider。


云服务提供商 Fixtures 参考

云服务提供商Fixtures 文件关键常量
AWStests/providers/aws/utils.pyAWS_REGION_US_EAST_1、AWS_ACCOUNT_NUMBER
Azuretests/providers/azure/azure_fixtures.pyAZURE_SUBSCRIPTION_ID
GCPtests/providers/gcp/gcp_fixtures.pyGCP_PROJECT_ID
K8stests/providers/kubernetes/kubernetes_fixtures.py-

测试文件结构

tests/providers/{provider}/services/{service}/
├── {service}_service_test.py      # Service tests
└── {check_name}/
    └── {check_name}_test.py       # Check tests

注意:不要在测试文件夹中创建 __init__.py 文件。


必需的测试场景

每个检查项都必须测试:

场景预期结果
资源符合要求status == "PASS"
资源不符合要求status == "FAIL"
没有资源len(results) == 0

应包含的断言

# Always verify these
assert result[0].status == "PASS"  # or "FAIL"
assert result[0].status_extended == "Expected message..."
assert result[0].resource_id == expected_id
assert result[0].resource_name == expected_name

# Provider-specific
assert result[0].region == "us-east-1"           # AWS
assert result[0].subscription == AZURE_SUBSCRIPTION_ID  # Azure
assert result[0].project_id == GCP_PROJECT_ID    # GCP

命令

# All SDK tests
uv run pytest -n auto -vvv tests/

# Specific provider
uv run pytest tests/providers/{provider}/ -v

# Specific check
uv run pytest tests/providers/{provider}/services/{service}/{check_name}/ -v

# Stop on first failure
uv run pytest -x tests/

资源

  • 模板:请参阅 assets/ 获取完整的测试模板(使用 moto 的 AWS 模板、使用 MagicMock 的 Azure/GCP 模板)
  • 文档:请参阅 references/testing-docs.md,获取 Prowler 开发者指南的官方链接

评论

支持 Markdown