feat(checks): PHP runtime coverage after the October security release (#13127)

Co-authored-by: Rubén De la Torre Vico <98956809+puchy22@users.noreply.github.com>
This commit is contained in:
Pedro MartínandRubén De la Torre Vico authored and GitHub committed 2026-10-23 12:49:36 +02:00
1 parent 35d25a7218
commit 669df91ac9
63 files changed
+1928 -732

No files matched your search

+13
View File
@@ -111,6 +111,19 @@ config_aws = {
"max_idle_disconnect_timeout_in_seconds": 600,
"max_disconnect_timeout_in_seconds": 300,
"max_session_duration_seconds": 36000,
"elasticbeanstalk_php_min_branch": "8.2",
"elasticbeanstalk_php_min_platform_version": "4.13.13",
"lightsail_php_blueprints": [
"wordpress",
"lamp",
"nginx",
"drupal",
"joomla",
"magento",
"prestashop",
"plesk",
"cpanel",
],
"obsolete_lambda_runtimes": [
"java8",
"go1.x",
+23
View File
@@ -93,6 +93,28 @@ aws:
# aws.appstream_fleet_maximum_session_duration
max_session_duration_seconds: 36000 # 10 Hours
# AWS Elastic Beanstalk Configuration
# aws.elasticbeanstalk_environment_php_platform_updated
# Oldest PHP branch still receiving security fixes
elasticbeanstalk_php_min_branch: "8.2"
# Oldest Amazon Linux 2023 PHP platform version bundling the fixed PHP release
elasticbeanstalk_php_min_platform_version: "4.13.13"
# AWS Lightsail Configuration
# aws.lightsail_instance_php_blueprint_updated
# Blueprint id prefixes that ship PHP and require manual runtime verification
lightsail_php_blueprints:
[
"wordpress",
"lamp",
"nginx",
"drupal",
"joomla",
"magento",
"prestashop",
"plesk",
"cpanel",
]
# AWS Lambda Configuration
# aws.awslambda_function_using_supported_runtimes
obsolete_lambda_runtimes:
@@ -415,6 +437,7 @@ azure:
defender_attack_path_minimal_risk_level: "High"
# Azure App Service
# Minimum runtime versions; any newer version passes
# azure.app_ensure_php_version_is_latest
php_latest_version: "8.2"
# azure.app_ensure_python_version_is_latest
@@ -0,0 +1,136 @@
from unittest import mock
import pytest
from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import (
Environment,
)
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_EU_WEST_1,
set_mocked_aws_provider,
)
CHECK_PATH = "prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_php_platform_updated.elasticbeanstalk_environment_php_platform_updated"
ENV_ARN = f"arn:aws:elasticbeanstalk:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:environment/test-app/test-env"
def _environment(solution_stack_name="", platform_arn=""):
return Environment(
id="e-abc123",
name="test-env",
arn=ENV_ARN,
region=AWS_REGION_EU_WEST_1,
application_name="test-app",
solution_stack_name=solution_stack_name,
platform_arn=platform_arn,
)
def _run_check(environments, audit_config=None):
elasticbeanstalk_client = mock.MagicMock()
elasticbeanstalk_client.environments = environments
elasticbeanstalk_client.audit_config = audit_config or {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider([AWS_REGION_EU_WEST_1]),
),
mock.patch(
f"{CHECK_PATH}.elasticbeanstalk_client", new=elasticbeanstalk_client
),
):
from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_environment_php_platform_updated.elasticbeanstalk_environment_php_platform_updated import (
elasticbeanstalk_environment_php_platform_updated,
)
return elasticbeanstalk_environment_php_platform_updated().execute()
class Test_elasticbeanstalk_environment_php_platform_updated:
def test_no_environments(self):
assert len(_run_check({})) == 0
def test_non_php_environment_is_skipped(self):
environments = {
ENV_ARN: _environment("64bit Amazon Linux 2023 v4.7.3 running Python 3.12")
}
assert len(_run_check(environments)) == 0
def test_environment_without_stack_is_skipped(self):
assert len(_run_check({ENV_ARN: _environment()})) == 0
@pytest.mark.parametrize(
"solution_stack_name,expected_status,expected_message",
[
(
"64bit Amazon Linux 2023 v4.13.13 running PHP 8.4",
"PASS",
"Elastic Beanstalk environment test-env runs PHP 8.4 on platform version 4.13.13, which includes the fixed PHP release.",
),
(
"64bit Amazon Linux 2023 v4.14.0 running PHP 8.2",
"PASS",
"Elastic Beanstalk environment test-env runs PHP 8.2 on platform version 4.14.0, which includes the fixed PHP release.",
),
(
"64bit Amazon Linux 2023 v4.13.12 running PHP 8.5",
"FAIL",
"Elastic Beanstalk environment test-env runs PHP 8.5 on platform version 4.13.12, below the minimum fixed platform version 4.13.13.",
),
(
"64bit Amazon Linux 2023 v4.9.2 running PHP 8.3",
"FAIL",
"Elastic Beanstalk environment test-env runs PHP 8.3 on platform version 4.9.2, below the minimum fixed platform version 4.13.13.",
),
(
"64bit Amazon Linux 2023 v4.13.13 running PHP 8.1",
"FAIL",
"Elastic Beanstalk environment test-env runs PHP 8.1, a branch that no longer receives security fixes (minimum supported branch is 8.2).",
),
(
"64bit Amazon Linux 2 v3.9.5 running PHP 8.2",
"FAIL",
"Elastic Beanstalk environment test-env runs PHP 8.2 on platform '64bit Amazon Linux 2 v3.9.5 running PHP 8.2', a platform family without fixed PHP releases; migrate it to a PHP 8.2 Amazon Linux 2023 platform version 4.13.13 or newer.",
),
],
)
def test_php_environment(
self, solution_stack_name, expected_status, expected_message
):
result = _run_check({ENV_ARN: _environment(solution_stack_name)})
assert len(result) == 1
assert result[0].status == expected_status
assert result[0].status_extended == expected_message
assert result[0].resource_id == "e-abc123"
assert result[0].resource_arn == ENV_ARN
assert result[0].region == AWS_REGION_EU_WEST_1
def test_platform_arn_fallback(self):
environments = {
ENV_ARN: _environment(
platform_arn=f"arn:aws:elasticbeanstalk:{AWS_REGION_EU_WEST_1}::platform/PHP 8.3 running on 64bit Amazon Linux 2023/4.13.13"
)
}
result = _run_check(environments)
assert len(result) == 1
assert result[0].status == "PASS"
def test_custom_minimums(self):
environments = {
ENV_ARN: _environment("64bit Amazon Linux 2023 v4.13.13 running PHP 8.3")
}
result = _run_check(
environments,
{
"elasticbeanstalk_php_min_branch": "8.4",
"elasticbeanstalk_php_min_platform_version": "4.13.13",
},
)
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Elastic Beanstalk environment test-env runs PHP 8.3, a branch that no longer receives security fixes (minimum supported branch is 8.4)."
)
@@ -7,7 +7,11 @@ from moto import mock_aws
from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import (
ElasticBeanstalk,
)
from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_EU_WEST_1,
set_mocked_aws_provider,
)
make_api_call = botocore.client.BaseClient._make_api_call
@@ -120,6 +124,44 @@ class Test_ElasticBeanstalk_Service:
== "test-app"
)
@mock_aws
def test_describe_environments_platform(self):
environment_arn = f"arn:aws:elasticbeanstalk:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:environment/test-app/test-env"
def mock_describe_environments(self, operation_name, kwarg):
if operation_name == "DescribeEnvironments":
return {
"Environments": [
{
"EnvironmentId": "e-abc123",
"EnvironmentArn": environment_arn,
"ApplicationName": "test-app",
"EnvironmentName": "test-env",
"SolutionStackName": "64bit Amazon Linux 2023 v4.13.13 running PHP 8.4",
"PlatformArn": f"arn:aws:elasticbeanstalk:{AWS_REGION_EU_WEST_1}::platform/PHP 8.4 running on 64bit Amazon Linux 2023/4.13.13",
}
]
}
return make_api_call(self, operation_name, kwarg)
with patch(
"botocore.client.BaseClient._make_api_call",
new=mock_describe_environments,
):
elasticbeanstalk = ElasticBeanstalk(
set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
)
environment = elasticbeanstalk.environments[environment_arn]
assert (
environment.solution_stack_name
== "64bit Amazon Linux 2023 v4.13.13 running PHP 8.4"
)
assert (
environment.platform_arn
== f"arn:aws:elasticbeanstalk:{AWS_REGION_EU_WEST_1}::platform/PHP 8.4 running on 64bit Amazon Linux 2023/4.13.13"
)
# Test _describe_configuration_settings
@mock_aws
def test_describe_configuration_settings(self):
@@ -0,0 +1,89 @@
from unittest.mock import MagicMock, patch
import pytest
from prowler.providers.aws.services.lightsail.lightsail_service import Instance
from tests.providers.aws.utils import (
AWS_REGION_US_EAST_1,
AWS_REGION_US_EAST_1_AZA,
BASE_LIGHTSAIL_ARN,
set_mocked_aws_provider,
)
CHECK_PATH = "prowler.providers.aws.services.lightsail.lightsail_instance_php_blueprint_updated.lightsail_instance_php_blueprint_updated"
INSTANCE_ARN = f"{BASE_LIGHTSAIL_ARN}:Instance/test-instance"
def _instance(blueprint_id, blueprint_name=""):
return Instance(
name="test-instance",
id="1234/5678",
arn=INSTANCE_ARN,
tags=[],
region=AWS_REGION_US_EAST_1,
availability_zone=AWS_REGION_US_EAST_1_AZA,
static_ip=False,
public_ip="",
private_ip="172.16.2.3",
ipv6_addresses=[],
ip_address_type="ipv4",
ports=[],
auto_snapshot=False,
blueprint_id=blueprint_id,
blueprint_name=blueprint_name,
)
def _run_check(instances, audit_config=None):
lightsail_client = MagicMock
lightsail_client.instances = instances
lightsail_client.audit_config = audit_config or {}
with (
patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider([AWS_REGION_US_EAST_1]),
),
patch(
"prowler.providers.aws.services.lightsail.lightsail_service.Lightsail",
new=lightsail_client,
),
patch(f"{CHECK_PATH}.lightsail_client", new=lightsail_client),
):
from prowler.providers.aws.services.lightsail.lightsail_instance_php_blueprint_updated.lightsail_instance_php_blueprint_updated import (
lightsail_instance_php_blueprint_updated,
)
return lightsail_instance_php_blueprint_updated().execute()
class Test_lightsail_instance_php_blueprint_updated:
def test_no_instances(self):
assert len(_run_check({})) == 0
@pytest.mark.parametrize("blueprint_id", ["ubuntu_22_04", "nodejs", "mean", ""])
def test_non_php_blueprint_is_skipped(self, blueprint_id):
assert len(_run_check({INSTANCE_ARN: _instance(blueprint_id)})) == 0
@pytest.mark.parametrize(
"blueprint_id",
["wordpress", "wordpress_multisite", "lamp_8_bitnami", "nginx", "drupal"],
)
def test_php_blueprint_is_manual(self, blueprint_id):
result = _run_check({INSTANCE_ARN: _instance(blueprint_id, "WordPress")})
assert len(result) == 1
assert result[0].status == "MANUAL"
assert (
result[0].status_extended
== f"Lightsail instance test-instance was created from the PHP blueprint '{blueprint_id}'; the PHP runtime cannot be inspected through the API, verify over SSH that it runs a fixed release (8.2.35, 8.3.36, 8.4.27, 8.5.12 or newer); Bitnami-packaged blueprints bundle a PHP copy that apt never updates, so migrate them to the Lightsail-packaged blueprint or replace the bundled PHP with a package-managed one."
)
assert result[0].resource_id == "1234/5678"
assert result[0].resource_arn == INSTANCE_ARN
assert result[0].region == AWS_REGION_US_EAST_1
def test_custom_blueprint_list(self):
instances = {INSTANCE_ARN: _instance("custom_php_app")}
assert len(_run_check(instances)) == 0
result = _run_check(instances, {"lightsail_php_blueprints": ["custom_php"]})
assert len(result) == 1
assert result[0].status == "MANUAL"
@@ -202,6 +202,14 @@ class TestLightsailService:
== f"{BASE_LIGHTSAIL_ARN}:Instance/test-id"
)
assert lightsail.instances[f"{BASE_LIGHTSAIL_ARN}:Instance/test-id"].tags == []
assert (
lightsail.instances[f"{BASE_LIGHTSAIL_ARN}:Instance/test-id"].blueprint_id
== "wordpress"
)
assert (
lightsail.instances[f"{BASE_LIGHTSAIL_ARN}:Instance/test-id"].blueprint_name
== "WordPress"
)
assert (
lightsail.instances[f"{BASE_LIGHTSAIL_ARN}:Instance/test-id"].region
== AWS_REGION_US_EAST_1
@@ -207,7 +207,24 @@ class Test_WAFv2_Service:
"CloudWatchMetricsEnabled": True,
"MetricName": "web-acl-test-metric",
},
}
},
{
"Name": "AWS-AWSManagedRulesPHPRuleSet",
"Priority": 2,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesPHPRuleSet",
"Version": "Version_2.3",
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": True,
"CloudWatchMetricsEnabled": True,
"MetricName": "php-rule-set",
},
},
],
VisibilityConfig={
"SampledRequestsEnabled": False,
@@ -221,6 +238,11 @@ class Test_WAFv2_Service:
aws = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
wafv2 = WAFv2(aws)
assert len(wafv2.web_acls) == 1
assert len(wafv2.web_acls[waf_arn].rules) == 1
assert len(wafv2.web_acls[waf_arn].rules) == 2
assert wafv2.web_acls[waf_arn].rules[0].name == "rule-on"
assert wafv2.web_acls[waf_arn].rules[0].cloudwatch_metrics_enabled
assert wafv2.web_acls[waf_arn].rules[0].managed_rule_group is None
managed = wafv2.web_acls[waf_arn].rules[1].managed_rule_group
assert managed.vendor_name == "AWS"
assert managed.name == "AWSManagedRulesPHPRuleSet"
assert managed.version == "Version_2.3"
@@ -0,0 +1,89 @@
from unittest import mock
from prowler.providers.aws.services.wafv2.wafv2_service import (
ManagedRuleGroup,
Rule,
WebAclv2,
)
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)
CHECK_PATH = "prowler.providers.aws.services.wafv2.wafv2_webacl_managed_rule_groups_enabled.wafv2_webacl_managed_rule_groups_enabled"
WEB_ACL_ARN = f"arn:aws:wafv2:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:regional/webacl/test-acl/abc"
def _web_acl(rules):
return WebAclv2(
arn=WEB_ACL_ARN,
name="test-acl",
id="abc",
albs=[],
user_pools=[],
region=AWS_REGION_US_EAST_1,
tags=[],
rules=rules,
)
def _run_check(web_acls):
wafv2_client = mock.MagicMock()
wafv2_client.web_acls = web_acls
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider([AWS_REGION_US_EAST_1]),
),
mock.patch(f"{CHECK_PATH}.wafv2_client", new=wafv2_client),
):
from prowler.providers.aws.services.wafv2.wafv2_webacl_managed_rule_groups_enabled.wafv2_webacl_managed_rule_groups_enabled import (
wafv2_webacl_managed_rule_groups_enabled,
)
return wafv2_webacl_managed_rule_groups_enabled().execute()
class Test_wafv2_webacl_managed_rule_groups_enabled:
def test_no_web_acls(self):
assert len(_run_check({})) == 0
def test_no_managed_rule_groups(self):
result = _run_check({WEB_ACL_ARN: _web_acl([Rule(name="custom-rule")])})
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "AWS WAFv2 Web ACL test-acl does not use any managed rule group."
)
assert result[0].resource_id == "abc"
assert result[0].resource_arn == WEB_ACL_ARN
assert result[0].region == AWS_REGION_US_EAST_1
def test_managed_rule_groups(self):
rules = [
Rule(name="custom-rule"),
Rule(
name="AWS-AWSManagedRulesCommonRuleSet",
managed_rule_group=ManagedRuleGroup(
vendor_name="AWS", name="AWSManagedRulesCommonRuleSet"
),
),
Rule(
name="AWS-AWSManagedRulesPHPRuleSet",
managed_rule_group=ManagedRuleGroup(
vendor_name="AWS",
name="AWSManagedRulesPHPRuleSet",
version="Version_2.3",
),
),
]
result = _run_check({WEB_ACL_ARN: _web_acl(rules)})
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "AWS WAFv2 Web ACL test-acl uses managed rule groups: AWS/AWSManagedRulesCommonRuleSet, AWS/AWSManagedRulesPHPRuleSet."
)
@@ -0,0 +1,54 @@
import pytest
from prowler.providers.azure.lib.runtime.runtime_version import (
parse_java_version,
parse_runtime_version,
runtime_version_at_least,
)
class Test_runtime_version_helpers:
@pytest.mark.parametrize(
"value,expected",
[
("PHP|8.3", (8, 3)),
("php|8.0", (8, 0)),
("PYTHON|3.12", (3, 12)),
("8.2.35", (8, 2, 35)),
("", ()),
(None, ()),
("PHP|latest", ()),
],
)
def test_parse_runtime_version(self, value, expected):
assert parse_runtime_version(value) == expected
@pytest.mark.parametrize(
"value,expected",
[
("JAVA|17-java17", (17,)),
("TOMCAT|10.0-java11", (11,)),
("JBOSSEAP|7-java8", (8,)),
("JAVA|8-jre8", (8,)),
("java1.8", (8,)),
("17", (17,)),
("", ()),
],
)
def test_parse_java_version(self, value, expected):
assert parse_java_version(value) == expected
@pytest.mark.parametrize(
"current,minimum,expected",
[
((8, 3), (8, 2), True),
((8, 2), (8, 2), True),
((8, 1), (8, 2), False),
((8, 2), (8, 2, 35), True),
((9,), (8, 2), True),
((), (8, 2), False),
((8, 2), (), False),
],
)
def test_runtime_version_at_least(self, current, minimum, expected):
assert runtime_version_at_least(current, minimum) is expected
@@ -1,6 +1,8 @@
from unittest import mock
from uuid import uuid4
import pytest
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_DISPLAY,
AZURE_SUBSCRIPTION_ID,
@@ -8,333 +10,165 @@ from tests.providers.azure.azure_fixtures import (
set_mocked_azure_provider,
)
CHECK_PATH = "prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest"
def _run_check(app_client):
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(f"{CHECK_PATH}.app_client", new=app_client),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
return app_ensure_java_version_is_latest().execute()
def _app_client(configurations, audit_config=None):
# app_service imports monitor_client, which needs the provider patched at import time
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
):
from prowler.providers.azure.services.app.app_service import WebApp
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = audit_config or {}
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=configurations,
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
return app_client, resource_id
class Test_app_ensure_java_version_is_latest:
def test_app_no_subscriptions(self):
app_client = mock.MagicMock
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.apps = {}
assert len(_run_check(app_client)) == 0
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest.app_client",
new=app_client,
),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
check = app_ensure_java_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_app_subscriptions_empty(self):
app_client = mock.MagicMock
def test_app_subscription_empty(self):
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.apps = {AZURE_SUBSCRIPTION_ID: {}}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest.app_client",
new=app_client,
),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
check = app_ensure_java_version_is_latest()
result = check.execute()
assert len(result) == 0
assert len(_run_check(app_client)) == 0
def test_app_configurations_none(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client, _ = _app_client(None)
assert len(_run_check(app_client)) == 0
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
def test_app_linux_fx_version_none_is_skipped(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version=None, java_version=None)
)
assert len(_run_check(app_client)) == 0
def test_app_linux_fx_version_none_uses_windows_version(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version=None, java_version="17")
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == "PASS"
def test_app_other_runtime_is_skipped(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version="php|8.0", java_version=None)
)
assert len(_run_check(app_client)) == 0
@pytest.mark.parametrize(
"configurations,minimum,expected_status,expected_message",
[
(
{"linux_fx_version": "Tomcat|9.0-java11", "java_version": None},
"17",
"FAIL",
"Java version is set to 'Tomcat|9.0-java11', which is below the minimum supported version 'java 17',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "Tomcat|9.0-java17", "java_version": None},
"17",
"PASS",
"Java version is set to 'Tomcat|9.0-java17', which meets the minimum supported version 'java 17',",
),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=None,
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_java_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_app_linux_java_version_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"java_latest_version": "17"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
(
{"linux_fx_version": "JAVA|21-java21", "java_version": None},
"17",
"PASS",
"Java version is set to 'JAVA|21-java21', which meets the minimum supported version 'java 17',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "JAVA|8-jre8", "java_version": None},
"17",
"FAIL",
"Java version is set to 'JAVA|8-jre8', which is below the minimum supported version 'java 17',",
),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(
linux_fx_version="Tomcat|9.0-java17", java_version=None
),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_java_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Java version is set to 'java 17' for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
def test_app_linux_java_version_not_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"java_latest_version": "17"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
(
{"linux_fx_version": "JBOSSEAP|7-java8", "java_version": None},
"11",
"FAIL",
"Java version is set to 'JBOSSEAP|7-java8', which is below the minimum supported version 'java 11',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "", "java_version": "11"},
"17",
"FAIL",
"Java version is set to 'java11', which is below the minimum supported version 'java 17',",
),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(
linux_fx_version="Tomcat|9.0-java11", java_version=None
),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_java_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Java version is set to 'Tomcat|9.0-java11', but should be set to 'java 17' for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
def test_app_windows_java_version_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"java_latest_version": "17"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
(
{"linux_fx_version": "", "java_version": "17"},
"17",
"PASS",
"Java version is set to 'java17', which meets the minimum supported version 'java 17',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "", "java_version": "1.8"},
"17",
"FAIL",
"Java version is set to 'java1.8', which is below the minimum supported version 'java 17',",
),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
],
)
def test_app_java_version(
self, configurations, minimum, expected_status, expected_message
):
app_client, resource_id = _app_client(
mock.MagicMock(**configurations), {"java_latest_version": minimum}
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == expected_status
assert (
result[0].status_extended
== f"{expected_message} for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(
linux_fx_version="", java_version="17"
),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_java_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Java version is set to 'java 17' for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
def test_app_windows_java_version_not_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"java_latest_version": "17"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest.app_client",
new=app_client,
),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(
linux_fx_version="", java_version="11"
),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_java_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Java version is set to 'java11', but should be set to 'java 17' for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
def test_app_linux_php_version_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"java_latest_version": "17"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest.app_client",
new=app_client,
),
):
from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import (
app_ensure_java_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(
linux_fx_version="php|8.0", java_version=None
),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_java_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_app_default_minimum(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version="JAVA|21-java21", java_version=None)
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == "PASS"
@@ -1,6 +1,8 @@
from unittest import mock
from uuid import uuid4
import pytest
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_DISPLAY,
AZURE_SUBSCRIPTION_ID,
@@ -8,186 +10,177 @@ from tests.providers.azure.azure_fixtures import (
set_mocked_azure_provider,
)
CHECK_PATH = "prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest"
def _run_check(app_client):
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(f"{CHECK_PATH}.app_client", new=app_client),
):
from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import (
app_ensure_php_version_is_latest,
)
return app_ensure_php_version_is_latest().execute()
def _app_client(configurations, audit_config=None):
# app_service imports monitor_client, which needs the provider patched at import time
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
):
from prowler.providers.azure.services.app.app_service import WebApp
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = audit_config or {}
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=configurations,
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
return app_client, resource_id
class Test_app_ensure_php_version_is_latest:
def test_app_no_subscriptions(self):
app_client = mock.MagicMock
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.apps = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest.app_client",
new=app_client,
),
):
from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import (
app_ensure_php_version_is_latest,
)
check = app_ensure_php_version_is_latest()
result = check.execute()
assert len(result) == 0
assert len(_run_check(app_client)) == 0
def test_app_subscription_empty(self):
app_client = mock.MagicMock
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.apps = {AZURE_SUBSCRIPTION_ID: {}}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest.app_client",
new=app_client,
),
):
from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import (
app_ensure_php_version_is_latest,
)
check = app_ensure_php_version_is_latest()
result = check.execute()
assert len(result) == 0
assert len(_run_check(app_client)) == 0
def test_app_configurations_none(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client, _ = _app_client(None)
assert len(_run_check(app_client)) == 0
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
def test_app_linux_fx_version_none_is_skipped(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version=None, php_version="")
)
assert len(_run_check(app_client)) == 0
def test_app_linux_fx_version_none_uses_windows_version(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version=None, php_version="8.5")
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == "PASS"
def test_app_other_runtime_is_skipped(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version="PYTHON|3.12", php_version="")
)
assert len(_run_check(app_client)) == 0
@pytest.mark.parametrize(
"configurations,minimum,expected_status,expected_message",
[
(
{"linux_fx_version": "php|8.0", "php_version": ""},
"8.2",
"FAIL",
"PHP version is set to 'php|8.0', which is below the minimum supported version '8.2',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "PHP|8.1", "php_version": ""},
"8.2",
"FAIL",
"PHP version is set to 'PHP|8.1', which is below the minimum supported version '8.2',",
),
):
from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import (
app_ensure_php_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=None,
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_php_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_app_php_version_not_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"php_latest_version": "8.2"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
(
{"linux_fx_version": "PHP|8.2", "php_version": ""},
"8.2",
"PASS",
"PHP version is set to 'PHP|8.2', which meets the minimum supported version '8.2',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "PHP|8.3", "php_version": ""},
"8.2",
"PASS",
"PHP version is set to 'PHP|8.3', which meets the minimum supported version '8.2',",
),
):
from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import (
app_ensure_php_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(linux_fx_version="php|8.0"),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_php_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"PHP version is set to 'php|8.0', the latest version that you could use is the '8.2' version, for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
def test_app_php_version_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"php_latest_version": "8.2"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
(
{"linux_fx_version": "PHP|8.5", "php_version": ""},
"8.2",
"PASS",
"PHP version is set to 'PHP|8.5', which meets the minimum supported version '8.2',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "PHP|8.3", "php_version": ""},
"8.4",
"FAIL",
"PHP version is set to 'PHP|8.3', which is below the minimum supported version '8.4',",
),
):
from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import (
app_ensure_php_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
(
{"linux_fx_version": "PHP|8.2", "php_version": ""},
"8.2.35",
"PASS",
"PHP version is set to 'PHP|8.2', which meets the minimum supported version '8.2.35',",
),
(
{"linux_fx_version": "", "php_version": "7.4"},
"8.2",
"FAIL",
"PHP version is set to '7.4', which is below the minimum supported version '8.2',",
),
(
{"linux_fx_version": "", "php_version": "8.2"},
"8.2",
"PASS",
"PHP version is set to '8.2', which meets the minimum supported version '8.2',",
),
(
{"linux_fx_version": "PHP|latest", "php_version": ""},
"8.2",
"FAIL",
"PHP version is set to 'PHP|latest', which is below the minimum supported version '8.2',",
),
],
)
def test_app_php_version(
self, configurations, minimum, expected_status, expected_message
):
app_client, resource_id = _app_client(
mock.MagicMock(**configurations), {"php_latest_version": minimum}
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == expected_status
assert (
result[0].status_extended
== f"{expected_message} for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(linux_fx_version="php|8.2"),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_php_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"PHP version is set to '8.2' for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
def test_app_default_minimum(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version="PHP|8.5", php_version="")
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == "PASS"
@@ -1,6 +1,8 @@
from unittest import mock
from uuid import uuid4
import pytest
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_DISPLAY,
AZURE_SUBSCRIPTION_ID,
@@ -8,185 +10,153 @@ from tests.providers.azure.azure_fixtures import (
set_mocked_azure_provider,
)
CHECK_PATH = "prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest"
def _run_check(app_client):
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(f"{CHECK_PATH}.app_client", new=app_client),
):
from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import (
app_ensure_python_version_is_latest,
)
return app_ensure_python_version_is_latest().execute()
def _app_client(configurations, audit_config=None):
# app_service imports monitor_client, which needs the provider patched at import time
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
):
from prowler.providers.azure.services.app.app_service import WebApp
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = audit_config or {}
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=configurations,
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
return app_client, resource_id
class Test_app_ensure_python_version_is_latest:
def test_app_no_subscriptions(self):
app_client = mock.MagicMock
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.apps = {}
assert len(_run_check(app_client)) == 0
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest.app_client",
new=app_client,
),
):
from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import (
app_ensure_python_version_is_latest,
)
check = app_ensure_python_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_app_subscriptions_empty(self):
app_client = mock.MagicMock
def test_app_subscription_empty(self):
app_client = mock.MagicMock()
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.apps = {AZURE_SUBSCRIPTION_ID: {}}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest.app_client",
new=app_client,
),
):
from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import (
app_ensure_python_version_is_latest,
)
check = app_ensure_python_version_is_latest()
result = check.execute()
assert len(result) == 0
assert len(_run_check(app_client)) == 0
def test_app_configurations_none(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
app_client, _ = _app_client(None)
assert len(_run_check(app_client)) == 0
def test_app_linux_fx_version_none_is_skipped(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version=None, python_version="")
)
assert len(_run_check(app_client)) == 0
def test_app_linux_fx_version_none_uses_windows_version(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version=None, python_version="3.12")
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == "PASS"
def test_app_other_runtime_is_skipped(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version="PHP|8.2", python_version="")
)
assert len(_run_check(app_client)) == 0
@pytest.mark.parametrize(
"configurations,minimum,expected_status,expected_message",
[
(
{"linux_fx_version": "python|3.10", "python_version": ""},
"3.12",
"FAIL",
"Python version is set to 'python|3.10', which is below the minimum supported version '3.12',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "PYTHON|3.12", "python_version": ""},
"3.12",
"PASS",
"Python version is set to 'PYTHON|3.12', which meets the minimum supported version '3.12',",
),
):
from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import (
app_ensure_python_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=None,
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_python_version_is_latest()
result = check.execute()
assert len(result) == 0
def test_app_python_version_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"python_latest_version": "3.12"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
(
{"linux_fx_version": "PYTHON|3.13", "python_version": ""},
"3.12",
"PASS",
"Python version is set to 'PYTHON|3.13', which meets the minimum supported version '3.12',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "PYTHON|3.9", "python_version": ""},
"3.10",
"FAIL",
"Python version is set to 'PYTHON|3.9', which is below the minimum supported version '3.10',",
),
):
from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import (
app_ensure_python_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(linux_fx_version="python|3.12"),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_python_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Python version is set to '3.12' for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
def test_app_python_version_not_latest(self):
resource_id = f"/subscriptions/{uuid4()}"
app_client = mock.MagicMock
app_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
app_client.audit_config = {"python_latest_version": "3.12"}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
(
{"linux_fx_version": "", "python_version": "3.4"},
"3.12",
"FAIL",
"Python version is set to '3.4', which is below the minimum supported version '3.12',",
),
mock.patch(
"prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest.app_client",
new=app_client,
(
{"linux_fx_version": "", "python_version": "3.12"},
"3.12",
"PASS",
"Python version is set to '3.12', which meets the minimum supported version '3.12',",
),
):
from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import (
app_ensure_python_version_is_latest,
)
from prowler.providers.azure.services.app.app_service import WebApp
],
)
def test_app_python_version(
self, configurations, minimum, expected_status, expected_message
):
app_client, resource_id = _app_client(
mock.MagicMock(**configurations), {"python_latest_version": minimum}
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == expected_status
assert (
result[0].status_extended
== f"{expected_message} for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
app_client.apps = {
AZURE_SUBSCRIPTION_ID: {
resource_id: WebApp(
resource_id=resource_id,
name="app_id-1",
auth_enabled=True,
configurations=mock.MagicMock(linux_fx_version="python|3.10"),
client_cert_mode="Ignore",
https_only=False,
identity=None,
location="West Europe",
)
}
}
check = app_ensure_python_version_is_latest()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Python version is 'python|3.10', the latest version that you could use is the '3.12' version, for app 'app_id-1' in subscription '{AZURE_SUBSCRIPTION_DISPLAY}'."
)
assert result[0].resource_id == resource_id
assert result[0].resource_name == "app_id-1"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].location == "West Europe"
def test_app_default_minimum(self):
app_client, _ = _app_client(
mock.MagicMock(linux_fx_version="PYTHON|3.13", python_version="")
)
result = _run_check(app_client)
assert len(result) == 1
assert result[0].status == "PASS"
+1
View File
@@ -93,6 +93,7 @@ class TestGCPProvider:
"max_unused_account_days": 180,
"storage_min_retention_days": 90,
"secretmanager_max_rotation_days": 90,
"appengine_php_min_version": "8.2",
"mig_min_zones": 2,
"max_snapshot_age_days": 90,
}
@@ -0,0 +1,90 @@
from unittest.mock import MagicMock, patch
from googleapiclient.errors import HttpError
from prowler.providers.gcp.services.appengine.appengine_service import AppEngine
from tests.providers.gcp.gcp_fixtures import (
GCP_PROJECT_ID,
GCP_US_CENTER1_LOCATION,
mock_is_api_active,
set_mocked_gcp_provider,
)
def _make_appengine_client(versions, app_error=None):
client = MagicMock()
apps = client.apps()
if app_error:
apps.get().execute.side_effect = app_error
else:
apps.get().execute.return_value = {
"id": GCP_PROJECT_ID,
"locationId": GCP_US_CENTER1_LOCATION,
}
apps.services().list().execute.return_value = {"services": [{"id": "default"}]}
apps.services().list_next.return_value = None
apps.services().versions().list().execute.return_value = {"versions": versions}
apps.services().versions().list_next.return_value = None
return client
def _patched_service(client):
return (
patch(
"prowler.providers.gcp.lib.service.service.GCPService.__is_api_active__",
new=mock_is_api_active,
),
patch(
"prowler.providers.gcp.lib.service.service.GCPService.__generate_client__",
new=lambda *_, **__: client,
),
)
class TestAppEngineService:
def test_get_versions(self):
client = _make_appengine_client(
[
{
"name": f"apps/{GCP_PROJECT_ID}/services/default/versions/v1",
"id": "v1",
"runtime": "php82",
"env": "standard",
"servingStatus": "SERVING",
},
{
"id": "v2",
"runtime": "php",
"env": "flexible",
"servingStatus": "STOPPED",
},
]
)
api_active, generate_client = _patched_service(client)
with api_active, generate_client:
appengine = AppEngine(set_mocked_gcp_provider(project_ids=[GCP_PROJECT_ID]))
assert len(appengine.versions) == 2
first = appengine.versions[0]
assert first.id == f"apps/{GCP_PROJECT_ID}/services/default/versions/v1"
assert first.name == "default/v1"
assert first.version_id == "v1"
assert first.service == "default"
assert first.project_id == GCP_PROJECT_ID
assert first.location == GCP_US_CENTER1_LOCATION
assert first.runtime == "php82"
assert first.env == "standard"
assert first.serving_status == "SERVING"
second = appengine.versions[1]
assert second.id == f"apps/{GCP_PROJECT_ID}/services/default/versions/v2"
assert second.env == "flexible"
assert second.serving_status == "STOPPED"
def test_project_without_application(self):
response = MagicMock(status=404)
client = _make_appengine_client([], app_error=HttpError(response, b"not found"))
api_active, generate_client = _patched_service(client)
with api_active, generate_client:
appengine = AppEngine(set_mocked_gcp_provider(project_ids=[GCP_PROJECT_ID]))
assert appengine.versions == []
@@ -0,0 +1,115 @@
from unittest import mock
import pytest
from tests.providers.gcp.gcp_fixtures import (
GCP_PROJECT_ID,
GCP_US_CENTER1_LOCATION,
set_mocked_gcp_provider,
)
CHECK_PATH = "prowler.providers.gcp.services.appengine.appengine_version_php_runtime_supported.appengine_version_php_runtime_supported"
def _version(runtime, env="standard", serving_status="SERVING"):
from prowler.providers.gcp.services.appengine.appengine_service import Version
return Version(
id=f"apps/{GCP_PROJECT_ID}/services/default/versions/v1",
name="default/v1",
version_id="v1",
service="default",
project_id=GCP_PROJECT_ID,
location=GCP_US_CENTER1_LOCATION,
runtime=runtime,
env=env,
serving_status=serving_status,
)
def _run_check(versions, audit_config=None):
appengine_client = mock.MagicMock()
appengine_client.versions = versions
appengine_client.audit_config = audit_config or {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_gcp_provider(),
),
mock.patch(f"{CHECK_PATH}.appengine_client", new=appengine_client),
):
from prowler.providers.gcp.services.appengine.appengine_version_php_runtime_supported.appengine_version_php_runtime_supported import (
appengine_version_php_runtime_supported,
)
return appengine_version_php_runtime_supported().execute()
class Test_appengine_version_php_runtime_supported:
def test_no_versions(self):
assert len(_run_check([])) == 0
@pytest.mark.parametrize(
"runtime,env,serving_status",
[
("python312", "standard", "SERVING"),
("nodejs20", "standard", "SERVING"),
("php82", "standard", "STOPPED"),
("custom", "flexible", "SERVING"),
],
)
def test_skipped_versions(self, runtime, env, serving_status):
assert len(_run_check([_version(runtime, env, serving_status)])) == 0
@pytest.mark.parametrize(
"runtime,expected_status,expected_message",
[
(
"php82",
"PASS",
"App Engine version default/v1 runs PHP 8.2 ('php82'), a supported runtime that receives security fixes automatically.",
),
(
"php85",
"PASS",
"App Engine version default/v1 runs PHP 8.5 ('php85'), a supported runtime that receives security fixes automatically.",
),
(
"php81",
"FAIL",
"App Engine version default/v1 runs PHP 8.1 ('php81'), a runtime that no longer receives security fixes (minimum supported version is 8.2).",
),
(
"php55",
"FAIL",
"App Engine version default/v1 runs PHP 5.5 ('php55'), a runtime that no longer receives security fixes (minimum supported version is 8.2).",
),
],
)
def test_standard_runtime(self, runtime, expected_status, expected_message):
result = _run_check([_version(runtime)])
assert len(result) == 1
assert result[0].status == expected_status
assert result[0].status_extended == expected_message
assert (
result[0].resource_id
== f"apps/{GCP_PROJECT_ID}/services/default/versions/v1"
)
assert result[0].resource_name == "default/v1"
assert result[0].project_id == GCP_PROJECT_ID
assert result[0].location == GCP_US_CENTER1_LOCATION
def test_flexible_runtime_is_manual(self):
result = _run_check([_version("php", env="flexible")])
assert len(result) == 1
assert result[0].status == "MANUAL"
assert (
result[0].status_extended
== "App Engine version default/v1 runs on the flexible environment with runtime 'php', which is not patched in place; redeploy it on a fixed PHP release (8.2 or newer) and verify the image."
)
def test_custom_minimum(self):
result = _run_check([_version("php83")], {"appengine_php_min_version": "8.4"})
assert len(result) == 1
assert result[0].status == "FAIL"