mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 12:31:54 +00:00
feat(app): Add new Azure functions checks (#4189)
Co-authored-by: Sergio <sergio@prowler.com>
This commit is contained in:
committed by
GitHub
parent
465261e1df
commit
536f0df9d3
@@ -7,11 +7,13 @@ WINDOWS_AZURE_SERVICE_MANAGEMENT_API = "797f4846-ba00-4fd7-ba43-dac1f8f63013"
|
||||
GUEST_USER_ACCESS_NO_RESTRICTICTED = UUID("a0b1b346-4d3e-4e8b-98f8-753987be4970")
|
||||
GUEST_USER_ACCESS_RESTRICTICTED = UUID("2af84b1e-32c8-42b7-82bc-daa82404023b")
|
||||
|
||||
# General built-in roles
|
||||
# General administrator built-in roles
|
||||
CONTRIBUTOR_ROLE_ID = "b24988ac-6180-42a0-ab88-20f7382dd24c"
|
||||
OWNER_ROLE_ID = "8e3af657-a8ff-443c-a75c-2fe8c4bcb635"
|
||||
ROLE_BASED_ACCESS_CONTROL_ADMINISTRATOR_ROLE_ID = "f58310d9-a9f6-439a-9e8d-f62e7b41a168"
|
||||
USER_ACCESS_ADMINISTRATOR_ROLE_ID = "18d7d88d-d35e-4fb5-a5c3-7773c20a72d9"
|
||||
|
||||
# Compute roles
|
||||
# Compute admin roles IDs
|
||||
VIRTUAL_MACHINE_CONTRIBUTOR_ROLE_ID = "9980e02c-c2be-4d73-94e8-173b1dc7cf3c"
|
||||
VIRTUAL_MACHINE_ADMINISTRATOR_LOGIN_ROLE_ID = "1c0163c0-47e6-4577-8991-ea5c82e286e4"
|
||||
VIRTUAL_MACHINE_USER_LOGIN_ROLE_ID = "fb879df8-f326-4884-b1cf-06f3ad86be52"
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "app_function_access_keys_configured",
|
||||
"CheckTitle": "Ensure that Azure Functions are using access keys for enhanced security",
|
||||
"CheckType": [],
|
||||
"ServiceName": "app",
|
||||
"SubServiceName": "function",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Microsoft.Web/sites",
|
||||
"Description": "Azure Functions provide a way to secure HTTP function endpoints during development and production. Using access keys adds an extra layer of protection, ensuring that only authorized users or systems can access the functions. This is particularly important when dealing with public apps or sensitive data.",
|
||||
"Risk": "Unprotected function endpoints may be vulnerable to unauthorized access, leading to potential data breaches or malicious activity.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=python-v2%2Cisolated-process%2Cnodejs-v4%2Cfunctionsv2&pivots=programming-language-csharp#authorization-keys",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/Functions/azure-function-anonymous-access.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use access keys to secure Azure Functions. You can create and manage keys in the Azure portal or using the Azure CLI. For more information, see the official documentation.",
|
||||
"Url": "https://learn.microsoft.com/en-us/azure/azure-functions/security-concepts?tabs=v4#function-access-keys"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "For additional security, consider using managed identities and key vaults along with access keys. This provides granular control over resource access and improves auditability."
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
|
||||
|
||||
class app_function_access_keys_configured(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for (
|
||||
subscription_name,
|
||||
functions,
|
||||
) in app_client.functions.items():
|
||||
for function_id, function in functions.items():
|
||||
report = Check_Report_Azure(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Function {function.name} does not have function keys configured."
|
||||
)
|
||||
report.subscription = subscription_name
|
||||
report.resource_name = function.name
|
||||
report.resource_id = function_id
|
||||
report.location = function.location
|
||||
|
||||
if len(function.function_keys) > 0:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Function {function.name} has function keys configured."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "app_function_application_insights_enabled",
|
||||
"CheckTitle": "Ensure Function App has Application Insights configured",
|
||||
"CheckType": [],
|
||||
"ServiceName": "app",
|
||||
"SubServiceName": "function",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Microsoft.Web/sites",
|
||||
"Description": "Application Insights is a powerful tool for monitoring the performance and health of Azure Function Apps. It provides valuable insights into exceptions, performance issues, and usage patterns, enabling timely detection and resolution of issues.",
|
||||
"Risk": "Without Application Insights, you may miss critical errors, performance degradation, or abnormal behavior in your Function App, potentially impacting availability and user experience.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/Functions/function-app-insights-on.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable Application Insights for your Azure Function App to monitor its performance and health.",
|
||||
"Url": "https://learn.microsoft.com/en-us/azure/azure-monitor/app/monitor-functions"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
from prowler.providers.azure.services.appinsights.appinsights_client import (
|
||||
appinsights_client,
|
||||
)
|
||||
|
||||
|
||||
class app_function_application_insights_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for (
|
||||
subscription_name,
|
||||
functions,
|
||||
) in app_client.functions.items():
|
||||
for function_id, function in functions.items():
|
||||
report = Check_Report_Azure(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Function {function.name} is not using Application Insights."
|
||||
)
|
||||
report.subscription = subscription_name
|
||||
report.resource_name = function.name
|
||||
report.resource_id = function_id
|
||||
report.location = function.location
|
||||
|
||||
if function.enviroment_variables.get(
|
||||
"APPINSIGHTS_INSTRUMENTATIONKEY", ""
|
||||
) in [
|
||||
component.instrumentation_key
|
||||
for component in appinsights_client.components[
|
||||
subscription_name
|
||||
].values()
|
||||
]:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Function {function.name} is using Application Insights."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "app_function_ftps_deployment_disabled",
|
||||
"CheckTitle": "Ensure that FTP and FTPS deployments are disabled for Azure Functions to prevent unauthorized access and data breaches.",
|
||||
"CheckType": [],
|
||||
"ServiceName": "app",
|
||||
"SubServiceName": "function",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Microsoft.Web/sites",
|
||||
"Description": "Azure FTP deployment endpoints are unencrypted and public, making them vulnerable to attacks. Disabling FTP and FTPS deployments enhances security by preventing unauthorized access to login credentials and sensitive codebases.",
|
||||
"Risk": "If left enabled, attackers can intercept network traffic and gain full control of the app or service, leading to potential data breaches and unauthorized modifications.",
|
||||
"RelatedUrl": "https://docs.microsoft.com/en-us/azure/app-service/deploy-ftp",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "az webapp config set --resource-group <resource-group> --name <app-name> --ftps-state Disabled",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "",
|
||||
"Arm": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended to disable FTP and FTPS deployments for Azure Functions to mitigate security risks. Instead, consider using more secure deployment methods such as Docker contianer or enabling continuous deployment with GitHub Actions.",
|
||||
"Url": "https://learn.microsoft.com/en-us/azure/azure-functions/functions-deployment-technologies?tabs=windows#trigger-syncing"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "This check ensures that Azure Functions are deployed securely, reducing the attack surface and protecting sensitive information."
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
|
||||
|
||||
class app_function_ftps_deployment_disabled(Check):
|
||||
def execute(self) -> Check_Report_Azure:
|
||||
findings = []
|
||||
|
||||
for (
|
||||
subscription_name,
|
||||
functions,
|
||||
) in app_client.functions.items():
|
||||
for function_id, function in functions.items():
|
||||
report = Check_Report_Azure(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Function {function.name} has {'FTP' if function.ftps_state == 'AllAllowed' else 'FTPS' if function.ftps_state == 'FtpsOnly' else 'FTP or FTPS'} deployment enabled"
|
||||
report.subscription = subscription_name
|
||||
report.resource_name = function.name
|
||||
report.resource_id = function_id
|
||||
report.location = function.location
|
||||
|
||||
if function.ftps_state == "Disabled":
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Function {function.name} has FTP and FTPS deployment disabled"
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "app_function_identity_is_configured",
|
||||
"CheckTitle": "Ensure Azure function has system or user assigned managed identity configured",
|
||||
"CheckType": [],
|
||||
"ServiceName": "app",
|
||||
"SubServiceName": "function",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Microsoft.Web/sites",
|
||||
"Description": "Azure Functions should have managed identities configured for enhanced security and access control.",
|
||||
"Risk": "Not using managed identities can lead to less secure authentication and authorization practices, potentially exposing sensitive data.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "az functionapp identity assign --name <function_name> --resource-group <resource_group> --identities [system]",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/Functions/azure-function-system-assigned-identity.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended to enable managed identities for Azure Functions to enhance security and access control. This allows the function app to easily access other Azure resources securely and with the appropriate permissions.",
|
||||
"Url": "https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
|
||||
|
||||
class app_function_identity_is_configured(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for (
|
||||
subscription_name,
|
||||
functions,
|
||||
) in app_client.functions.items():
|
||||
for function_id, function in functions.items():
|
||||
report = Check_Report_Azure(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Function {function.name} does not have a managed identity enabled."
|
||||
report.subscription = subscription_name
|
||||
report.resource_name = function.name
|
||||
report.resource_id = function_id
|
||||
report.location = function.location
|
||||
|
||||
if function.identity:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Function {function.name} has a {function.identity.type if getattr(function.identity, 'type', '') else 'managed'} identity enabled."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "app_function_identity_without_admin_privileges",
|
||||
"CheckTitle": "Ensure that your Azure functions are not configured with an identity with admin privileges",
|
||||
"CheckType": [],
|
||||
"ServiceName": "app",
|
||||
"SubServiceName": "function",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Microsoft.Web/sites",
|
||||
"Description": "It is important to ensure that Azure functions are not configured with administrative privileges to maintain the principle of least privilege and reduce the attack surface. By limiting the privileges of Azure functions, potential security risks and data leaks can be mitigated.",
|
||||
"Risk": "If Azure functions are configured with administrative privileges, it increases the risk of unauthorized access, privilege escalation, and data breaches. Attackers can exploit these privileges to gain access to sensitive data and compromise the entire system.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/Functions/azure-function-admin-permissions.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "To remediate this issue, ensure that Azure functions are not configured with an identity that has administrative privileges. Instead, use the principle of least privilege to grant only the necessary permissions to Azure functions. For more information, refer to the official documentation: Use the principle of least privilege.",
|
||||
"Url": "https://docs.microsoft.com/en-us/azure/architecture/framework/security/design-identity-authorization#use-the-principle-of-least-privilege"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "This check helps prevent privilege escalation attacks and ensures that Azure functions operate with the necessary permissions, reducing the impact of potential security breaches."
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.config import (
|
||||
CONTRIBUTOR_ROLE_ID,
|
||||
OWNER_ROLE_ID,
|
||||
ROLE_BASED_ACCESS_CONTROL_ADMINISTRATOR_ROLE_ID,
|
||||
USER_ACCESS_ADMINISTRATOR_ROLE_ID,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
from prowler.providers.azure.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
class app_function_identity_without_admin_privileges(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for (
|
||||
subscription_name,
|
||||
functions,
|
||||
) in app_client.functions.items():
|
||||
for function_id, function in functions.items():
|
||||
if function.identity:
|
||||
report = Check_Report_Azure(self.metadata())
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Function {function.name} has a managed identity enabled but without admin privileges."
|
||||
report.subscription = subscription_name
|
||||
report.resource_name = function.name
|
||||
report.resource_id = function_id
|
||||
report.location = function.location
|
||||
|
||||
admin_roles_assigned = []
|
||||
|
||||
for role_assignment in iam_client.role_assignments[
|
||||
subscription_name
|
||||
].values():
|
||||
if (
|
||||
role_assignment.agent_id == function.identity.principal_id
|
||||
and role_assignment.role_id
|
||||
in [
|
||||
CONTRIBUTOR_ROLE_ID,
|
||||
OWNER_ROLE_ID,
|
||||
ROLE_BASED_ACCESS_CONTROL_ADMINISTRATOR_ROLE_ID,
|
||||
USER_ACCESS_ADMINISTRATOR_ROLE_ID,
|
||||
]
|
||||
):
|
||||
for role in iam_client.roles[subscription_name]:
|
||||
if role.id.split("/")[-1] == role_assignment.role_id:
|
||||
admin_roles_assigned.append(role.name)
|
||||
|
||||
if admin_roles_assigned:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Function {function.name} has a managed identity enabled and it is configure with admin privileges using {'roles: ' + ', '.join(admin_roles_assigned) if len(admin_roles_assigned) > 1 else 'role ' + admin_roles_assigned[0]}."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "app_function_latest_runtime_version",
|
||||
"CheckTitle": "Ensure Azure Functions are using the latest supported runtime",
|
||||
"CheckType": [],
|
||||
"ServiceName": "app",
|
||||
"SubServiceName": "function",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Microsoft.Web/sites",
|
||||
"Description": "Keeping Azure Functions up to date with the latest supported runtime version is crucial for security and performance. Updates often include security patches and enhancements, helping to protect against known vulnerabilities and potential exploits. Additionally, newer runtime versions may offer improved functionality and optimized resource utilization.",
|
||||
"Risk": "Using outdated runtime versions may introduce security risks and performance degradation. Outdated runtimes may have unpatched vulnerabilities, making them susceptible to attacks.",
|
||||
"RelatedUrl": "https://docs.microsoft.com/en-us/azure/azure-functions/functions-versions",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "az functionapp config appsettings set --name <function_app_name> --resource-group <resource_group_name> --settings FUNCTIONS_EXTENSION_VERSION=~4",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/Functions/azure-function-runtime-version.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "",
|
||||
"Url": "https://learn.microsoft.com/en-us/azure/azure-functions/migrate-version-3-version-4?tabs=net8%2Cazure-cli%2Cwindows&pivots=programming-language-python"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Stay informed about the latest security updates and patch releases for Azure Functions to maintain a secure and up-to-date environment."
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
|
||||
|
||||
class app_function_latest_runtime_version(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for (
|
||||
subscription_name,
|
||||
functions,
|
||||
) in app_client.functions.items():
|
||||
for function_id, function in functions.items():
|
||||
report = Check_Report_Azure(self.metadata())
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Function {function.name} is using the latest runtime."
|
||||
)
|
||||
report.subscription = subscription_name
|
||||
report.resource_name = function.name
|
||||
report.resource_id = function_id
|
||||
report.location = function.location
|
||||
|
||||
if (
|
||||
function.enviroment_variables.get("FUNCTIONS_EXTENSION_VERSION", "")
|
||||
!= "~4"
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Function {function.name} is not using the latest runtime. The current runtime is '{function.enviroment_variables.get('FUNCTIONS_EXTENSION_VERSION', '')}' and should be '~4'."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "app_function_not_publicly_accessible",
|
||||
"CheckTitle": "Ensure Azure Functions are not publicly accessible",
|
||||
"CheckType": [],
|
||||
"ServiceName": "app",
|
||||
"SubServiceName": "function",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Microsoft.Web/sites",
|
||||
"Description": "Azure Functions should not be exposed to the public internet. Restricting access helps protect applications from potential threats and reduces the attack surface.",
|
||||
"Risk": "Exposing Azure Functions to the public internet increases the risk of unauthorized access, data breaches, and other security threats.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/azure-functions/functions-networking-options",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/Functions/azure-function-exposed.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Review the Azure Functions security guidelines and ensure that access restrictions are in place. Use Azure Private Link and Key Vault for enhanced security.",
|
||||
"Url": "https://learn.microsoft.com/en-us/azure/app-service/overview-access-restrictions"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
|
||||
|
||||
class app_function_not_publicly_accessible(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for (
|
||||
subscription_name,
|
||||
functions,
|
||||
) in app_client.functions.items():
|
||||
for function_id, function in functions.items():
|
||||
report = Check_Report_Azure(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Function {function.name} is publicly accessible."
|
||||
)
|
||||
report.subscription = subscription_name
|
||||
report.resource_name = function.name
|
||||
report.resource_id = function_id
|
||||
report.location = function.location
|
||||
|
||||
if not function.public_access:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Function {function.name} is not publicly accessible."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "app_function_vnet_integration_enabled",
|
||||
"CheckTitle": "Ensure Virtual Network Integration is Enabled for Azure Functions",
|
||||
"CheckType": [],
|
||||
"ServiceName": "app",
|
||||
"SubServiceName": "function",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Microsoft.Web/sites",
|
||||
"Description": "Enabling Virtual Network Integration for Azure Functions provides an additional layer of security by restricting access to selected virtual network subnets. This helps to protect your Function Apps from unauthorized access and potential threats.",
|
||||
"Risk": "Without Virtual Network Integration, your Function Apps may be exposed to the public internet, increasing the risk of unauthorized access and potential security breaches.",
|
||||
"RelatedUrl": "https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#virtual-network-integration",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "az functionapp vnet-integration update --name <function_app_name> --resource-group <resource_group_name> --vnet <vnet_name> --subnet <subnet_name>",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/Functions/azure-function-vnet-integration-on.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended to enable Virtual Network Integration for Azure Functions to enhance security and protect against unauthorized access.",
|
||||
"Url": "https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#enable-virtual-network-integration"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
|
||||
|
||||
class app_function_vnet_integration_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for (
|
||||
subscription_name,
|
||||
functions,
|
||||
) in app_client.functions.items():
|
||||
for function_id, function in functions.items():
|
||||
report = Check_Report_Azure(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Function {function.name} does not have virtual network integration enabled."
|
||||
report.subscription = subscription_name
|
||||
report.resource_name = function.name
|
||||
report.resource_id = function_id
|
||||
report.location = function.location
|
||||
|
||||
if function.vnet_subnet_id:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Function {function.name} has Virtual Network integration enabled with subnet '{function.vnet_subnet_id}' enabled."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -1,4 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict
|
||||
|
||||
from azure.mgmt.web import WebSiteManagementClient
|
||||
from azure.mgmt.web.models import ManagedServiceIdentity, SiteConfigResource
|
||||
@@ -10,11 +11,11 @@ from prowler.providers.azure.services.monitor.monitor_client import monitor_clie
|
||||
from prowler.providers.azure.services.monitor.monitor_service import DiagnosticSetting
|
||||
|
||||
|
||||
########################## App
|
||||
class App(AzureService):
|
||||
def __init__(self, provider: AzureProvider):
|
||||
super().__init__(WebSiteManagementClient, provider)
|
||||
self.apps = self.__get_apps__()
|
||||
self.functions = self.__get_functions__()
|
||||
|
||||
def __get_apps__(self):
|
||||
logger.info("App - Getting apps...")
|
||||
@@ -26,41 +27,43 @@ class App(AzureService):
|
||||
apps.update({subscription_name: {}})
|
||||
|
||||
for app in apps_list:
|
||||
platform_auth = getattr(
|
||||
client.web_apps.get_auth_settings_v2(
|
||||
resource_group_name=app.resource_group, name=app.name
|
||||
),
|
||||
"platform",
|
||||
None,
|
||||
)
|
||||
# Filter function apps
|
||||
if getattr(app, "kind", "app").startswith("app"):
|
||||
platform_auth = getattr(
|
||||
client.web_apps.get_auth_settings_v2(
|
||||
resource_group_name=app.resource_group, name=app.name
|
||||
),
|
||||
"platform",
|
||||
None,
|
||||
)
|
||||
|
||||
apps[subscription_name].update(
|
||||
{
|
||||
app.name: WebApp(
|
||||
resource_id=app.id,
|
||||
auth_enabled=(
|
||||
getattr(platform_auth, "enabled", False)
|
||||
if platform_auth
|
||||
else False
|
||||
),
|
||||
configurations=client.web_apps.get_configuration(
|
||||
resource_group_name=app.resource_group,
|
||||
name=app.name,
|
||||
),
|
||||
client_cert_mode=self.__get_client_cert_mode__(
|
||||
getattr(app, "client_cert_enabled", False),
|
||||
getattr(app, "client_cert_mode", "Ignore"),
|
||||
),
|
||||
monitor_diagnostic_settings=self.__get_app_monitor_settings__(
|
||||
app.name, app.resource_group, subscription_name
|
||||
),
|
||||
https_only=getattr(app, "https_only", False),
|
||||
identity=getattr(app, "identity", None),
|
||||
location=app.location,
|
||||
kind=getattr(app, "kind", "app"),
|
||||
)
|
||||
}
|
||||
)
|
||||
apps[subscription_name].update(
|
||||
{
|
||||
app.name: WebApp(
|
||||
resource_id=app.id,
|
||||
auth_enabled=(
|
||||
getattr(platform_auth, "enabled", False)
|
||||
if platform_auth
|
||||
else False
|
||||
),
|
||||
configurations=client.web_apps.get_configuration(
|
||||
resource_group_name=app.resource_group,
|
||||
name=app.name,
|
||||
),
|
||||
client_cert_mode=self.__get_client_cert_mode__(
|
||||
getattr(app, "client_cert_enabled", False),
|
||||
getattr(app, "client_cert_mode", "Ignore"),
|
||||
),
|
||||
monitor_diagnostic_settings=self.__get_app_monitor_settings__(
|
||||
app.name, app.resource_group, subscription_name
|
||||
),
|
||||
https_only=getattr(app, "https_only", False),
|
||||
identity=getattr(app, "identity", None),
|
||||
location=app.location,
|
||||
kind=app.kind,
|
||||
)
|
||||
}
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -68,6 +71,73 @@ class App(AzureService):
|
||||
|
||||
return apps
|
||||
|
||||
def __get_functions__(self):
|
||||
logger.info("Function - Getting functions...")
|
||||
functions = {}
|
||||
|
||||
for subscription_name, client in self.clients.items():
|
||||
try:
|
||||
functions_list = client.web_apps.list()
|
||||
functions.update({subscription_name: {}})
|
||||
|
||||
for function in functions_list:
|
||||
# Filter function apps
|
||||
if getattr(function, "kind", "").startswith("functionapp"):
|
||||
# List host keys
|
||||
host_keys = client.web_apps.list_host_keys(
|
||||
resource_group_name=function.resource_group,
|
||||
name=function.name,
|
||||
) # Need to add role 'Logic App Contributor' to the service principal to get the host keys or add to the reader role the permission 'Microsoft.Web/sites/host/listkeys'
|
||||
|
||||
function_config = client.web_apps.get_configuration(
|
||||
resource_group_name=function.resource_group,
|
||||
name=function.name,
|
||||
)
|
||||
|
||||
functions[subscription_name].update(
|
||||
{
|
||||
function.id: FunctionApp(
|
||||
name=function.name,
|
||||
location=function.location,
|
||||
kind=function.kind,
|
||||
function_keys=getattr(
|
||||
host_keys, "function_keys", {}
|
||||
),
|
||||
enviroment_variables=getattr(
|
||||
client.web_apps.list_application_settings(
|
||||
resource_group_name=function.resource_group,
|
||||
name=function.name,
|
||||
),
|
||||
"properties",
|
||||
{},
|
||||
),
|
||||
identity=getattr(function, "identity", None),
|
||||
public_access=(
|
||||
False
|
||||
if getattr(
|
||||
function, "public_network_access", ""
|
||||
)
|
||||
== "Disabled"
|
||||
else True
|
||||
),
|
||||
vnet_subnet_id=getattr(
|
||||
function,
|
||||
"virtual_network_subnet_id",
|
||||
"",
|
||||
),
|
||||
ftps_state=getattr(
|
||||
function_config, "ftps_state", ""
|
||||
),
|
||||
)
|
||||
}
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
return functions
|
||||
|
||||
def __get_client_cert_mode__(
|
||||
self, client_cert_enabled: bool, client_cert_mode: str
|
||||
):
|
||||
@@ -112,3 +182,16 @@ class WebApp:
|
||||
https_only: bool = False
|
||||
monitor_diagnostic_settings: list[DiagnosticSetting] = None
|
||||
kind: str = "app"
|
||||
|
||||
|
||||
@dataclass
|
||||
class FunctionApp:
|
||||
name: str
|
||||
location: str
|
||||
kind: str
|
||||
function_keys: Dict[str, str]
|
||||
enviroment_variables: Dict[str, str]
|
||||
identity: ManagedServiceIdentity
|
||||
public_access: bool
|
||||
vnet_subnet_id: str
|
||||
ftps_state: str
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from azure.mgmt.applicationinsights import ApplicationInsightsManagementClient
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.azure.azure_provider import AzureProvider
|
||||
@@ -29,6 +28,9 @@ class AppInsights(AzureService):
|
||||
resource_id=component.id,
|
||||
resource_name=component.name,
|
||||
location=component.location,
|
||||
instrumentation_key=getattr(
|
||||
component, "instrumentation_key", "Not Found"
|
||||
),
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -40,8 +42,8 @@ class AppInsights(AzureService):
|
||||
return components
|
||||
|
||||
|
||||
@dataclass
|
||||
class Component:
|
||||
class Component(BaseModel):
|
||||
resource_id: str
|
||||
resource_name: str
|
||||
location: str
|
||||
instrumentation_key: str
|
||||
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_app_function_access_keys_configured:
|
||||
def test_app_no_subscriptions(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_access_keys_configured.app_function_access_keys_configured.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_access_keys_configured.app_function_access_keys_configured import (
|
||||
app_function_access_keys_configured,
|
||||
)
|
||||
|
||||
app_client.functions = {}
|
||||
|
||||
check = app_function_access_keys_configured()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_subscription_empty(self):
|
||||
app_client = mock.MagicMock
|
||||
app_client.functions = {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_function_access_keys_configured.app_function_access_keys_configured.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_access_keys_configured.app_function_access_keys_configured import (
|
||||
app_function_access_keys_configured,
|
||||
)
|
||||
|
||||
check = app_function_access_keys_configured()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_function_no_keys(self):
|
||||
app_client = mock.MagicMock
|
||||
app_client.functions = {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_function_access_keys_configured.app_function_access_keys_configured.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_access_keys_configured.app_function_access_keys_configured import (
|
||||
app_function_access_keys_configured,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_access_keys_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 does not have function keys configured."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_using_functions_keys(self):
|
||||
app_client = mock.MagicMock
|
||||
app_client.functions = {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_function_access_keys_configured.app_function_access_keys_configured.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_access_keys_configured.app_function_access_keys_configured import (
|
||||
app_function_access_keys_configured,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={
|
||||
"default": "key1",
|
||||
"key2": "key2",
|
||||
},
|
||||
enviroment_variables={},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_access_keys_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 has function keys configured."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_app_function_application_insights_enabled:
|
||||
def test_app_no_subscriptions(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled import (
|
||||
app_function_application_insights_enabled,
|
||||
)
|
||||
|
||||
app_client.functions = {}
|
||||
|
||||
check = app_function_application_insights_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_subscription_empty(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled import (
|
||||
app_function_application_insights_enabled,
|
||||
)
|
||||
|
||||
app_client.functions = {AZURE_SUBSCRIPTION_ID: {}}
|
||||
|
||||
check = app_function_application_insights_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_function_no_app_insights(self):
|
||||
app_client = mock.MagicMock
|
||||
app_insights = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.app_client",
|
||||
new=app_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.appinsights_client",
|
||||
new=app_insights,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled import (
|
||||
app_function_application_insights_enabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
from prowler.providers.azure.services.appinsights.appinsights_service import (
|
||||
Component,
|
||||
)
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
app_insights.components = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"app_id-1": Component(
|
||||
resource_id="component_id",
|
||||
resource_name="component_name",
|
||||
location="West Europe",
|
||||
instrumentation_key="1234",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_application_insights_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 is not using Application Insights."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_using_app_insights(self):
|
||||
app_client = mock.MagicMock
|
||||
app_insights = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.app_client",
|
||||
new=app_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.appinsights_client",
|
||||
new=app_insights,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled import (
|
||||
app_function_application_insights_enabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
from prowler.providers.azure.services.appinsights.appinsights_service import (
|
||||
Component,
|
||||
)
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={"APPINSIGHTS_INSTRUMENTATIONKEY": "1234"},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
app_insights.components = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"app_id-1": Component(
|
||||
resource_id="component_id",
|
||||
resource_name="component_name",
|
||||
location="West Europe",
|
||||
instrumentation_key="1234",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_application_insights_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 is using Application Insights."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_using_app_insights_different_key(self):
|
||||
app_client = mock.MagicMock
|
||||
app_insights = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.app_client",
|
||||
new=app_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.appinsights_client",
|
||||
new=app_insights,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled import (
|
||||
app_function_application_insights_enabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
from prowler.providers.azure.services.appinsights.appinsights_service import (
|
||||
Component,
|
||||
)
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={"APPINSIGHTS_INSTRUMENTATIONKEY": "1234"},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
app_insights.components = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"app_id-1": Component(
|
||||
resource_id="component_id",
|
||||
resource_name="component_name",
|
||||
location="West Europe",
|
||||
instrumentation_key="5678",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_application_insights_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 is not using Application Insights."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_with_app_insights_no_key(self):
|
||||
app_client = mock.MagicMock
|
||||
app_insights = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.app_client",
|
||||
new=app_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled.appinsights_client",
|
||||
new=app_insights,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_application_insights_enabled.app_function_application_insights_enabled import (
|
||||
app_function_application_insights_enabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
from prowler.providers.azure.services.appinsights.appinsights_service import (
|
||||
Component,
|
||||
)
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
app_insights.components = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"app_id-1": Component(
|
||||
resource_id="component_id",
|
||||
resource_name="component_name",
|
||||
location="West Europe",
|
||||
instrumentation_key="Not Found",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_application_insights_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 is not using Application Insights."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_app_function_ftps_deployment_disabled:
|
||||
def test_no_subscriptions(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled import (
|
||||
app_function_ftps_deployment_disabled,
|
||||
)
|
||||
|
||||
app_client.functions = {}
|
||||
|
||||
check = app_function_ftps_deployment_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_subscription_empty(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled import (
|
||||
app_function_ftps_deployment_disabled,
|
||||
)
|
||||
|
||||
app_client.functions = {AZURE_SUBSCRIPTION_ID: {}}
|
||||
|
||||
check = app_function_ftps_deployment_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_function_ftp_deployment_enabled(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled import (
|
||||
app_function_ftps_deployment_disabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=mock.MagicMock(type="SystemAssigned"),
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_ftps_deployment_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 has FTP deployment enabled"
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].location == "West Europe"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
|
||||
def test_function_ftps_deployment_enabled(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled import (
|
||||
app_function_ftps_deployment_disabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=mock.MagicMock(type="SystemAssigned"),
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="FtpsOnly",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_ftps_deployment_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 has FTPS deployment enabled"
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].location == "West Europe"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
|
||||
def test_function_ftp_and_ftps_deployment_disabled(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_ftps_deployment_disabled.app_function_ftps_deployment_disabled import (
|
||||
app_function_ftps_deployment_disabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=mock.MagicMock(type="SystemAssigned"),
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="Disabled",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_ftps_deployment_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 has FTP and FTPS deployment disabled"
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].location == "West Europe"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_app_function_identity_is_configured:
|
||||
def test_app_no_subscriptions(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_is_configured.app_function_identity_is_configured.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_is_configured.app_function_identity_is_configured import (
|
||||
app_function_identity_is_configured,
|
||||
)
|
||||
|
||||
app_client.functions = {}
|
||||
|
||||
check = app_function_identity_is_configured()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_subscription_empty(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_is_configured.app_function_identity_is_configured.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_is_configured.app_function_identity_is_configured import (
|
||||
app_function_identity_is_configured,
|
||||
)
|
||||
|
||||
app_client.functions = {AZURE_SUBSCRIPTION_ID: {}}
|
||||
|
||||
check = app_function_identity_is_configured()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_function_no_identity(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_is_configured.app_function_identity_is_configured.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_is_configured.app_function_identity_is_configured import (
|
||||
app_function_identity_is_configured,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_identity_is_configured()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 does not have a managed identity enabled."
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_identity_configured(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_is_configured.app_function_identity_is_configured.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_is_configured.app_function_identity_is_configured import (
|
||||
app_function_identity_is_configured,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=mock.MagicMock(type="SystemAssigned"),
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_identity_is_configured()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 has a SystemAssigned identity enabled."
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.config import USER_ACCESS_ADMINISTRATOR_ROLE_ID
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_app_function_identity_without_admin_privileges:
|
||||
def test_app_no_subscriptions(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges import (
|
||||
app_function_identity_without_admin_privileges,
|
||||
)
|
||||
|
||||
app_client.functions = {}
|
||||
|
||||
check = app_function_identity_without_admin_privileges()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_subscription_empty(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges import (
|
||||
app_function_identity_without_admin_privileges,
|
||||
)
|
||||
|
||||
app_client.functions = {AZURE_SUBSCRIPTION_ID: {}}
|
||||
|
||||
check = app_function_identity_without_admin_privileges()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_function_no_identity(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges import (
|
||||
app_function_identity_without_admin_privileges,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_identity_without_admin_privileges()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_function_no_admin_roles(self):
|
||||
app_client = mock.MagicMock
|
||||
iam_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges.app_client",
|
||||
new=app_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges.iam_client",
|
||||
new=iam_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges import (
|
||||
app_function_identity_without_admin_privileges,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
from prowler.providers.azure.services.iam.iam_service import (
|
||||
Role,
|
||||
RoleAssignment,
|
||||
)
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=mock.MagicMock(principal_id="123"),
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
iam_client.role_assignments = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"1": RoleAssignment(
|
||||
role_id="1",
|
||||
agent_id="123",
|
||||
agent_type="User",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
iam_client.roles = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Role(
|
||||
id="1",
|
||||
name="role1",
|
||||
type="User",
|
||||
assignable_scopes=[],
|
||||
permissions=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
check = app_function_identity_without_admin_privileges()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 has a managed identity enabled but without admin privileges."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_admin_roles(self):
|
||||
app_client = mock.MagicMock
|
||||
iam_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges.app_client",
|
||||
new=app_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges.iam_client",
|
||||
new=iam_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_identity_without_admin_privileges.app_function_identity_without_admin_privileges import (
|
||||
app_function_identity_without_admin_privileges,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
from prowler.providers.azure.services.iam.iam_service import (
|
||||
Role,
|
||||
RoleAssignment,
|
||||
)
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=mock.MagicMock(principal_id="123"),
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
iam_client.role_assignments = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"1": RoleAssignment(
|
||||
role_id=USER_ACCESS_ADMINISTRATOR_ROLE_ID,
|
||||
agent_id="123",
|
||||
agent_type="User",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
iam_client.roles = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Role(
|
||||
id=USER_ACCESS_ADMINISTRATOR_ROLE_ID,
|
||||
name="User Access Administrator",
|
||||
type="User",
|
||||
assignable_scopes=[],
|
||||
permissions=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
check = app_function_identity_without_admin_privileges()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 has a managed identity enabled and it is configure with admin privileges using role User Access Administrator."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_app_function_latest_runtime_version:
|
||||
def test_app_no_subscriptions(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_latest_runtime_version.app_function_latest_runtime_version.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_latest_runtime_version.app_function_latest_runtime_version import (
|
||||
app_function_latest_runtime_version,
|
||||
)
|
||||
|
||||
app_client.functions = {}
|
||||
|
||||
check = app_function_latest_runtime_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_subscription_empty(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_latest_runtime_version.app_function_latest_runtime_version.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_latest_runtime_version.app_function_latest_runtime_version import (
|
||||
app_function_latest_runtime_version,
|
||||
)
|
||||
|
||||
app_client.functions = {AZURE_SUBSCRIPTION_ID: {}}
|
||||
|
||||
check = app_function_latest_runtime_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_function_runtime_is_latest(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_latest_runtime_version.app_function_latest_runtime_version.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_latest_runtime_version.app_function_latest_runtime_version import (
|
||||
app_function_latest_runtime_version,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={"FUNCTIONS_EXTENSION_VERSION": "~4"},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_latest_runtime_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert result[0].status_extended == (
|
||||
"Function function1 is using the latest runtime."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_runtime_is_not_latest(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_latest_runtime_version.app_function_latest_runtime_version.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_latest_runtime_version.app_function_latest_runtime_version import (
|
||||
app_function_latest_runtime_version,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={"FUNCTIONS_EXTENSION_VERSION": "2"},
|
||||
identity=None,
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_latest_runtime_version()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].status_extended == (
|
||||
"Function function1 is not using the latest runtime. The current runtime is '2' and should be '~4'."
|
||||
)
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_app_function_not_publicly_accessible:
|
||||
def test_app_no_subscriptions(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_not_publicly_accessible.app_function_not_publicly_accessible.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_not_publicly_accessible.app_function_not_publicly_accessible import (
|
||||
app_function_not_publicly_accessible,
|
||||
)
|
||||
|
||||
app_client.functions = {}
|
||||
|
||||
check = app_function_not_publicly_accessible()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_subscription_empty(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_not_publicly_accessible.app_function_not_publicly_accessible.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_not_publicly_accessible.app_function_not_publicly_accessible import (
|
||||
app_function_not_publicly_accessible,
|
||||
)
|
||||
|
||||
app_client.functions = {AZURE_SUBSCRIPTION_ID: {}}
|
||||
|
||||
check = app_function_not_publicly_accessible()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_function_not_publicly_accessible(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_not_publicly_accessible.app_function_not_publicly_accessible.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_not_publicly_accessible.app_function_not_publicly_accessible import (
|
||||
app_function_not_publicly_accessible,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=mock.MagicMock(type="SystemAssigned"),
|
||||
public_access=False,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_not_publicly_accessible()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 is not publicly accessible."
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_publicly_accessible(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_not_publicly_accessible.app_function_not_publicly_accessible.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_not_publicly_accessible.app_function_not_publicly_accessible import (
|
||||
app_function_not_publicly_accessible,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=mock.MagicMock(type="SystemAssigned"),
|
||||
public_access=True,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_not_publicly_accessible()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 is publicly accessible."
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].location == "West Europe"
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_app_function_vnet_integration_enabled:
|
||||
def test_app_no_subscriptions(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_vnet_integration_enabled.app_function_vnet_integration_enabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_vnet_integration_enabled.app_function_vnet_integration_enabled import (
|
||||
app_function_vnet_integration_enabled,
|
||||
)
|
||||
|
||||
app_client.functions = {}
|
||||
|
||||
check = app_function_vnet_integration_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_subscription_empty(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_vnet_integration_enabled.app_function_vnet_integration_enabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_vnet_integration_enabled.app_function_vnet_integration_enabled import (
|
||||
app_function_vnet_integration_enabled,
|
||||
)
|
||||
|
||||
app_client.functions = {AZURE_SUBSCRIPTION_ID: {}}
|
||||
|
||||
check = app_function_vnet_integration_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_app_function_vnet_integration_enabled(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_vnet_integration_enabled.app_function_vnet_integration_enabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_vnet_integration_enabled.app_function_vnet_integration_enabled import (
|
||||
app_function_vnet_integration_enabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=None,
|
||||
public_access=True,
|
||||
vnet_subnet_id="vnet_subnet_id",
|
||||
ftps_state="FtpsOnly",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_vnet_integration_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 has Virtual Network integration enabled with subnet 'vnet_subnet_id' enabled."
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].location == "West Europe"
|
||||
|
||||
def test_app_function_vnet_integration_disabled(self):
|
||||
app_client = mock.MagicMock
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
), mock.patch(
|
||||
"prowler.providers.azure.services.app.app_function_vnet_integration_enabled.app_function_vnet_integration_enabled.app_client",
|
||||
new=app_client,
|
||||
):
|
||||
from prowler.providers.azure.services.app.app_function_vnet_integration_enabled.app_function_vnet_integration_enabled import (
|
||||
app_function_vnet_integration_enabled,
|
||||
)
|
||||
from prowler.providers.azure.services.app.app_service import FunctionApp
|
||||
|
||||
function_id = str(uuid4())
|
||||
|
||||
app_client.functions = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
function_id: FunctionApp(
|
||||
name="function1",
|
||||
location="West Europe",
|
||||
kind="functionapp,linux",
|
||||
function_keys={},
|
||||
enviroment_variables={},
|
||||
identity=None,
|
||||
public_access=True,
|
||||
vnet_subnet_id=None,
|
||||
ftps_state="AllAllowed",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
check = app_function_vnet_integration_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Function function1 does not have virtual network integration enabled."
|
||||
)
|
||||
assert result[0].resource_name == "function1"
|
||||
assert result[0].resource_id == function_id
|
||||
assert result[0].location == "West Europe"
|
||||
@@ -8,7 +8,7 @@ from tests.providers.azure.azure_fixtures import (
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
# TODO: we have to fix this test not to use MagicMock but set the App service while mocking the import ot the Monitor client
|
||||
# TODO: we have to fix this test not to use MagicMock but set the App service while mocking the import of the Monitor client
|
||||
# def mock_app_get_apps(_):
|
||||
# return {
|
||||
# AZURE_SUBSCRIPTION_ID: {
|
||||
|
||||
+1
@@ -63,6 +63,7 @@ class Test_appinsights_ensure_is_configured:
|
||||
resource_id="/subscriptions/resource_id",
|
||||
resource_name="AppInsightsTest",
|
||||
location="westeurope",
|
||||
instrumentation_key="",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ def mock_appinsights_get_components(_):
|
||||
resource_id="/subscriptions/resource_id",
|
||||
resource_name="AppInsightsTest",
|
||||
location="westeurope",
|
||||
instrumentation_key="",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user