diff --git a/docs/user-guide/providers/azure/authentication.mdx b/docs/user-guide/providers/azure/authentication.mdx index 852e79d115..08bc79d8ff 100644 --- a/docs/user-guide/providers/azure/authentication.mdx +++ b/docs/user-guide/providers/azure/authentication.mdx @@ -210,7 +210,9 @@ For more detailed guidance on subscription management and permissions: The following security checks require the `ProwlerRole` permissions for execution. Ensure the role is assigned to the identity assumed by Prowler before running these checks: - `app_function_access_keys_configured` +- `app_function_application_insights_enabled` - `app_function_ftps_deployment_disabled` +- `app_function_latest_runtime_version` --- diff --git a/prowler/changelog.d/azure-function-app-permission-warnings.fixed.md b/prowler/changelog.d/azure-function-app-permission-warnings.fixed.md new file mode 100644 index 0000000000..f455f1ee19 --- /dev/null +++ b/prowler/changelog.d/azure-function-app-permission-warnings.fixed.md @@ -0,0 +1 @@ +Azure Function App optional permission failures now log as warnings, and Function App environment variable fields use the correct spelling internally diff --git a/prowler/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled.py b/prowler/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled.py index 6fec5e7042..a903097beb 100644 --- a/prowler/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled.py +++ b/prowler/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled.py @@ -14,7 +14,7 @@ class app_function_application_insights_enabled(Check): subscription_id, subscription_id ) for function in functions.values(): - if function.enviroment_variables is not None: + if function.environment_variables is not None: report = Check_Report_Azure( metadata=self.metadata(), resource=function ) @@ -22,9 +22,9 @@ class app_function_application_insights_enabled(Check): report.status = "FAIL" report.status_extended = f"Function {function.name} from subscription {subscription_name} ({subscription_id}) is not using Application Insights." - if function.enviroment_variables.get( + if function.environment_variables.get( "APPINSIGHTS_INSTRUMENTATIONKEY", None - ) or function.enviroment_variables.get( + ) or function.environment_variables.get( "APPLICATIONINSIGHTS_CONNECTION_STRING", None ): report.status = "PASS" diff --git a/prowler/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version.py b/prowler/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version.py index 828362a8fe..694c5bbdc6 100644 --- a/prowler/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version.py +++ b/prowler/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version.py @@ -14,7 +14,7 @@ class app_function_latest_runtime_version(Check): subscription_id, subscription_id ) for function in functions.values(): - if function.enviroment_variables is not None: + if function.environment_variables is not None: report = Check_Report_Azure( metadata=self.metadata(), resource=function ) @@ -23,13 +23,13 @@ class app_function_latest_runtime_version(Check): report.status_extended = f"Function {function.name} from subscription {subscription_name} ({subscription_id}) is using the latest runtime." if ( - function.enviroment_variables.get( + function.environment_variables.get( "FUNCTIONS_EXTENSION_VERSION", "" ) != "~4" ): report.status = "FAIL" - report.status_extended = f"Function {function.name} from subscription {subscription_name} ({subscription_id}) is not using the latest runtime. The current runtime is '{function.enviroment_variables.get('FUNCTIONS_EXTENSION_VERSION', '')}' and should be '~4'." + report.status_extended = f"Function {function.name} from subscription {subscription_name} ({subscription_id}) is not using the latest runtime. The current runtime is '{function.environment_variables.get('FUNCTIONS_EXTENSION_VERSION', '')}' and should be '~4'." findings.append(report) diff --git a/prowler/providers/azure/services/app/app_service.py b/prowler/providers/azure/services/app/app_service.py index 83d23be516..5b090bf1f2 100644 --- a/prowler/providers/azure/services/app/app_service.py +++ b/prowler/providers/azure/services/app/app_service.py @@ -158,7 +158,7 @@ class App(AzureService): location=function.location, kind=function.kind, function_keys=function_keys, - enviroment_variables=getattr( + environment_variables=getattr( application_settings, "properties", None ), identity=getattr(function, "identity", None), @@ -225,7 +225,7 @@ class App(AzureService): name=name, ) except Exception as error: - logger.error( + logger.warning( f"Error getting host keys for {name} in {resource_group}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) return None @@ -249,7 +249,7 @@ class App(AzureService): name=name, ) except Exception as error: - logger.error( + logger.warning( f"Error getting application settings for {name} in {resource_group}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) return None @@ -296,7 +296,7 @@ class FunctionApp: location: str kind: str function_keys: Optional[Dict[str, str]] - enviroment_variables: Optional[Dict[str, str]] + environment_variables: Optional[Dict[str, str]] identity: ManagedServiceIdentity public_access: bool vnet_subnet_id: str diff --git a/tests/providers/azure/services/app/app_function_access_keys_configured/app_function_access_keys_configured_test.py b/tests/providers/azure/services/app/app_function_access_keys_configured/app_function_access_keys_configured_test.py index 770ca07b2b..803ba7bd75 100644 --- a/tests/providers/azure/services/app/app_function_access_keys_configured/app_function_access_keys_configured_test.py +++ b/tests/providers/azure/services/app/app_function_access_keys_configured/app_function_access_keys_configured_test.py @@ -87,7 +87,7 @@ class Test_app_function_access_keys_configured: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=None, public_access=False, vnet_subnet_id=None, @@ -143,7 +143,7 @@ class Test_app_function_access_keys_configured: "default": "key1", "key2": "key2", }, - enviroment_variables={}, + environment_variables={}, identity=None, public_access=False, vnet_subnet_id=None, diff --git a/tests/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled_test.py b/tests/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled_test.py index 4a55b1e108..b113d96e50 100644 --- a/tests/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled_test.py +++ b/tests/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled_test.py @@ -87,7 +87,7 @@ class Test_app_function_application_insights_enabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=None, public_access=False, vnet_subnet_id=None, @@ -138,7 +138,9 @@ class Test_app_function_application_insights_enabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={"APPINSIGHTS_INSTRUMENTATIONKEY": "1234"}, + environment_variables={ + "APPINSIGHTS_INSTRUMENTATIONKEY": "1234" + }, identity=None, public_access=False, vnet_subnet_id=None, @@ -189,7 +191,9 @@ class Test_app_function_application_insights_enabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={"APPINSIGHTS_INSTRUMENTATIONKEY": "1234"}, + environment_variables={ + "APPINSIGHTS_INSTRUMENTATIONKEY": "1234" + }, identity=None, public_access=False, vnet_subnet_id=None, @@ -240,7 +244,7 @@ class Test_app_function_application_insights_enabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=None, public_access=False, vnet_subnet_id=None, diff --git a/tests/providers/azure/services/app/app_function_ftps_deployment_disabled/app_function_ftps_deployment_disabled_test.py b/tests/providers/azure/services/app/app_function_ftps_deployment_disabled/app_function_ftps_deployment_disabled_test.py index b08c712da1..fb20317347 100644 --- a/tests/providers/azure/services/app/app_function_ftps_deployment_disabled/app_function_ftps_deployment_disabled_test.py +++ b/tests/providers/azure/services/app/app_function_ftps_deployment_disabled/app_function_ftps_deployment_disabled_test.py @@ -87,7 +87,7 @@ class Test_app_function_ftps_deployment_disabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=mock.MagicMock(type="SystemAssigned"), public_access=False, vnet_subnet_id=None, @@ -138,7 +138,7 @@ class Test_app_function_ftps_deployment_disabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=mock.MagicMock(type="SystemAssigned"), public_access=False, vnet_subnet_id=None, @@ -189,7 +189,7 @@ class Test_app_function_ftps_deployment_disabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=mock.MagicMock(type="SystemAssigned"), public_access=False, vnet_subnet_id=None, diff --git a/tests/providers/azure/services/app/app_function_identity_is_configured/app_function_identity_is_configured_test.py b/tests/providers/azure/services/app/app_function_identity_is_configured/app_function_identity_is_configured_test.py index 84dbece6d2..5a770a196c 100644 --- a/tests/providers/azure/services/app/app_function_identity_is_configured/app_function_identity_is_configured_test.py +++ b/tests/providers/azure/services/app/app_function_identity_is_configured/app_function_identity_is_configured_test.py @@ -87,7 +87,7 @@ class Test_app_function_identity_is_configured: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=None, public_access=False, vnet_subnet_id=None, @@ -138,7 +138,7 @@ class Test_app_function_identity_is_configured: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=mock.MagicMock(type="SystemAssigned"), public_access=False, vnet_subnet_id=None, diff --git a/tests/providers/azure/services/app/app_function_identity_without_admin_privileges/app_function_identity_without_admin_privileges_test.py b/tests/providers/azure/services/app/app_function_identity_without_admin_privileges/app_function_identity_without_admin_privileges_test.py index 9c7f074c3b..ef42098847 100644 --- a/tests/providers/azure/services/app/app_function_identity_without_admin_privileges/app_function_identity_without_admin_privileges_test.py +++ b/tests/providers/azure/services/app/app_function_identity_without_admin_privileges/app_function_identity_without_admin_privileges_test.py @@ -88,7 +88,7 @@ class Test_app_function_identity_without_admin_privileges: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=None, public_access=False, vnet_subnet_id=None, @@ -140,7 +140,7 @@ class Test_app_function_identity_without_admin_privileges: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=mock.MagicMock(principal_id="123"), public_access=False, vnet_subnet_id=None, @@ -228,7 +228,7 @@ class Test_app_function_identity_without_admin_privileges: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=mock.MagicMock(principal_id="123"), public_access=False, vnet_subnet_id=None, diff --git a/tests/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version_test.py b/tests/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version_test.py index 89bfc642b0..597335bc39 100644 --- a/tests/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version_test.py +++ b/tests/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version_test.py @@ -87,7 +87,7 @@ class Test_app_function_latest_runtime_version: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={"FUNCTIONS_EXTENSION_VERSION": "~4"}, + environment_variables={"FUNCTIONS_EXTENSION_VERSION": "~4"}, identity=None, public_access=False, vnet_subnet_id=None, @@ -137,7 +137,7 @@ class Test_app_function_latest_runtime_version: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={"FUNCTIONS_EXTENSION_VERSION": "2"}, + environment_variables={"FUNCTIONS_EXTENSION_VERSION": "2"}, identity=None, public_access=False, vnet_subnet_id=None, diff --git a/tests/providers/azure/services/app/app_function_not_publicly_accessible/app_function_not_publicly_accessible_test.py b/tests/providers/azure/services/app/app_function_not_publicly_accessible/app_function_not_publicly_accessible_test.py index 3c60aebc20..f8f36af8f4 100644 --- a/tests/providers/azure/services/app/app_function_not_publicly_accessible/app_function_not_publicly_accessible_test.py +++ b/tests/providers/azure/services/app/app_function_not_publicly_accessible/app_function_not_publicly_accessible_test.py @@ -87,7 +87,7 @@ class Test_app_function_not_publicly_accessible: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=mock.MagicMock(type="SystemAssigned"), public_access=False, vnet_subnet_id=None, @@ -138,7 +138,7 @@ class Test_app_function_not_publicly_accessible: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=mock.MagicMock(type="SystemAssigned"), public_access=True, vnet_subnet_id=None, diff --git a/tests/providers/azure/services/app/app_function_vnet_integration_enabled/app_function_vnet_integration_enabled_test.py b/tests/providers/azure/services/app/app_function_vnet_integration_enabled/app_function_vnet_integration_enabled_test.py index f12422f1da..c12b14de7c 100644 --- a/tests/providers/azure/services/app/app_function_vnet_integration_enabled/app_function_vnet_integration_enabled_test.py +++ b/tests/providers/azure/services/app/app_function_vnet_integration_enabled/app_function_vnet_integration_enabled_test.py @@ -87,7 +87,7 @@ class Test_app_function_vnet_integration_enabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=None, public_access=True, vnet_subnet_id="vnet_subnet_id", @@ -138,7 +138,7 @@ class Test_app_function_vnet_integration_enabled: location="West Europe", kind="functionapp,linux", function_keys={}, - enviroment_variables={}, + environment_variables={}, identity=None, public_access=True, vnet_subnet_id=None, diff --git a/tests/providers/azure/services/app/app_service_test.py b/tests/providers/azure/services/app/app_service_test.py index 76f602431e..ccd396cc0d 100644 --- a/tests/providers/azure/services/app/app_service_test.py +++ b/tests/providers/azure/services/app/app_service_test.py @@ -222,7 +222,7 @@ class Test_App_Service: location="West Europe", kind="functionapp", function_keys=None, - enviroment_variables=None, + environment_variables=None, identity=ManagedServiceIdentity(type="SystemAssigned"), public_access=True, vnet_subnet_id="", @@ -247,6 +247,56 @@ class Test_App_Service: == "functionapp-1" ) + def test_get_function_host_keys_logs_warning_on_optional_failure(self): + from prowler.providers.azure.services.app.app_service import App + + mock_client = MagicMock() + mock_client.web_apps.list_host_keys.side_effect = Exception("Forbidden") + app = object.__new__(App) + app.clients = {AZURE_SUBSCRIPTION_ID: mock_client} + + with ( + patch( + "prowler.providers.azure.services.app.app_service.logger.warning" + ) as mock_warning, + patch( + "prowler.providers.azure.services.app.app_service.logger.error" + ) as mock_error, + ): + result = app._get_function_host_keys( + AZURE_SUBSCRIPTION_ID, RESOURCE_GROUP, "functionapp-1" + ) + + assert result is None + mock_warning.assert_called_once() + mock_error.assert_not_called() + + def test_list_application_settings_logs_warning_on_optional_failure(self): + from prowler.providers.azure.services.app.app_service import App + + mock_client = MagicMock() + mock_client.web_apps.list_application_settings.side_effect = Exception( + "Forbidden" + ) + app = object.__new__(App) + app.clients = {AZURE_SUBSCRIPTION_ID: mock_client} + + with ( + patch( + "prowler.providers.azure.services.app.app_service.logger.warning" + ) as mock_warning, + patch( + "prowler.providers.azure.services.app.app_service.logger.error" + ) as mock_error, + ): + result = app._list_application_settings( + AZURE_SUBSCRIPTION_ID, RESOURCE_GROUP, "functionapp-1" + ) + + assert result is None + mock_warning.assert_called_once() + mock_error.assert_not_called() + class Test_App_get_apps: def test_get_apps_no_resource_groups(self):