diff --git a/transformations/azure_compliance/macros/free/iam/custom_subscription_owner_roles.sql b/transformations/azure_compliance/macros/free/iam/custom_subscription_owner_roles.sql index 5384fb511..747583826 100644 --- a/transformations/azure_compliance/macros/free/iam/custom_subscription_owner_roles.sql +++ b/transformations/azure_compliance/macros/free/iam/custom_subscription_owner_roles.sql @@ -1,6 +1,10 @@ {% macro iam_custom_subscription_owner_roles(framework, check_id) %} + {{ return(adapter.dispatch('iam_custom_subscription_owner_roles')(framework, check_id)) }} +{% endmacro %} ---check if definition matches scopes +{% macro default__iam_custom_subscription_owner_roles(framework, check_id) %}{% endmacro %} + +{% macro postgres__iam_custom_subscription_owner_roles(framework, check_id) %} WITH custom_roles AS ( SELECT * FROM azure_authorization_role_definitions @@ -36,6 +40,59 @@ meets_actions AS ( FROM definition_actions GROUP BY _cq_id ) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure That No Custom Subscription Administrator Roles Exist' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN has_wide_scope AND has_all_action + THEN 'fail' + ELSE 'pass' + END AS status +FROM custom_roles + JOIN meets_scopes USING (_cq_id) JOIN meets_actions USING (_cq_id) +{% endmacro %} + +{% macro snowflake__iam_custom_subscription_owner_roles(framework, check_id) %} +--check if definition matches scopes +WITH custom_roles AS ( + SELECT * + FROM azure_authorization_role_definitions + WHERE properties:type = 'CustomRole' +), +assignable_scopes AS ( + SELECT + _cq_id, + value AS assignable_scope + FROM custom_roles, + LATERAL FLATTEN(input => properties:assignableScopes) AS scope +), +meets_scopes AS ( + SELECT + _cq_id, + CASE WHEN MAX(CASE WHEN assignable_scope = '/' OR assignable_scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS has_wide_scope + FROM assignable_scopes + GROUP BY _cq_id +), +--check if definition matches actions +definition_actions AS ( + SELECT + _cq_id, + actions.value AS action + FROM custom_roles, + LATERAL FLATTEN(input => properties:permissions) AS p, + LATERAL FLATTEN(input => p.value:actions) AS actions +), +meets_actions AS ( + SELECT + _cq_id, + CASE WHEN MAX(CASE WHEN action = '*' THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS has_all_action + FROM definition_actions + GROUP BY _cq_id +) SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/free/security/defender_on_for_app_service.sql b/transformations/azure_compliance/macros/free/security/defender_on_for_app_service.sql index f2db6ef3d..fd429a3c4 100644 --- a/transformations/azure_compliance/macros/free/security/defender_on_for_app_service.sql +++ b/transformations/azure_compliance/macros/free/security/defender_on_for_app_service.sql @@ -1,5 +1,10 @@ {% macro security_defender_on_for_app_service(framework, check_id) %} + {{ return(adapter.dispatch('security_defender_on_for_app_service')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_defender_on_for_app_service(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_defender_on_for_app_service(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_security_pricings asp WHERE "name" = 'AppServices' +{% endmacro %} + +{% macro snowflake__security_defender_on_for_app_service(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Azure Defender is set to On for App Service (Automatic)' as title, + subscription_id, + id, + case + when properties:pricingTier = 'Standard' + then 'pass' else 'fail' + end +FROM azure_security_pricings asp +WHERE name = 'AppServices' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/free/security/defender_on_for_container_registeries.sql b/transformations/azure_compliance/macros/free/security/defender_on_for_container_registeries.sql index 86c494453..78f974900 100644 --- a/transformations/azure_compliance/macros/free/security/defender_on_for_container_registeries.sql +++ b/transformations/azure_compliance/macros/free/security/defender_on_for_container_registeries.sql @@ -1,5 +1,10 @@ {% macro security_defender_on_for_container_registeries(framework, check_id) %} + {{ return(adapter.dispatch('security_defender_on_for_container_registeries')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_defender_on_for_container_registeries(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_defender_on_for_container_registeries(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_security_pricings asp WHERE "name" = 'ContainerRegistry' +{% endmacro %} + +{% macro snowflake__security_defender_on_for_container_registeries(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Azure Defender is set to On for Container Registries (Automatic)' as title, + subscription_id, + id, + case + when properties:pricingTier = 'Standard' + then 'pass' else 'fail' + end +FROM azure_security_pricings asp +WHERE name = 'ContainerRegistry' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/free/security/defender_on_for_k8s.sql b/transformations/azure_compliance/macros/free/security/defender_on_for_k8s.sql index 0b3742993..e32077de1 100644 --- a/transformations/azure_compliance/macros/free/security/defender_on_for_k8s.sql +++ b/transformations/azure_compliance/macros/free/security/defender_on_for_k8s.sql @@ -1,5 +1,10 @@ {% macro security_defender_on_for_k8s(framework, check_id) %} + {{ return(adapter.dispatch('security_defender_on_for_k8s')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_defender_on_for_k8s(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_defender_on_for_k8s(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_security_pricings asp WHERE "name" = 'KubernetesService' +{% endmacro %} + +{% macro snowflake__security_defender_on_for_k8s(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Azure Defender is set to On for Kubernetes (Automatic)' as title, + subscription_id, + id, + case + when properties:pricingTier = 'Standard' + then 'pass' else 'fail' + end +FROM azure_security_pricings asp +WHERE name = 'KubernetesService' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/free/security/defender_on_for_key_vault.sql b/transformations/azure_compliance/macros/free/security/defender_on_for_key_vault.sql index d34a808d4..f68cd59d7 100644 --- a/transformations/azure_compliance/macros/free/security/defender_on_for_key_vault.sql +++ b/transformations/azure_compliance/macros/free/security/defender_on_for_key_vault.sql @@ -1,5 +1,10 @@ {% macro security_defender_on_for_key_vault(framework, check_id) %} + {{ return(adapter.dispatch('security_defender_on_for_key_vault')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_defender_on_for_key_vault(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_defender_on_for_key_vault(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_security_pricings asp WHERE "name" = 'KeyVaults' +{% endmacro %} + +{% macro snowflake__security_defender_on_for_key_vault(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Azure Defender is set to On for Key Vault (Manual)' as title, + subscription_id, + id, + case + when properties:pricingTier = 'Standard' + then 'pass' else 'fail' + end +FROM azure_security_pricings asp +WHERE name = 'KeyVaults' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/free/security/defender_on_for_servers.sql b/transformations/azure_compliance/macros/free/security/defender_on_for_servers.sql index 093865afc..e5db1f39b 100644 --- a/transformations/azure_compliance/macros/free/security/defender_on_for_servers.sql +++ b/transformations/azure_compliance/macros/free/security/defender_on_for_servers.sql @@ -1,5 +1,10 @@ {% macro security_defender_on_for_servers(framework, check_id) %} + {{ return(adapter.dispatch('security_defender_on_for_servers')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_defender_on_for_servers(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_defender_on_for_servers(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_security_pricings asp WHERE "name" = 'VirtualMachines' +{% endmacro %} + +{% macro snowflake__security_defender_on_for_servers(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Azure Defender is set to On for Servers (Automatic)' as title, + subscription_id, + id, + case + when properties:pricingTier = 'Standard' + then 'pass' else 'fail' + end +FROM azure_security_pricings asp +WHERE name = 'VirtualMachines' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/free/security/defender_on_for_sql_servers.sql b/transformations/azure_compliance/macros/free/security/defender_on_for_sql_servers.sql index e52a4c33c..e812f107f 100644 --- a/transformations/azure_compliance/macros/free/security/defender_on_for_sql_servers.sql +++ b/transformations/azure_compliance/macros/free/security/defender_on_for_sql_servers.sql @@ -1,5 +1,10 @@ {% macro security_defender_on_for_sql_servers(framework, check_id) %} + {{ return(adapter.dispatch('security_defender_on_for_sql_servers')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_defender_on_for_sql_servers(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_defender_on_for_sql_servers(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_security_pricings asp WHERE "name" = 'SqlServers' -{% endmacro %} \ No newline at end of file +{% endmacro %} + +{% macro snowflake__security_defender_on_for_sql_servers(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Azure Defender is set to On for Azure SQL database servers (Automatic)' as title, + subscription_id, + id, + case + when properties:pricingTier = 'Standard' + then 'pass' else 'fail' + end +FROM azure_security_pricings asp +WHERE name = 'SqlServers' +{% endmacro %} diff --git a/transformations/azure_compliance/macros/free/security/defender_on_for_sql_servers_on_machines.sql b/transformations/azure_compliance/macros/free/security/defender_on_for_sql_servers_on_machines.sql index 5245e4dbe..2c0e7a03d 100644 --- a/transformations/azure_compliance/macros/free/security/defender_on_for_sql_servers_on_machines.sql +++ b/transformations/azure_compliance/macros/free/security/defender_on_for_sql_servers_on_machines.sql @@ -1,5 +1,10 @@ {% macro security_defender_on_for_sql_servers_on_machines(framework, check_id) %} + {{ return(adapter.dispatch('security_defender_on_for_sql_servers_on_machines')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_defender_on_for_sql_servers_on_machines(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_defender_on_for_sql_servers_on_machines(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_security_pricings asp WHERE "name" = 'SqlserverVirtualMachines' +{% endmacro %} + +{% macro snowflake__security_defender_on_for_sql_servers_on_machines(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, +'Ensure that Azure Defender is set to On for SQL servers on machines (Automatic)' as title, + subscription_id, + id, + case + when properties:pricingTier = 'Standard' + then 'pass' else 'fail' + end +FROM azure_security_pricings asp +WHERE name = 'SqlserverVirtualMachines' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/free/security/defender_on_for_storage.sql b/transformations/azure_compliance/macros/free/security/defender_on_for_storage.sql index 40cc6c73a..cfbaf0541 100644 --- a/transformations/azure_compliance/macros/free/security/defender_on_for_storage.sql +++ b/transformations/azure_compliance/macros/free/security/defender_on_for_storage.sql @@ -1,5 +1,10 @@ {% macro security_defender_on_for_storage(framework, check_id) %} + {{ return(adapter.dispatch('security_defender_on_for_storage')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_defender_on_for_storage(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_defender_on_for_storage(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_security_pricings asp WHERE "name" = 'StorageAccounts' +{% endmacro %} + +{% macro snowflake__security_defender_on_for_storage(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Azure Defender is set to On for Storage (Automatic)' as title, + subscription_id, + id, + case + when properties:pricingTier = 'Standard' + then 'pass' else 'fail' + end +FROM azure_security_pricings asp +WHERE name = 'StorageAccounts' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/compute/os_and_data_disks_encrypted_with_cmk.sql b/transformations/azure_compliance/macros/pro/compute/os_and_data_disks_encrypted_with_cmk.sql index 8a263d6eb..03e274cb0 100644 --- a/transformations/azure_compliance/macros/pro/compute/os_and_data_disks_encrypted_with_cmk.sql +++ b/transformations/azure_compliance/macros/pro/compute/os_and_data_disks_encrypted_with_cmk.sql @@ -1,5 +1,10 @@ {% macro compute_os_and_data_disks_encrypted_with_cmk(framework, check_id) %} + {{ return(adapter.dispatch('compute_os_and_data_disks_encrypted_with_cmk')(framework, check_id)) }} +{% endmacro %} +{% macro default__compute_os_and_data_disks_encrypted_with_cmk(framework, check_id) %}{% endmacro %} + +{% macro postgres__compute_os_and_data_disks_encrypted_with_cmk(framework, check_id) %} SELECT v._cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -14,4 +19,21 @@ SELECT v._cq_sync_time As sync_time, FROM azure_compute_virtual_machines v JOIN azure_compute_disks d ON LOWER(v.id) = LOWER(d.properties ->> 'managedBy') - {% endmacro %} \ No newline at end of file +{% endmacro %} + +{% macro snowflake__compute_os_and_data_disks_encrypted_with_cmk(framework, check_id) %} +SELECT v._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that ''OS and Data'' disks are encrypted with CMK (Automated)' AS title, + v.subscription_id AS subscription_id, + v.id AS resource_id, + CASE + WHEN d.properties:encryption:type NOT LIKE '%CustomerKey%' + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_compute_virtual_machines v + JOIN azure_compute_disks d ON + LOWER(v.id) = LOWER(d.properties:managedBy) +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/compute/unattached_disks_are_encrypted_with_cmk.sql b/transformations/azure_compliance/macros/pro/compute/unattached_disks_are_encrypted_with_cmk.sql index d6f99b9a3..e22db7901 100644 --- a/transformations/azure_compliance/macros/pro/compute/unattached_disks_are_encrypted_with_cmk.sql +++ b/transformations/azure_compliance/macros/pro/compute/unattached_disks_are_encrypted_with_cmk.sql @@ -1,5 +1,10 @@ {% macro compute_unattached_disks_are_encrypted_with_cmk(framework, check_id) %} + {{ return(adapter.dispatch('compute_unattached_disks_are_encrypted_with_cmk')(framework, check_id)) }} +{% endmacro %} +{% macro default__compute_unattached_disks_are_encrypted_with_cmk(framework, check_id) %}{% endmacro %} + +{% macro postgres__compute_unattached_disks_are_encrypted_with_cmk(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -13,4 +18,20 @@ SELECT _cq_sync_time As sync_time, END AS status FROM azure_compute_disks WHERE properties ->> 'diskState' = 'Unattached' +{% endmacro %} + +{% macro snowflake__compute_unattached_disks_are_encrypted_with_cmk(framework, check_id) %} +SELECT _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that ''Unattached disks'' are encrypted with CMK (Automated)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN properties:encryption:type NOT LIKE '%CustomerKey%' + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_compute_disks +WHERE properties:diskState = 'Unattached' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/compute/vhds_not_encrypted.sql b/transformations/azure_compliance/macros/pro/compute/vhds_not_encrypted.sql index 08822a720..68976c601 100644 --- a/transformations/azure_compliance/macros/pro/compute/vhds_not_encrypted.sql +++ b/transformations/azure_compliance/macros/pro/compute/vhds_not_encrypted.sql @@ -1,5 +1,10 @@ {% macro compute_vhds_not_encrypted(framework, check_id) %} + {{ return(adapter.dispatch('compute_vhds_not_encrypted')(framework, check_id)) }} +{% endmacro %} +{% macro default__compute_vhds_not_encrypted(framework, check_id) %}{% endmacro %} + +{% macro postgres__compute_vhds_not_encrypted(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -12,4 +17,19 @@ SELECT _cq_sync_time As sync_time, ELSE 'pass' END AS status FROM azure_compute_disks +{% endmacro %} + +{% macro snowflake__compute_vhds_not_encrypted(framework, check_id) %} +SELECT _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that VHD''s are encrypted (Manual)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN (properties:encryptionSettingsCollection:enabled)::boolean IS DISTINCT FROM TRUE + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_compute_disks {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/compute/vms_utilizing_managed_disks.sql b/transformations/azure_compliance/macros/pro/compute/vms_utilizing_managed_disks.sql index 352c17dee..11cf6d366 100644 --- a/transformations/azure_compliance/macros/pro/compute/vms_utilizing_managed_disks.sql +++ b/transformations/azure_compliance/macros/pro/compute/vms_utilizing_managed_disks.sql @@ -1,5 +1,10 @@ {% macro compute_vms_utilizing_managed_disks(framework, check_id) %} + {{ return(adapter.dispatch('compute_vms_utilizing_managed_disks')(framework, check_id)) }} +{% endmacro %} +{% macro default__compute_vms_utilizing_managed_disks(framework, check_id) %}{% endmacro %} + +{% macro postgres__compute_vms_utilizing_managed_disks(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -12,4 +17,19 @@ SELECT _cq_sync_time As sync_time, ELSE 'pass' END AS status FROM azure_compute_virtual_machines +{% endmacro %} + +{% macro snowflake__compute_vms_utilizing_managed_disks(framework, check_id) %} +SELECT _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure Virtual Machines are utilizing Managed Disks (Manual)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN properties:storageProfile:osDisk:managedDisk:id IS NULL + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_compute_virtual_machines {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/container/aks_rbac_disabled.sql b/transformations/azure_compliance/macros/pro/container/aks_rbac_disabled.sql index 28a45bfd8..83d116c74 100644 --- a/transformations/azure_compliance/macros/pro/container/aks_rbac_disabled.sql +++ b/transformations/azure_compliance/macros/pro/container/aks_rbac_disabled.sql @@ -1,5 +1,10 @@ {% macro container_aks_rbac_disabled(framework, check_id) %} + {{ return(adapter.dispatch('container_aks_rbac_disabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__container_aks_rbac_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__container_aks_rbac_disabled(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT ELSE 'pass' END AS status FROM azure_containerservice_managed_clusters +{% endmacro %} + +{% macro snowflake__container_aks_rbac_disabled(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Role-Based Access Control (RBAC) should be used on Kubernetes Services' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN (properties:enableRBAC)::boolean IS distinct from TRUE + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_containerservice_managed_clusters {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/keyvault/keys_without_expiration_date.sql b/transformations/azure_compliance/macros/pro/keyvault/keys_without_expiration_date.sql index c3f2ceeba..cd6db68e6 100644 --- a/transformations/azure_compliance/macros/pro/keyvault/keys_without_expiration_date.sql +++ b/transformations/azure_compliance/macros/pro/keyvault/keys_without_expiration_date.sql @@ -1,5 +1,10 @@ {% macro keyvault_keys_without_expiration_date(framework, check_id) %} + {{ return(adapter.dispatch('keyvault_keys_without_expiration_date')(framework, check_id)) }} +{% endmacro %} +{% macro default__keyvault_keys_without_expiration_date(framework, check_id) %}{% endmacro %} + +{% macro postgres__keyvault_keys_without_expiration_date(framework, check_id) %} SELECT akv._cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -15,4 +20,22 @@ SELECT akv._cq_sync_time As sync_time, FROM azure_keyvault_keyvault akv JOIN azure_keyvault_keyvault_keys akvk ON akv._cq_id = akvk._cq_parent_id +{% endmacro %} + +{% macro snowflake__keyvault_keys_without_expiration_date(framework, check_id) %} +SELECT akv._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that the expiration date is set on all keys (Automated)' AS title, + akv.subscription_id AS subscription_id, + akvk.id AS resource_id, + CASE + WHEN (akvk.properties:attributes:enabled)::boolean = TRUE + AND (akvk.properties:attributes:exp) IS NULL + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_keyvault_keyvault akv + JOIN azure_keyvault_keyvault_keys akvk + ON akv._cq_id = akvk._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/keyvault/not_recoverable.sql b/transformations/azure_compliance/macros/pro/keyvault/not_recoverable.sql index 64b7dcd02..50ac62218 100644 --- a/transformations/azure_compliance/macros/pro/keyvault/not_recoverable.sql +++ b/transformations/azure_compliance/macros/pro/keyvault/not_recoverable.sql @@ -1,5 +1,10 @@ {% macro keyvault_not_recoverable(framework, check_id) %} + {{ return(adapter.dispatch('keyvault_not_recoverable')(framework, check_id)) }} +{% endmacro %} +{% macro default__keyvault_not_recoverable(framework, check_id) %}{% endmacro %} + +{% macro postgres__keyvault_not_recoverable(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -12,4 +17,19 @@ SELECT _cq_sync_time As sync_time, ELSE 'pass' END AS status FROM azure_keyvault_keyvault +{% endmacro %} + +{% macro snowflake__keyvault_not_recoverable(framework, check_id) %} +SELECT _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure the key vault is recoverable (Automated)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN (properties:enableSoftDelete::boolean != TRUE) OR (properties:enablePurgeProtection::boolean != TRUE) + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_keyvault_keyvault {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/keyvault/secrets_without_expiration_date.sql b/transformations/azure_compliance/macros/pro/keyvault/secrets_without_expiration_date.sql index 69e4446ed..fb71355d3 100644 --- a/transformations/azure_compliance/macros/pro/keyvault/secrets_without_expiration_date.sql +++ b/transformations/azure_compliance/macros/pro/keyvault/secrets_without_expiration_date.sql @@ -1,5 +1,10 @@ {% macro keyvault_secrets_without_expiration_date(framework, check_id) %} + {{ return(adapter.dispatch('keyvault_secrets_without_expiration_date')(framework, check_id)) }} +{% endmacro %} +{% macro default__keyvault_secrets_without_expiration_date(framework, check_id) %}{% endmacro %} + +{% macro postgres__keyvault_secrets_without_expiration_date(framework, check_id) %} SELECT akv._cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -15,4 +20,22 @@ SELECT akv._cq_sync_time As sync_time, FROM azure_keyvault_keyvault akv JOIN azure_keyvault_keyvault_secrets akvs ON akv._cq_id = akvs._cq_parent_id +{% endmacro %} + +{% macro snowflake__keyvault_secrets_without_expiration_date(framework, check_id) %} +SELECT akv._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that the expiration date is set on all Secrets (Automated)' AS title, + akv.subscription_id AS subscription_id, + akvs.id AS resource_id, + CASE + WHEN (akvs.properties:attributes:enabled)::boolean = TRUE + AND (akvs.properties:attributes:exp) IS NULL + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_keyvault_keyvault akv + JOIN azure_keyvault_keyvault_secrets akvs + ON akv._cq_id = akvs._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/diagnostic_logs_for_all_services.sql b/transformations/azure_compliance/macros/pro/monitor/diagnostic_logs_for_all_services.sql index 249feeac1..e67ef9b0b 100644 --- a/transformations/azure_compliance/macros/pro/monitor/diagnostic_logs_for_all_services.sql +++ b/transformations/azure_compliance/macros/pro/monitor/diagnostic_logs_for_all_services.sql @@ -1,5 +1,28 @@ {% macro monitor_diagnostic_logs_for_all_services(framework, check_id) %} + {{ return(adapter.dispatch('monitor_diagnostic_logs_for_all_services')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_diagnostic_logs_for_all_services(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_diagnostic_logs_for_all_services(framework, check_id) %} +SELECT + amr._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Diagnostic Logs are enabled for all services which support it.' AS title, + amr.subscription_id AS subscription_id, + amr.id AS resource_id, + CASE + WHEN amds.id IS DISTINCT FROM NULL + THEN 'pass' + ELSE 'fail' + END AS status +FROM azure_monitor_resources AS amr + LEFT JOIN azure_monitor_diagnostic_settings AS amds + ON amr._cq_id = amds._cq_parent_id +{% endmacro %} + +{% macro snowflake__monitor_diagnostic_logs_for_all_services(framework, check_id) %} SELECT amr._cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/pro/monitor/insufficient_diagnostic_capturing_settings.sql b/transformations/azure_compliance/macros/pro/monitor/insufficient_diagnostic_capturing_settings.sql index abd7798f1..e8d0378a0 100644 --- a/transformations/azure_compliance/macros/pro/monitor/insufficient_diagnostic_capturing_settings.sql +++ b/transformations/azure_compliance/macros/pro/monitor/insufficient_diagnostic_capturing_settings.sql @@ -1,5 +1,10 @@ {% macro monitor_insufficient_diagnostic_capturing_settings(framework, check_id) %} + {{ return(adapter.dispatch('monitor_insufficient_diagnostic_capturing_settings')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_insufficient_diagnostic_capturing_settings(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_insufficient_diagnostic_capturing_settings(framework, check_id) %} WITH diagnostic_settings AS ( SELECT _cq_sync_time, @@ -31,4 +36,37 @@ SELECT FROM required_settings WHERE enabled GROUP BY _cq_sync_time, subscription_id, id +{% endmacro %} + +{% macro snowflake__monitor_insufficient_diagnostic_capturing_settings(framework, check_id) %} + WITH diagnostic_settings AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + (value:enabled)::boolean AS enabled, + value:category AS category + FROM azure_monitor_subscription_diagnostic_settings, + LATERAL FLATTEN(input => properties:logs) AS logs + ), + required_settings AS ( + SELECT * + FROM diagnostic_settings + WHERE category IN ('Administrative', 'Alert', 'Policy', 'Security') + ) + SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure Diagnostic Setting captures appropriate categories' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN COUNT(id) = 4 + THEN 'pass' + ELSE 'fail' + END AS status + FROM required_settings + WHERE enabled + GROUP BY _cq_sync_time, subscription_id, id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_network_sg.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_network_sg.sql index a30bcdb8f..61dc14bce 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_network_sg.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_network_sg.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_create_or_update_network_sg(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_create_or_update_network_sg')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_create_or_update_network_sg(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_create_or_update_network_sg(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -43,4 +48,52 @@ SELECT bool_or(condition)::text AS status FROM conditions GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_create_or_update_network_sg(framework, check_id) %} + WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Network/networkSecurityGroups/write' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Create or Update Network Security Group' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_network_sg_rule.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_network_sg_rule.sql index 3fedf5140..4d770ba0c 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_network_sg_rule.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_network_sg_rule.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_create_or_update_network_sg_rule(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_create_or_update_network_sg_rule')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_create_or_update_network_sg_rule(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_create_or_update_network_sg_rule(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -43,4 +48,52 @@ SELECT bool_or(condition)::text AS status FROM conditions GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_create_or_update_network_sg_rule(framework, check_id) %} +WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Network/networkSecurityGroups/securityRules/write' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Create or Update Network Security Group Rule' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_or_delete_sql_server_firewall_rule.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_or_delete_sql_server_firewall_rule.sql index 59a413c29..ce60197e1 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_or_delete_sql_server_firewall_rule.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_or_delete_sql_server_firewall_rule.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_create_or_update_or_delete_sql_server_firewall_rule(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_create_or_update_or_delete_sql_server_firewall_rule')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_create_or_update_or_delete_sql_server_firewall_rule(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_create_or_update_or_delete_sql_server_firewall_rule(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -42,4 +47,53 @@ SELECT scope AS resrouce_id, bool_or(condition)::text AS status FROM conditions -GROUP BY _cq_sync_time, subscription_id, scope{% endmacro %} \ No newline at end of file +GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_create_or_update_or_delete_sql_server_firewall_rule(framework, check_id) %} +WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Sql/servers/firewallRules/write' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Create or Update or Delete SQL Server Firewall Rule' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_security_solution.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_security_solution.sql index be70bdfc6..59b9e7e73 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_security_solution.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_or_update_security_solution.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_create_or_update_security_solution(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_create_or_update_security_solution')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_create_or_update_security_solution(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_create_or_update_security_solution(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -42,4 +47,53 @@ SELECT scope AS resrouce_id, bool_or(condition)::text AS status FROM conditions -GROUP BY _cq_sync_time, subscription_id, scope{% endmacro %} \ No newline at end of file +GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_create_or_update_security_solution(framework, check_id) %} +WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Security/securitySolutions/write' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Create or Update Security Solution' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_policy_assignment.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_policy_assignment.sql index 6db078ed0..d8a36d24c 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_policy_assignment.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_create_policy_assignment.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_create_policy_assignment(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_create_policy_assignment')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_create_policy_assignment(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_create_policy_assignment(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -43,4 +48,52 @@ SELECT bool_or(condition)::text AS status FROM conditions GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_create_policy_assignment(framework, check_id) %} +WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Security/policies/write' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Create Policy Assignment' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_network_sg.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_network_sg.sql index c5a26ab05..604513c05 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_network_sg.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_network_sg.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_delete_network_sg(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_delete_network_sg')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_delete_network_sg(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_delete_network_sg(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -43,4 +48,52 @@ SELECT bool_or(condition)::text AS status FROM conditions GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_delete_network_sg(framework, check_id) %} +WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Network/networkSecurityGroups/delete' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Delete Network Security Group' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_network_sg_rule.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_network_sg_rule.sql index 36cf5f5b5..5f53edb74 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_network_sg_rule.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_network_sg_rule.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_delete_network_sg_rule(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_delete_network_sg_rule')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_delete_network_sg_rule(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_delete_network_sg_rule(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -42,4 +47,53 @@ SELECT scope AS resrouce_id, bool_or(condition)::text AS status FROM conditions -GROUP BY _cq_sync_time, subscription_id, scope{% endmacro %} \ No newline at end of file +GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_delete_network_sg_rule(framework, check_id) %} +WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Network/networkSecurityGroups/securityRules/delete' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Delete Network Security Group Rule' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_policy_assignment.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_policy_assignment.sql index f2fd102a0..81bab865d 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_policy_assignment.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_policy_assignment.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_delete_policy_assignment(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_delete_policy_assignment')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_delete_policy_assignment(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_delete_policy_assignment(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -42,4 +47,53 @@ SELECT scope AS resrouce_id, bool_or(condition)::text AS status FROM conditions -GROUP BY _cq_sync_time, subscription_id, scope{% endmacro %} \ No newline at end of file +GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_delete_policy_assignment(framework, check_id) %} +WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Security/policies/delete' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Delete Policy Assignment' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_security_solution.sql b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_security_solution.sql index ba9b7eea1..3a93eca6f 100644 --- a/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_security_solution.sql +++ b/transformations/azure_compliance/macros/pro/monitor/log_alert_for_delete_security_solution.sql @@ -1,5 +1,10 @@ {% macro monitor_log_alert_for_delete_security_solution(framework, check_id) %} + {{ return(adapter.dispatch('monitor_log_alert_for_delete_security_solution')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_log_alert_for_delete_security_solution(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_log_alert_for_delete_security_solution(framework, check_id) %} WITH fields AS ( SELECT _cq_sync_time, @@ -43,4 +48,52 @@ SELECT bool_or(condition)::text AS status FROM conditions GROUP BY _cq_sync_time, subscription_id, scope +{% endmacro %} + +{% macro snowflake__monitor_log_alert_for_delete_security_solution(framework, check_id) %} +WITH fields AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + location, + (properties:enabled)::boolean AS enabled, + value:field AS field, + field:equals AS equals + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:condition:allOf) AS conditions +), +scopes AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + value as scope + FROM azure_monitor_activity_log_alerts, + LATERAL FLATTEN(input => properties:scopes) AS scope +), +conditions AS ( + SELECT + fields._cq_sync_time, + fields.subscription_id AS subscription_id, + fields.id AS id, + scopes.scope AS scope, + location = 'global' + AND enabled + AND equals = 'Microsoft.Security/securitySolutions/delete' + AND scopes.scope REGEXP '^\/subscriptions\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$' + AS condition + FROM fields JOIN scopes ON fields.id = scopes.id + WHERE field = 'operationName' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Activity Log Alert exists for Delete Security Solution' AS title, + subscription_id AS subscription_id, + scope AS resrouce_id, + CASE WHEN MAX(CASE WHEN condition THEN 1 ELSE 0 END) = 1 THEN 'pass' ELSE 'fail' END AS status +FROM conditions +GROUP BY _cq_sync_time, subscription_id, scope {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/logging_key_valut_is_enabled.sql b/transformations/azure_compliance/macros/pro/monitor/logging_key_valut_is_enabled.sql index fd492db1d..bd52d7369 100644 --- a/transformations/azure_compliance/macros/pro/monitor/logging_key_valut_is_enabled.sql +++ b/transformations/azure_compliance/macros/pro/monitor/logging_key_valut_is_enabled.sql @@ -1,5 +1,10 @@ {% macro monitor_logging_key_valut_is_enabled(framework, check_id) %} + {{ return(adapter.dispatch('monitor_logging_key_valut_is_enabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_logging_key_valut_is_enabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_logging_key_valut_is_enabled(framework, check_id) %} WITH diagnosis_logs AS ( SELECT amr._cq_sync_time, @@ -25,4 +30,32 @@ SELECT ELSE 'fail' END AS status FROM diagnosis_logs +{% endmacro %} + +{% macro snowflake__monitor_logging_key_valut_is_enabled(framework, check_id) %} +WITH diagnosis_logs AS ( + SELECT + amr._cq_sync_time, + amds.subscription_id, + amds.id || '/' || (coalesce(value:category, value:categoryGroup))::text AS id, + value:category IS DISTINCT FROM NULL AS hasCategory, + (value:retentionPolicy:days)::int >= 180 AS satisfyRetentionDays + FROM azure_monitor_resources as amr + LEFT JOIN azure_monitor_diagnostic_settings as amds ON amr._cq_id = amds._cq_parent_id, + LATERAL FLATTEN(input => properties:logs) AS logs + WHERE amr.type = 'Microsoft.KeyVault/vaults' +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that logging for Azure Key Vault is ''Enabled''' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN hasCategory AND satisfyRetentionDays + THEN 'pass' + ELSE 'fail' + END AS status +FROM diagnosis_logs {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/monitor/no_diagnostic_setting.sql b/transformations/azure_compliance/macros/pro/monitor/no_diagnostic_setting.sql index 8e848b989..5f344da9d 100644 --- a/transformations/azure_compliance/macros/pro/monitor/no_diagnostic_setting.sql +++ b/transformations/azure_compliance/macros/pro/monitor/no_diagnostic_setting.sql @@ -1,5 +1,29 @@ {% macro monitor_no_diagnostic_setting(framework, check_id) %} + {{ return(adapter.dispatch('monitor_no_diagnostic_setting')(framework, check_id)) }} +{% endmacro %} +{% macro default__monitor_no_diagnostic_setting(framework, check_id) %}{% endmacro %} + +{% macro postgres__monitor_no_diagnostic_setting(framework, check_id) %} +SELECT + amr._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that a ''Diagnostics Setting'' exists' AS title, + amr.subscription_id AS subscription_id, + amr.id AS resource_id, + CASE + WHEN amds.properties IS NULL + THEN 'fail' + ELSE 'pass' + END AS status +FROM + azure_monitor_resources AS amr + LEFT JOIN azure_monitor_diagnostic_settings AS amds + ON amr._cq_id = amds._cq_parent_id +{% endmacro %} + +{% macro snowflake__monitor_no_diagnostic_setting(framework, check_id) %} SELECT amr._cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/pro/network/nsg_log_retention_period.sql b/transformations/azure_compliance/macros/pro/network/nsg_log_retention_period.sql index 986c3b659..f5e95dd60 100644 --- a/transformations/azure_compliance/macros/pro/network/nsg_log_retention_period.sql +++ b/transformations/azure_compliance/macros/pro/network/nsg_log_retention_period.sql @@ -1,5 +1,10 @@ {% macro network_nsg_log_retention_period(framework, check_id) %} + {{ return(adapter.dispatch('network_nsg_log_retention_period')(framework, check_id)) }} +{% endmacro %} +{% macro default__network_nsg_log_retention_period(framework, check_id) %}{% endmacro %} + +{% macro postgres__network_nsg_log_retention_period(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT ELSE 'fail' END AS status FROM azure_network_watcher_flow_logs +{% endmacro %} + +{% macro snowflake__network_nsg_log_retention_period(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Network Security Group Flow Log retention period is ''greater than 90 days''' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN (properties:retentionPolicy:days)::INT >= 90 + THEN 'pass' + ELSE 'fail' + END AS status +FROM azure_network_watcher_flow_logs {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/network/rdp_services_are_restricted_from_the_internet.sql b/transformations/azure_compliance/macros/pro/network/rdp_services_are_restricted_from_the_internet.sql index b6442b5d5..2335eb9f3 100644 --- a/transformations/azure_compliance/macros/pro/network/rdp_services_are_restricted_from_the_internet.sql +++ b/transformations/azure_compliance/macros/pro/network/rdp_services_are_restricted_from_the_internet.sql @@ -1,5 +1,10 @@ {% macro network_rdp_services_are_restricted_from_the_internet(framework, check_id) %} + {{ return(adapter.dispatch('network_rdp_services_are_restricted_from_the_internet')(framework, check_id)) }} +{% endmacro %} +{% macro default__network_rdp_services_are_restricted_from_the_internet(framework, check_id) %}{% endmacro %} + +{% macro postgres__network_rdp_services_are_restricted_from_the_internet(framework, check_id) %} WITH conditions AS ( SELECT _cq_sync_time, @@ -10,7 +15,7 @@ WITH conditions AS ( direction = 'Inbound' AS isInbound, sourceAddressPrefix IN ('*', '0.0.0.0', '/0', '/0', 'internet', 'any') AS matchPrefix, 3389 BETWEEN start_dest_port AND end_dest_port AS inRange - FROM view_azure_nsg_dest_port_ranges + FROM {{ ref('view_azure_nsg_dest_port_ranges') }} ), statuses_by_port_range AS ( SELECT @@ -38,4 +43,45 @@ SELECT ELSE 'pass' END AS status FROM statuses +{% endmacro %} + +{% macro snowflake__network_rdp_services_are_restricted_from_the_internet(framework, check_id) %} +WITH conditions AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + access = 'Allow' AS acceptAccess, + protocol = '*' OR upper(protocol) = 'TCP' AS matchProtocol, + direction = 'Inbound' AS isInbound, + sourceAddressPrefix IN ('*', '0.0.0.0', '/0', '/0', 'internet', 'any') AS matchPrefix, + 3389 BETWEEN start_dest_port AND end_dest_port AS inRange + FROM view_azure_nsg_dest_port_ranges +), +statuses_by_port_range AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + acceptAccess AND matchProtocol AND isInbound AND matchPrefix AND inRange AS failed + FROM conditions +), +statuses AS ( + SELECT _cq_sync_time, subscription_id, id, BOOLOR_AGG(failed) AS failed + FROM statuses_by_port_range + GROUP BY _cq_sync_time, subscription_id, id +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that RDP access is restricted from the Internet' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN failed + THEN 'fail' + ELSE 'pass' + END AS status +FROM statuses {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/network/ssh_services_are_restricted_from_the_internet.sql b/transformations/azure_compliance/macros/pro/network/ssh_services_are_restricted_from_the_internet.sql index cc197012e..1a22d2480 100644 --- a/transformations/azure_compliance/macros/pro/network/ssh_services_are_restricted_from_the_internet.sql +++ b/transformations/azure_compliance/macros/pro/network/ssh_services_are_restricted_from_the_internet.sql @@ -1,5 +1,10 @@ {% macro network_ssh_services_are_restricted_from_the_internet(framework, check_id) %} + {{ return(adapter.dispatch('network_ssh_services_are_restricted_from_the_internet')(framework, check_id)) }} +{% endmacro %} +{% macro default__network_ssh_services_are_restricted_from_the_internet(framework, check_id) %}{% endmacro %} + +{% macro postgres__network_ssh_services_are_restricted_from_the_internet(framework, check_id) %} WITH conditions AS ( SELECT _cq_sync_time, @@ -38,4 +43,45 @@ SELECT ELSE 'pass' END AS status FROM statuses +{% endmacro %} + +{% macro snowflake__network_ssh_services_are_restricted_from_the_internet(framework, check_id) %} +WITH conditions AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + access = 'Allow' AS acceptAccess, + protocol = '*' OR upper(protocol) = 'TCP' AS matchProtocol, + direction = 'Inbound' AS isInbound, + sourceAddressPrefix IN ('*', '0.0.0.0', '/0', '/0', 'internet', 'any') AS matchPrefix, + 22 BETWEEN start_dest_port AND end_dest_port AS inRange + FROM {{ ref('view_azure_nsg_dest_port_ranges') }} +), +statuses_by_port_range AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + acceptAccess AND matchProtocol AND isInbound AND matchPrefix AND inRange AS failed + FROM conditions +), +statuses AS ( + SELECT _cq_sync_time, subscription_id, id, BOOLOR_AGG(failed) AS failed + FROM statuses_by_port_range + GROUP BY _cq_sync_time, subscription_id, id +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that SSH access is restricted from the Internet' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN failed + THEN 'fail' + ELSE 'pass' + END AS status +FROM statuses {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/network/udp_services_are_restricted_from_the_internet.sql b/transformations/azure_compliance/macros/pro/network/udp_services_are_restricted_from_the_internet.sql index ca07aa0a4..2f89259fb 100644 --- a/transformations/azure_compliance/macros/pro/network/udp_services_are_restricted_from_the_internet.sql +++ b/transformations/azure_compliance/macros/pro/network/udp_services_are_restricted_from_the_internet.sql @@ -1,5 +1,10 @@ {% macro network_udp_services_are_restricted_from_the_internet(framework, check_id) %} + {{ return(adapter.dispatch('network_udp_services_are_restricted_from_the_internet')(framework, check_id)) }} +{% endmacro %} +{% macro default__network_udp_services_are_restricted_from_the_internet(framework, check_id) %}{% endmacro %} + +{% macro postgres__network_udp_services_are_restricted_from_the_internet(framework, check_id) %} WITH conditions AS ( SELECT _cq_sync_time, @@ -41,4 +46,54 @@ SELECT ELSE 'pass' END AS status FROM statuses +{% endmacro %} + +{% macro snowflake__network_udp_services_are_restricted_from_the_internet(framework, check_id) %} +WITH conditions AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + access = 'Allow' AS acceptAccess, + protocol = '*' OR upper(protocol) = 'UDP' AS matchProtocol, + direction = 'Inbound' AS isInbound, + sourceAddressPrefix IN ('*', '0.0.0.0', '/0', '/0', 'internet', 'any') AS matchPrefix, + CASE + WHEN + 53 BETWEEN start_dest_port AND end_dest_port + OR 123 BETWEEN start_dest_port AND end_dest_port + OR 161 BETWEEN start_dest_port AND end_dest_port + OR 389 BETWEEN start_dest_port AND end_dest_port + OR 1900 BETWEEN start_dest_port AND end_dest_port + THEN TRUE + ELSE FALSE + END AS inRange + FROM view_azure_nsg_dest_port_ranges +), +statuses_by_port_range AS ( + SELECT + _cq_sync_time, + subscription_id, + id, + acceptAccess AND matchProtocol AND isInbound AND matchPrefix AND inRange AS failed + FROM conditions +), +statuses AS ( + SELECT _cq_sync_time, subscription_id, id, BOOLOR_AGG(failed) AS failed + FROM statuses_by_port_range + GROUP BY _cq_sync_time, subscription_id, id +) +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that UDP Services are restricted from the Internet' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN failed + THEN 'fail' + ELSE 'pass' + END AS status +FROM statuses {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/security/auto_provisioning_monitoring_agent_enabled.sql b/transformations/azure_compliance/macros/pro/security/auto_provisioning_monitoring_agent_enabled.sql index 75a416603..2818583f1 100644 --- a/transformations/azure_compliance/macros/pro/security/auto_provisioning_monitoring_agent_enabled.sql +++ b/transformations/azure_compliance/macros/pro/security/auto_provisioning_monitoring_agent_enabled.sql @@ -1,5 +1,10 @@ {% macro security_auto_provisioning_monitoring_agent_enabled(framework, check_id) %} + {{ return(adapter.dispatch('security_auto_provisioning_monitoring_agent_enabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__security_auto_provisioning_monitoring_agent_enabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__security_auto_provisioning_monitoring_agent_enabled(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -12,4 +17,21 @@ SELECT then 'pass' else 'fail' end FROM azure_security_auto_provisioning_settings asaps -WHERE "name" = 'default'{% endmacro %} \ No newline at end of file +WHERE "name" = 'default' +{% endmacro %} + +{% macro snowflake__security_auto_provisioning_monitoring_agent_enabled(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that "Automatic provisioning of monitoring agent" is set to "On" (Automated)' as title, + subscription_id, + id, + case + when properties:autoProvision = 'On' + then 'pass' else 'fail' + end +FROM azure_security_auto_provisioning_settings asaps +WHERE name = 'default' +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/ad_admin_configured.sql b/transformations/azure_compliance/macros/pro/sql/ad_admin_configured.sql index 52b022b62..17f357bfc 100644 --- a/transformations/azure_compliance/macros/pro/sql/ad_admin_configured.sql +++ b/transformations/azure_compliance/macros/pro/sql/ad_admin_configured.sql @@ -1,4 +1,10 @@ {% macro sql_ad_admin_configured(framework, check_id) %} + {{ return(adapter.dispatch('sql_ad_admin_configured')(framework, check_id)) }} +{% endmacro %} + +{% macro default__sql_ad_admin_configured(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_ad_admin_configured(framework, check_id) %} WITH ad_admins_count AS ( SELECT ass._cq_id, count(*) AS admins_count FROM azure_sql_servers ass LEFT JOIN azure_sql_server_admins assa ON @@ -6,6 +12,31 @@ WITH ad_admins_count AS ( SELECT ass._cq_id, count(*) AS admins_count assa.properties->>'administratorType' ) +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Azure Active Directory Admin is configured (Automated)' as title, + s.subscription_id, + s.id, + case + when a.admins_count IS NULL + OR a.admins_count = 0 + then 'fail' else 'pass' + end +FROM azure_sql_servers s + LEFT JOIN ad_admins_count a ON + s._cq_id = a._cq_id +{% endmacro %} + +{% macro snowflake__sql_ad_admin_configured(framework, check_id) %} +WITH ad_admins_count AS ( SELECT ass._cq_id, count(*) AS admins_count + FROM azure_sql_servers ass + LEFT JOIN azure_sql_server_admins assa ON + ass._cq_id = assa._cq_parent_id WHERE assa.properties:administratorType = 'ActiveDirectory' GROUP BY ass._cq_id, + assa.properties:administratorType +) + SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/pro/sql/atp_on_sql_server_disabled.sql b/transformations/azure_compliance/macros/pro/sql/atp_on_sql_server_disabled.sql index a6452a108..1487eb56c 100644 --- a/transformations/azure_compliance/macros/pro/sql/atp_on_sql_server_disabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/atp_on_sql_server_disabled.sql @@ -1,5 +1,10 @@ {% macro sql_atp_on_sql_server_disabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_atp_on_sql_server_disabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_atp_on_sql_server_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_atp_on_sql_server_disabled(framework, check_id) %} SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT end FROM azure_sql_servers s JOIN azure_sql_server_advanced_threat_protection_settings atp ON s._cq_id = atp._cq_parent_id +{% endmacro %} + +{% macro snowflake__sql_atp_on_sql_server_disabled(framework, check_id) %} +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Advanced Threat Protection (ATP) on a SQL server is set to "Enabled" (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when atp.properties:state is distinct from 'Enabled' + then 'fail' else 'pass' + end +FROM azure_sql_servers s + JOIN azure_sql_server_advanced_threat_protection_settings atp ON s._cq_id = atp._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/auditing_off.sql b/transformations/azure_compliance/macros/pro/sql/auditing_off.sql index 7557eb07b..7f6c6f8b1 100644 --- a/transformations/azure_compliance/macros/pro/sql/auditing_off.sql +++ b/transformations/azure_compliance/macros/pro/sql/auditing_off.sql @@ -1,5 +1,10 @@ {% macro sql_auditing_off(framework, check_id) %} + {{ return(adapter.dispatch('sql_auditing_off')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_auditing_off(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_auditing_off(framework, check_id) %} SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -17,4 +22,24 @@ FROM azure_sql_servers s s._cq_id = assd._cq_parent_id LEFT JOIN azure_sql_server_database_blob_auditing_policies assdbap ON assd._cq_id = assdbap._cq_parent_id +{% endmacro %} + +{% macro snowflake__sql_auditing_off(framework, check_id) %} +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that "Auditing" is set to "On" (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when assdbap.properties:state != 'Enabled' + then 'fail' + else 'pass' + end +FROM azure_sql_servers s + LEFT JOIN azure_sql_server_databases assd ON + s._cq_id = assd._cq_parent_id + LEFT JOIN azure_sql_server_database_blob_auditing_policies assdbap ON + assd._cq_id = assdbap._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/auditing_retention_less_than_90_days.sql b/transformations/azure_compliance/macros/pro/sql/auditing_retention_less_than_90_days.sql index e811f065c..644835f0d 100644 --- a/transformations/azure_compliance/macros/pro/sql/auditing_retention_less_than_90_days.sql +++ b/transformations/azure_compliance/macros/pro/sql/auditing_retention_less_than_90_days.sql @@ -1,5 +1,10 @@ {% macro sql_auditing_retention_less_than_90_days(framework, check_id) %} + {{ return(adapter.dispatch('sql_auditing_retention_less_than_90_days')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_auditing_retention_less_than_90_days(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_auditing_retention_less_than_90_days(framework, check_id) %} SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -14,4 +19,21 @@ SELECT FROM azure_sql_servers s LEFT JOIN azure_sql_server_blob_auditing_policies assdbap ON s._cq_id = assdbap._cq_parent_id +{% endmacro %} + +{% macro snowflake__sql_auditing_retention_less_than_90_days(framework, check_id) %} +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that "Auditing" Retention is "greater than 90 days" (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when (assdbap.properties:retentionDays)::int < 90 + then 'fail' else 'pass' + end +FROM azure_sql_servers s + LEFT JOIN azure_sql_server_blob_auditing_policies assdbap ON + s._cq_id = assdbap._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/data_encryption_off.sql b/transformations/azure_compliance/macros/pro/sql/data_encryption_off.sql index 92727820a..900f1f1d2 100644 --- a/transformations/azure_compliance/macros/pro/sql/data_encryption_off.sql +++ b/transformations/azure_compliance/macros/pro/sql/data_encryption_off.sql @@ -1,5 +1,10 @@ {% macro sql_data_encryption_off(framework, check_id) %} + {{ return(adapter.dispatch('sql_data_encryption_off')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_data_encryption_off(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_data_encryption_off(framework, check_id) %} SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -18,4 +23,25 @@ FROM azure_sql_servers s LEFT JOIN azure_sql_transparent_data_encryptions tde ON asd._cq_id = tde._cq_parent_id WHERE asd.name != 'master' +{% endmacro %} + +{% macro snowflake__sql_data_encryption_off(framework, check_id) %} +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that "Data encryption" is set to "On" on a SQL Database (Automated)' as title, + s.subscription_id, + asd.id AS database_id, + CASE + WHEN tde.properties:state IS DISTINCT FROM 'Enabled' + THEN 'fail' + ELSE 'pass' + END +FROM azure_sql_servers s + LEFT JOIN azure_sql_server_databases asd ON + s._cq_id = asd._cq_parent_id + LEFT JOIN azure_sql_transparent_data_encryptions tde ON + asd._cq_id = tde._cq_parent_id +WHERE asd.name != 'master' {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/mysql_ssl_enforcment_disabled.sql b/transformations/azure_compliance/macros/pro/sql/mysql_ssl_enforcment_disabled.sql index 41e5004dc..28131c785 100644 --- a/transformations/azure_compliance/macros/pro/sql/mysql_ssl_enforcment_disabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/mysql_ssl_enforcment_disabled.sql @@ -1,5 +1,10 @@ {% macro sql_mysql_ssl_enforcment_disabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_mysql_ssl_enforcment_disabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_mysql_ssl_enforcment_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_mysql_ssl_enforcment_disabled(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -12,4 +17,19 @@ SELECT then 'fail' else 'pass' end FROM azure_mysql_servers +{% endmacro %} + +{% macro snowflake__sql_mysql_ssl_enforcment_disabled(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure "Enforce SSL connection" is set to "ENABLED" for MySQL Database Server (Automated)' as title, + subscription_id, + id AS server_id, + case + when properties:sslEnforcement is distinct from 'Enabled' + then 'fail' else 'pass' + end +FROM azure_mysql_servers {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/no_sql_allow_ingress_from_any_ip.sql b/transformations/azure_compliance/macros/pro/sql/no_sql_allow_ingress_from_any_ip.sql index 9859938b7..6bfaecfaa 100644 --- a/transformations/azure_compliance/macros/pro/sql/no_sql_allow_ingress_from_any_ip.sql +++ b/transformations/azure_compliance/macros/pro/sql/no_sql_allow_ingress_from_any_ip.sql @@ -1,5 +1,10 @@ {% macro sql_no_sql_allow_ingress_from_any_ip(framework, check_id) %} + {{ return(adapter.dispatch('sql_no_sql_allow_ingress_from_any_ip')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_no_sql_allow_ingress_from_any_ip(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_no_sql_allow_ingress_from_any_ip(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -14,4 +19,21 @@ SELECT ELSE 'pass' END AS status FROM azure_sql_server_firewall_rules +{% endmacro %} + +{% macro snowflake__sql_no_sql_allow_ingress_from_any_ip(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure no SQL Databases allow ingress 0.0.0.0/0 (ANY IP)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN properties:startIpAddress = '0.0.0.0' + AND (properties:endIpAddress = '0.0.0.0' OR properties:endIpAddress = '255.255.255.255') + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_sql_server_firewall_rules {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/postgresql_allow_access_to_azure_services_enabled.sql b/transformations/azure_compliance/macros/pro/sql/postgresql_allow_access_to_azure_services_enabled.sql index cdc27420d..312b318cf 100644 --- a/transformations/azure_compliance/macros/pro/sql/postgresql_allow_access_to_azure_services_enabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/postgresql_allow_access_to_azure_services_enabled.sql @@ -1,5 +1,10 @@ {% macro sql_postgresql_allow_access_to_azure_services_enabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_postgresql_allow_access_to_azure_services_enabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_postgresql_allow_access_to_azure_services_enabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_postgresql_allow_access_to_azure_services_enabled(framework, check_id) %} SELECT aps._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -16,4 +21,23 @@ SELECT FROM azure_postgresql_servers aps LEFT JOIN azure_postgresql_server_firewall_rules apsfr ON aps._cq_id = apsfr._cq_parent_id +{% endmacro %} + +{% macro snowflake__sql_postgresql_allow_access_to_azure_services_enabled(framework, check_id) %} +SELECT + aps._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure "Allow access to Azure services" for PostgreSQL Database Server is disabled (Automated)' as title, + aps.subscription_id, + aps.id AS server_id, + case + when apsfr.name = 'AllowAllAzureIps' + OR (apsfr.properties:startIPAddress = '0.0.0.0' + AND apsfr.properties:endIPAddress = '0.0.0.0') + then 'fail' else 'pass' + end +FROM azure_postgresql_servers aps + LEFT JOIN azure_postgresql_server_firewall_rules apsfr ON + aps._cq_id = apsfr._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/postgresql_connection_throttling_disabled.sql b/transformations/azure_compliance/macros/pro/sql/postgresql_connection_throttling_disabled.sql index e37bd9875..ea9c9b928 100644 --- a/transformations/azure_compliance/macros/pro/sql/postgresql_connection_throttling_disabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/postgresql_connection_throttling_disabled.sql @@ -1,4 +1,10 @@ {% macro sql_postgresql_connection_throttling_disabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_postgresql_connection_throttling_disabled')(framework, check_id)) }} +{% endmacro %} + +{% macro default__sql_postgresql_connection_throttling_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_postgresql_connection_throttling_disabled(framework, check_id) %} WITH value_check AS ( SELECT aps._cq_id, apsc.properties->>'value' as "value" FROM azure_postgresql_servers aps @@ -20,4 +26,30 @@ SELECT end FROM azure_postgresql_servers s LEFT JOIN value_check v ON - s._cq_id = v._cq_id{% endmacro %} \ No newline at end of file + s._cq_id = v._cq_id +{% endmacro %} + +{% macro snowflake__sql_postgresql_connection_throttling_disabled(framework, check_id) %} +WITH value_check AS ( + SELECT aps._cq_id, apsc.properties:value as value + FROM azure_postgresql_servers aps + LEFT JOIN azure_postgresql_server_configurations apsc ON + aps._cq_id = apsc._cq_parent_id + WHERE apsc.name = 'connection_throttling' +) + +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure server parameter "connection_throttling" is set to "ON" for PostgreSQL Database Server (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when v.value IS NULL OR v.value != 'on' + then 'fail' else 'pass' + end +FROM azure_postgresql_servers s + LEFT JOIN value_check v ON + s._cq_id = v._cq_id +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/postgresql_log_checkpoints_disabled.sql b/transformations/azure_compliance/macros/pro/sql/postgresql_log_checkpoints_disabled.sql index 8ea64e1ac..ff29eb738 100644 --- a/transformations/azure_compliance/macros/pro/sql/postgresql_log_checkpoints_disabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/postgresql_log_checkpoints_disabled.sql @@ -1,4 +1,10 @@ {% macro sql_postgresql_log_checkpoints_disabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_postgresql_log_checkpoints_disabled')(framework, check_id)) }} +{% endmacro %} + +{% macro default__sql_postgresql_log_checkpoints_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_postgresql_log_checkpoints_disabled(framework, check_id) %} WITH value_check AS ( SELECT aps._cq_id, apsc.properties->>'value' as "value" FROM azure_postgresql_servers aps @@ -7,6 +13,31 @@ WITH value_check AS ( WHERE apsc."name" = 'log_checkpoints' ) +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure server parameter "log_checkpoints" is set to "ON" for PostgreSQL Database Server (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when v.value IS NULL OR v.value != 'on' + then 'fail' else 'pass' + end +FROM azure_postgresql_servers s + LEFT JOIN value_check v ON + s._cq_id = v._cq_id +{% endmacro %} + +{% macro snowflake__sql_postgresql_log_checkpoints_disabled(framework, check_id) %} +WITH value_check AS ( + SELECT aps._cq_id, apsc.properties:value as value + FROM azure_postgresql_servers aps + LEFT JOIN azure_postgresql_server_configurations apsc ON + aps._cq_id = apsc._cq_parent_id + WHERE apsc.name = 'log_checkpoints' +) + SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/pro/sql/postgresql_log_connections_disabled.sql b/transformations/azure_compliance/macros/pro/sql/postgresql_log_connections_disabled.sql index 2972a2569..372895a58 100644 --- a/transformations/azure_compliance/macros/pro/sql/postgresql_log_connections_disabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/postgresql_log_connections_disabled.sql @@ -1,4 +1,10 @@ {% macro sql_postgresql_log_connections_disabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_postgresql_log_connections_disabled')(framework, check_id)) }} +{% endmacro %} + +{% macro default__sql_postgresql_log_connections_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_postgresql_log_connections_disabled(framework, check_id) %} WITH value_check AS ( SELECT aps._cq_id, apsc.properties->>'value' as "value" FROM azure_postgresql_servers aps @@ -7,6 +13,31 @@ WITH value_check AS ( WHERE apsc."name" = 'log_connections' ) +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure server parameter "log_connections" is set to "ON" for PostgreSQL Database Server (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when v.value IS NULL OR v.value != 'on' + then 'fail' else 'pass' + end +FROM azure_postgresql_servers s + LEFT JOIN value_check v ON + s._cq_id = v._cq_id +{% endmacro %} + +{% macro snowflake__sql_postgresql_log_connections_disabled(framework, check_id) %} +WITH value_check AS ( + SELECT aps._cq_id, apsc.properties:value as value + FROM azure_postgresql_servers aps + LEFT JOIN azure_postgresql_server_configurations apsc ON + aps._cq_id = apsc._cq_parent_id + WHERE apsc.name = 'log_connections' +) + SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/pro/sql/postgresql_log_disconnections_disabled.sql b/transformations/azure_compliance/macros/pro/sql/postgresql_log_disconnections_disabled.sql index b3b9501b2..4c35fa62f 100644 --- a/transformations/azure_compliance/macros/pro/sql/postgresql_log_disconnections_disabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/postgresql_log_disconnections_disabled.sql @@ -1,4 +1,10 @@ {% macro sql_postgresql_log_disconnections_disabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_postgresql_log_disconnections_disabled')(framework, check_id)) }} +{% endmacro %} + +{% macro default__sql_postgresql_log_disconnections_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_postgresql_log_disconnections_disabled(framework, check_id) %} WITH value_check AS ( SELECT aps._cq_id, apsc.properties->>'value' as "value" FROM azure_postgresql_servers aps @@ -7,6 +13,31 @@ WITH value_check AS ( WHERE apsc."name" = 'log_disconnections' ) +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure server parameter "log_disconnections" is set to "ON" for PostgreSQL Database Server (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when v.value IS NULL OR v.value != 'on' + then 'fail' else 'pass' + end +FROM azure_postgresql_servers s + LEFT JOIN value_check v ON + s._cq_id = v._cq_id +{% endmacro %} + +{% macro snowflake__sql_postgresql_log_disconnections_disabled(framework, check_id) %} +WITH value_check AS ( + SELECT aps._cq_id, apsc.properties:value as value + FROM azure_postgresql_servers aps + LEFT JOIN azure_postgresql_server_configurations apsc ON + aps._cq_id = apsc._cq_parent_id + WHERE apsc.name = 'log_disconnections' +) + SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/pro/sql/postgresql_log_retention_days_less_than_3_days.sql b/transformations/azure_compliance/macros/pro/sql/postgresql_log_retention_days_less_than_3_days.sql index 880d27737..37c2c9fb8 100644 --- a/transformations/azure_compliance/macros/pro/sql/postgresql_log_retention_days_less_than_3_days.sql +++ b/transformations/azure_compliance/macros/pro/sql/postgresql_log_retention_days_less_than_3_days.sql @@ -1,4 +1,10 @@ {% macro sql_postgresql_log_retention_days_less_than_3_days(framework, check_id) %} + {{ return(adapter.dispatch('sql_postgresql_log_retention_days_less_than_3_days')(framework, check_id)) }} +{% endmacro %} + +{% macro default__sql_postgresql_log_retention_days_less_than_3_days(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_postgresql_log_retention_days_less_than_3_days(framework, check_id) %} WITH value_check AS ( SELECT aps._cq_id, apsc.properties->>'value' as "value" FROM azure_postgresql_servers aps @@ -7,6 +13,31 @@ WITH value_check AS ( WHERE apsc."name" = 'log_retention_days' ) +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure server parameter "log_retention_days" is greater than 3 days for PostgreSQL Database Server (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when v.value IS NULL OR v.value::INTEGER < 3 + then 'fail' else 'pass' + end +FROM azure_postgresql_servers s + LEFT JOIN value_check v ON + s._cq_id = v._cq_id +{% endmacro %} + +{% macro snowflake__sql_postgresql_log_retention_days_less_than_3_days(framework, check_id) %} +WITH value_check AS ( + SELECT aps._cq_id, apsc.properties:value as value + FROM azure_postgresql_servers aps + LEFT JOIN azure_postgresql_server_configurations apsc ON + aps._cq_id = apsc._cq_parent_id + WHERE apsc.name = 'log_retention_days' +) + SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/pro/sql/postgresql_ssl_enforcment_disabled.sql b/transformations/azure_compliance/macros/pro/sql/postgresql_ssl_enforcment_disabled.sql index e01a3b316..eea2f9669 100644 --- a/transformations/azure_compliance/macros/pro/sql/postgresql_ssl_enforcment_disabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/postgresql_ssl_enforcment_disabled.sql @@ -1,5 +1,10 @@ {% macro sql_postgresql_ssl_enforcment_disabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_postgresql_ssl_enforcment_disabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_postgresql_ssl_enforcment_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_postgresql_ssl_enforcment_disabled(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -12,4 +17,19 @@ SELECT then 'fail' else 'pass' end FROM azure_postgresql_servers +{% endmacro %} + +{% macro snowflake__sql_postgresql_ssl_enforcment_disabled(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure "Enforce SSL connection" is set to "ENABLED" for PostgreSQL Database Server (Automated)' as title, + subscription_id, + id AS server_id, + case + when properties:sslEnforcement is distinct from 'Enabled' + then 'fail' else 'pass' + end as status +FROM azure_postgresql_servers {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/sqlserver_tde_not_encrypted_with_cmek.sql b/transformations/azure_compliance/macros/pro/sql/sqlserver_tde_not_encrypted_with_cmek.sql index d395a3ceb..5de635f28 100644 --- a/transformations/azure_compliance/macros/pro/sql/sqlserver_tde_not_encrypted_with_cmek.sql +++ b/transformations/azure_compliance/macros/pro/sql/sqlserver_tde_not_encrypted_with_cmek.sql @@ -1,5 +1,10 @@ {% macro sql_sqlserver_tde_not_encrypted_with_cmek(framework, check_id) %} + {{ return(adapter.dispatch('sql_sqlserver_tde_not_encrypted_with_cmek')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_sqlserver_tde_not_encrypted_with_cmek(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_sqlserver_tde_not_encrypted_with_cmek(framework, check_id) %} SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -16,4 +21,23 @@ SELECT FROM azure_sql_servers s LEFT JOIN azure_sql_server_encryption_protectors p ON s._cq_id = p._cq_parent_id +{% endmacro %} + +{% macro snowflake__sql_sqlserver_tde_not_encrypted_with_cmek(framework, check_id) %} +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure SQL server"s TDE protector is encrypted with Customer-managed key (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when p.kind != 'azurekeyvault' + OR p.properties:serverKeyType IS DISTINCT FROM 'AzureKeyVault' + OR p.properties:uri IS NULL + then 'fail' else 'pass' + end +FROM azure_sql_servers s + LEFT JOIN azure_sql_server_encryption_protectors p ON + s._cq_id = p._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/va_is_enabled_on_sql_server_by_storage_account.sql b/transformations/azure_compliance/macros/pro/sql/va_is_enabled_on_sql_server_by_storage_account.sql index f56f06577..0a1e414eb 100644 --- a/transformations/azure_compliance/macros/pro/sql/va_is_enabled_on_sql_server_by_storage_account.sql +++ b/transformations/azure_compliance/macros/pro/sql/va_is_enabled_on_sql_server_by_storage_account.sql @@ -1,5 +1,10 @@ {% macro sql_va_is_enabled_on_sql_server_by_storage_account(framework, check_id) %} + {{ return(adapter.dispatch('sql_va_is_enabled_on_sql_server_by_storage_account')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_va_is_enabled_on_sql_server_by_storage_account(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_va_is_enabled_on_sql_server_by_storage_account(framework, check_id) %} SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -14,4 +19,21 @@ SELECT FROM azure_sql_servers s LEFT JOIN azure_sql_server_vulnerability_assessments a ON s._cq_id = a._cq_parent_id +{% endmacro %} + +{% macro snowflake__sql_va_is_enabled_on_sql_server_by_storage_account(framework, check_id) %} +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Vulnerability Assessment (VA) is enabled on a SQL server by setting a Storage Account (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when a.properties:storageContainerPath IS NULL OR a.properties:storageContainerPath = '' + then 'fail' else 'pass' + end +FROM azure_sql_servers s + LEFT JOIN azure_sql_server_vulnerability_assessments a ON + s._cq_id = a._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/va_periodic_scans_enabled_on_sql_server.sql b/transformations/azure_compliance/macros/pro/sql/va_periodic_scans_enabled_on_sql_server.sql index a67df9a69..0d84a595d 100644 --- a/transformations/azure_compliance/macros/pro/sql/va_periodic_scans_enabled_on_sql_server.sql +++ b/transformations/azure_compliance/macros/pro/sql/va_periodic_scans_enabled_on_sql_server.sql @@ -1,5 +1,10 @@ {% macro sql_va_periodic_scans_enabled_on_sql_server(framework, check_id) %} + {{ return(adapter.dispatch('sql_va_periodic_scans_enabled_on_sql_server')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_va_periodic_scans_enabled_on_sql_server(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_va_periodic_scans_enabled_on_sql_server(framework, check_id) %} SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -14,4 +19,21 @@ SELECT FROM azure_sql_servers s LEFT JOIN azure_sql_server_vulnerability_assessments a ON s._cq_id = a._cq_parent_id +{% endmacro %} + +{% macro snowflake__sql_va_periodic_scans_enabled_on_sql_server(framework, check_id) %} +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that VA setting Periodic Recurring Scans is enabled on a SQL server (Automated)' as title, + s.subscription_id, + s.id, + case + when (a.properties:recurringScans:isEnabled)::boolean IS DISTINCT FROM TRUE + then 'fail' else 'pass' + end +FROM azure_sql_servers s + LEFT JOIN azure_sql_server_vulnerability_assessments a ON + s._cq_id = a._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/va_send_email_to_admins_and_owners_enabled.sql b/transformations/azure_compliance/macros/pro/sql/va_send_email_to_admins_and_owners_enabled.sql index 2483796b9..716839bc7 100644 --- a/transformations/azure_compliance/macros/pro/sql/va_send_email_to_admins_and_owners_enabled.sql +++ b/transformations/azure_compliance/macros/pro/sql/va_send_email_to_admins_and_owners_enabled.sql @@ -1,5 +1,10 @@ {% macro sql_va_send_email_to_admins_and_owners_enabled(framework, check_id) %} + {{ return(adapter.dispatch('sql_va_send_email_to_admins_and_owners_enabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__sql_va_send_email_to_admins_and_owners_enabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_va_send_email_to_admins_and_owners_enabled(framework, check_id) %} SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -14,4 +19,21 @@ SELECT FROM azure_sql_servers s LEFT JOIN azure_sql_server_vulnerability_assessments a ON s._cq_id = a._cq_parent_id +{% endmacro %} + +{% macro snowflake__sql_va_send_email_to_admins_and_owners_enabled(framework, check_id) %} +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that VA setting "Also send email notifications to admins and subscription owners" is set for a SQL server (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when (a.properties:recurringScans:emailSubscriptionAdmins)::boolean IS DISTINCT FROM TRUE + then 'fail' else 'pass' + end +FROM azure_sql_servers s + LEFT JOIN azure_sql_server_vulnerability_assessments a ON + s._cq_id = a._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/sql/va_send_scan_report_enabled_on_sql_server.sql b/transformations/azure_compliance/macros/pro/sql/va_send_scan_report_enabled_on_sql_server.sql index f87fab6e8..afdfbffcb 100644 --- a/transformations/azure_compliance/macros/pro/sql/va_send_scan_report_enabled_on_sql_server.sql +++ b/transformations/azure_compliance/macros/pro/sql/va_send_scan_report_enabled_on_sql_server.sql @@ -1,4 +1,10 @@ {% macro sql_va_send_scan_report_enabled_on_sql_server(framework, check_id) %} + {{ return(adapter.dispatch('sql_va_send_scan_report_enabled_on_sql_server')(framework, check_id)) }} +{% endmacro %} + +{% macro default__sql_va_send_scan_report_enabled_on_sql_server(framework, check_id) %}{% endmacro %} + +{% macro postgres__sql_va_send_scan_report_enabled_on_sql_server(framework, check_id) %} WITH vulnerability_emails AS ( SELECT id, @@ -13,6 +19,40 @@ emails_count AS ( GROUP BY id ) +SELECT + s._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that VA setting Send scan reports to is configured for a SQL server (Automated)' as title, + s.subscription_id, + s.id AS server_id, + case + when c.emails_number = 0 OR c.emails_number IS NULL + then 'fail' else 'pass' + end +FROM azure_sql_servers s + LEFT JOIN azure_sql_server_vulnerability_assessments sv ON + s._cq_id = sv._cq_parent_id + LEFT JOIN emails_count c ON + sv.id = c.id +{% endmacro %} + +{% macro snowflake__sql_va_send_scan_report_enabled_on_sql_server(framework, check_id) %} +WITH vulnerability_emails AS ( + SELECT + id, + email.value AS emails +FROM + azure_sql_server_vulnerability_assessments v, + LATERAL FLATTEN(input => v.properties:recurringScans:emails) AS email +), +emails_count AS ( + SELECT + id, + count(emails) AS emails_number + FROM vulnerability_emails + GROUP BY id +) SELECT s._cq_sync_time As sync_time, '{{framework}}' As framework, diff --git a/transformations/azure_compliance/macros/pro/storage/default_network_access_rule_is_deny.sql b/transformations/azure_compliance/macros/pro/storage/default_network_access_rule_is_deny.sql index ab7b97535..c16244467 100644 --- a/transformations/azure_compliance/macros/pro/storage/default_network_access_rule_is_deny.sql +++ b/transformations/azure_compliance/macros/pro/storage/default_network_access_rule_is_deny.sql @@ -1,5 +1,10 @@ {% macro storage_default_network_access_rule_is_deny(framework, check_id) %} + {{ return(adapter.dispatch('storage_default_network_access_rule_is_deny')(framework, check_id)) }} +{% endmacro %} +{% macro default__storage_default_network_access_rule_is_deny(framework, check_id) %}{% endmacro %} + +{% macro postgres__storage_default_network_access_rule_is_deny(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT ELSE 'pass' END AS status FROM azure_storage_accounts +{% endmacro %} + +{% macro snowflake__storage_default_network_access_rule_is_deny(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure default network access rule for Storage Accounts is set to deny' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN properties:networkAcls:defaultAction = 'Allow' + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_storage_accounts {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/storage/encrypt_with_cmk.sql b/transformations/azure_compliance/macros/pro/storage/encrypt_with_cmk.sql index dd0a22484..1865c72fa 100644 --- a/transformations/azure_compliance/macros/pro/storage/encrypt_with_cmk.sql +++ b/transformations/azure_compliance/macros/pro/storage/encrypt_with_cmk.sql @@ -1,5 +1,10 @@ {% macro storage_encrypt_with_cmk(framework, check_id) %} + {{ return(adapter.dispatch('storage_encrypt_with_cmk')(framework, check_id)) }} +{% endmacro %} +{% macro default__storage_encrypt_with_cmk(framework, check_id) %}{% endmacro %} + +{% macro postgres__storage_encrypt_with_cmk(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -14,4 +19,21 @@ SELECT ELSE 'fail' END AS status FROM azure_storage_accounts +{% endmacro %} + +{% macro snowflake__storage_encrypt_with_cmk(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure storage for critical data are encrypted with Customer Managed Key' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN properties:encryption:keySource = 'Microsoft.Keyvault' + AND properties:encryption:keyvaultproperties IS DISTINCT FROM NULL + THEN 'pass' + ELSE 'fail' + END AS status +FROM azure_storage_accounts {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/storage/encrypt_with_cmk_for_activity_log.sql b/transformations/azure_compliance/macros/pro/storage/encrypt_with_cmk_for_activity_log.sql index f64733491..c89f738b2 100644 --- a/transformations/azure_compliance/macros/pro/storage/encrypt_with_cmk_for_activity_log.sql +++ b/transformations/azure_compliance/macros/pro/storage/encrypt_with_cmk_for_activity_log.sql @@ -1,5 +1,10 @@ {% macro storage_encrypt_with_cmk_for_activity_log(framework, check_id) %} + {{ return(adapter.dispatch('storage_encrypt_with_cmk_for_activity_log')(framework, check_id)) }} +{% endmacro %} +{% macro default__storage_encrypt_with_cmk_for_activity_log(framework, check_id) %}{% endmacro %} + +{% macro postgres__storage_encrypt_with_cmk_for_activity_log(framework, check_id) %} SELECT asa._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -17,4 +22,24 @@ FROM azure_storage_accounts asa JOIN azure_monitor_diagnostic_settings amds ON asa.id = amds.properties->>'storageAccountId' WHERE amds.properties->>'storageAccountId' IS NOT NULL +{% endmacro %} + +{% macro snowflake__storage_encrypt_with_cmk_for_activity_log(framework, check_id) %} +SELECT + asa._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure the storage account containing the container with activity logs is encrypted with BYOK (Use Your Own Key)' AS title, + asa.subscription_id AS subscription_id, + asa.id AS resource_id, + CASE + WHEN asa.properties:encryption:keySource = 'Microsoft.Keyvault' + AND asa.properties:encryption:keyvaultproperties IS DISTINCT FROM NULL + THEN 'pass' + ELSE 'fail' + END AS status +FROM azure_storage_accounts asa + JOIN azure_monitor_diagnostic_settings amds + ON asa.id = amds.properties:storageAccountId +WHERE amds.properties:storageAccountId IS NOT NULL {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/storage/no_public_blob_container.sql b/transformations/azure_compliance/macros/pro/storage/no_public_blob_container.sql index 90d2ac725..7bba3e7d4 100644 --- a/transformations/azure_compliance/macros/pro/storage/no_public_blob_container.sql +++ b/transformations/azure_compliance/macros/pro/storage/no_public_blob_container.sql @@ -1,5 +1,10 @@ {% macro storage_no_public_blob_container(framework, check_id) %} + {{ return(adapter.dispatch('storage_no_public_blob_container')(framework, check_id)) }} +{% endmacro %} +{% macro default__storage_no_public_blob_container(framework, check_id) %}{% endmacro %} + +{% macro postgres__storage_no_public_blob_container(framework, check_id) %} SELECT azsc._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -15,4 +20,22 @@ SELECT END AS status FROM azure_storage_containers azsc JOIN azure_storage_accounts asa on azsc._cq_parent_id = asa._cq_id +{% endmacro %} + +{% macro snowflake__storage_no_public_blob_container(framework, check_id) %} +SELECT + azsc._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that ''Public access level'' is set to Private for blob containers' AS title, + azsc.subscription_id AS subscription_id, + azsc.id AS resrouce_id, + CASE + WHEN (asa.properties:allowBlobPublicAccess)::BOOLEAN = true + AND (azsc.properties:publicAccess) <> 'None' + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_storage_containers azsc + JOIN azure_storage_accounts asa on azsc._cq_parent_id = asa._cq_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/storage/no_publicly_accessible_insights_activity_logs.sql b/transformations/azure_compliance/macros/pro/storage/no_publicly_accessible_insights_activity_logs.sql index a8b0bffa6..cd836f44c 100644 --- a/transformations/azure_compliance/macros/pro/storage/no_publicly_accessible_insights_activity_logs.sql +++ b/transformations/azure_compliance/macros/pro/storage/no_publicly_accessible_insights_activity_logs.sql @@ -1,5 +1,10 @@ {% macro storage_no_publicly_accessible_insights_activity_logs(framework, check_id) %} + {{ return(adapter.dispatch('storage_no_publicly_accessible_insights_activity_logs')(framework, check_id)) }} +{% endmacro %} +{% macro default__storage_no_publicly_accessible_insights_activity_logs(framework, check_id) %}{% endmacro %} + +{% macro postgres__storage_no_publicly_accessible_insights_activity_logs(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -15,4 +20,23 @@ SELECT FROM azure_storage_containers WHERE name = 'insights-activity-logs' +{% endmacro %} + +{% macro snowflake__storage_no_publicly_accessible_insights_activity_logs(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure the storage container storing the activity logs is not publicly accessible' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN properties:publicAccess = 'None' + THEN 'pass' + ELSE 'fail' + END AS status +FROM azure_storage_containers +WHERE + name = 'insights-activity-logs' + {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/storage/secure_transfer_to_storage_accounts_should_be_enabled.sql b/transformations/azure_compliance/macros/pro/storage/secure_transfer_to_storage_accounts_should_be_enabled.sql index 546810d70..8b93bbad7 100644 --- a/transformations/azure_compliance/macros/pro/storage/secure_transfer_to_storage_accounts_should_be_enabled.sql +++ b/transformations/azure_compliance/macros/pro/storage/secure_transfer_to_storage_accounts_should_be_enabled.sql @@ -1,5 +1,10 @@ {% macro storage_secure_transfer_to_storage_accounts_should_be_enabled(framework, check_id) %} + {{ return(adapter.dispatch('storage_secure_transfer_to_storage_accounts_should_be_enabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__storage_secure_transfer_to_storage_accounts_should_be_enabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__storage_secure_transfer_to_storage_accounts_should_be_enabled(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -12,4 +17,19 @@ SELECT then 'fail' else 'pass' end FROM azure_storage_accounts +{% endmacro %} + +{% macro snowflake__storage_secure_transfer_to_storage_accounts_should_be_enabled(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Secure transfer to storage accounts should be enabled', + subscription_id, + id, + case + when properties:supportsHttpsTrafficOnly IS DISTINCT FROM 'true' + then 'fail' else 'pass' + end +FROM azure_storage_accounts {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/storage/soft_delete_is_enabled.sql b/transformations/azure_compliance/macros/pro/storage/soft_delete_is_enabled.sql index fdbebb2b3..0cf2e0e6b 100644 --- a/transformations/azure_compliance/macros/pro/storage/soft_delete_is_enabled.sql +++ b/transformations/azure_compliance/macros/pro/storage/soft_delete_is_enabled.sql @@ -1,5 +1,10 @@ {% macro storage_soft_delete_is_enabled(framework, check_id) %} + {{ return(adapter.dispatch('storage_soft_delete_is_enabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__storage_soft_delete_is_enabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__storage_soft_delete_is_enabled(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, @@ -13,4 +18,20 @@ SELECT ELSE 'fail' END AS status FROM azure_storage_blob_services +{% endmacro %} + +{% macro snowflake__storage_soft_delete_is_enabled(framework, check_id) %} +SELECT + _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure soft delete is enabled for Azure Storage' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN (properties:deleteRetentionPolicy:enabled)::boolean + THEN 'pass' + ELSE 'fail' + END AS status +FROM azure_storage_blob_services {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/web/app_allow_http.sql b/transformations/azure_compliance/macros/pro/web/app_allow_http.sql index 9d48d262e..e4f1a58da 100644 --- a/transformations/azure_compliance/macros/pro/web/app_allow_http.sql +++ b/transformations/azure_compliance/macros/pro/web/app_allow_http.sql @@ -1,5 +1,10 @@ {% macro web_app_allow_http(framework, check_id) %} + {{ return(adapter.dispatch('web_app_allow_http')(framework, check_id)) }} +{% endmacro %} +{% macro default__web_app_allow_http(framework, check_id) %}{% endmacro %} + +{% macro postgres__web_app_allow_http(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -12,4 +17,19 @@ SELECT _cq_sync_time As sync_time, ELSE 'pass' END AS status FROM azure_appservice_web_apps +{% endmacro %} + +{% macro snowflake__web_app_allow_http(framework, check_id) %} +SELECT _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service (Automated)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN (properties:httpsOnly)::boolean IS distinct from TRUE + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_appservice_web_apps {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/web/app_auth_unset.sql b/transformations/azure_compliance/macros/pro/web/app_auth_unset.sql index 59252eeed..d4bf31c1a 100644 --- a/transformations/azure_compliance/macros/pro/web/app_auth_unset.sql +++ b/transformations/azure_compliance/macros/pro/web/app_auth_unset.sql @@ -1,5 +1,10 @@ {% macro web_app_auth_unset(framework, check_id) %} + {{ return(adapter.dispatch('web_app_auth_unset')(framework, check_id)) }} +{% endmacro %} +{% macro default__web_app_auth_unset(framework, check_id) %}{% endmacro %} + +{% macro postgres__web_app_auth_unset(framework, check_id) %} SELECT awa._cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -14,6 +19,21 @@ SELECT awa._cq_sync_time As sync_time, FROM azure_appservice_web_apps awa LEFT JOIN azure_appservice_web_app_auth_settings awaas ON awa._cq_id = awaas._cq_parent_id +{% endmacro %} - +{% macro snowflake__web_app_auth_unset(framework, check_id) %} +SELECT awa._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure App Service Authentication is set on Azure App Service (Automated)' AS title, + awa.subscription_id AS subscription_id, + awa.id AS resource_id, + CASE + WHEN (awaas.properties:enabled)::boolean is distinct from TRUE + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_appservice_web_apps awa + LEFT JOIN azure_appservice_web_app_auth_settings awaas ON + awa._cq_id = awaas._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/web/app_client_cert_disabled.sql b/transformations/azure_compliance/macros/pro/web/app_client_cert_disabled.sql index 1262f5e0e..6d5a037cd 100644 --- a/transformations/azure_compliance/macros/pro/web/app_client_cert_disabled.sql +++ b/transformations/azure_compliance/macros/pro/web/app_client_cert_disabled.sql @@ -1,5 +1,10 @@ {% macro web_app_client_cert_disabled(framework, check_id) %} + {{ return(adapter.dispatch('web_app_client_cert_disabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__web_app_client_cert_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__web_app_client_cert_disabled(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -12,4 +17,19 @@ SELECT _cq_sync_time As sync_time, ELSE 'pass' END AS status FROM azure_appservice_web_apps +{% endmacro %} + +{% macro snowflake__web_app_client_cert_disabled(framework, check_id) %} +SELECT _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure the web app has ''Client Certificates (Incoming client certificates)'' set to ''On'' (Automated)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN kind LIKE 'app%' AND (properties:clientCertEnabled)::boolean is distinct from TRUE + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_appservice_web_apps {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/web/app_ftp_deployment_enabled.sql b/transformations/azure_compliance/macros/pro/web/app_ftp_deployment_enabled.sql index 4a417c9b3..00e24a64c 100644 --- a/transformations/azure_compliance/macros/pro/web/app_ftp_deployment_enabled.sql +++ b/transformations/azure_compliance/macros/pro/web/app_ftp_deployment_enabled.sql @@ -1,5 +1,10 @@ {% macro web_app_ftp_deployment_enabled(framework, check_id) %} + {{ return(adapter.dispatch('web_app_ftp_deployment_enabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__web_app_ftp_deployment_enabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__web_app_ftp_deployment_enabled(framework, check_id) %} SELECT aawac._cq_sync_time As sync_time, '{{framework}}' As framework, @@ -15,4 +20,22 @@ SELECT FROM azure_appservice_web_apps as aawa JOIN azure_appservice_web_app_configurations aawac ON aawa._cq_id = aawac._cq_parent_id +{% endmacro %} + +{% macro snowflake__web_app_ftp_deployment_enabled(framework, check_id) %} +SELECT + aawac._cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure FTP deployments are disabled (Automated)' AS title, + aawac.subscription_id AS subscription_id, + aawac.id AS resource_id, + CASE + WHEN aawac.properties:ftpsState = 'AllAllowed' + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_appservice_web_apps as aawa + JOIN azure_appservice_web_app_configurations aawac + ON aawa._cq_id = aawac._cq_parent_id {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/web/app_register_with_ad_disabled.sql b/transformations/azure_compliance/macros/pro/web/app_register_with_ad_disabled.sql index 7dd311306..5a674735d 100644 --- a/transformations/azure_compliance/macros/pro/web/app_register_with_ad_disabled.sql +++ b/transformations/azure_compliance/macros/pro/web/app_register_with_ad_disabled.sql @@ -1,5 +1,10 @@ {% macro web_app_register_with_ad_disabled(framework, check_id) %} + {{ return(adapter.dispatch('web_app_register_with_ad_disabled')(framework, check_id)) }} +{% endmacro %} +{% macro default__web_app_register_with_ad_disabled(framework, check_id) %}{% endmacro %} + +{% macro postgres__web_app_register_with_ad_disabled(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -12,4 +17,19 @@ SELECT _cq_sync_time As sync_time, ELSE 'pass' END AS status FROM azure_appservice_web_apps +{% endmacro %} + +{% macro snowflake__web_app_register_with_ad_disabled(framework, check_id) %} +SELECT _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure that Register with Azure Active Directory is enabled on App Service (Automated)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN identity:principalId IS NULL OR identity:principalId = '' + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_appservice_web_apps {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/pro/web/app_using_old_tls.sql b/transformations/azure_compliance/macros/pro/web/app_using_old_tls.sql index 4aa55820f..788a71938 100644 --- a/transformations/azure_compliance/macros/pro/web/app_using_old_tls.sql +++ b/transformations/azure_compliance/macros/pro/web/app_using_old_tls.sql @@ -1,5 +1,10 @@ {% macro web_app_using_old_tls(framework, check_id) %} + {{ return(adapter.dispatch('web_app_using_old_tls')(framework, check_id)) }} +{% endmacro %} +{% macro default__web_app_using_old_tls(framework, check_id) %}{% endmacro %} + +{% macro postgres__web_app_using_old_tls(framework, check_id) %} SELECT _cq_sync_time As sync_time, '{{framework}}' As framework, '{{check_id}}' As check_id, @@ -12,4 +17,19 @@ SELECT _cq_sync_time As sync_time, ELSE 'pass' END AS status FROM azure_appservice_web_apps +{% endmacro %} + +{% macro snowflake__web_app_using_old_tls(framework, check_id) %} +SELECT _cq_sync_time As sync_time, + '{{framework}}' As framework, + '{{check_id}}' As check_id, + 'Ensure web app is using the latest version of TLS encryption (Automated)' AS title, + subscription_id AS subscription_id, + id AS resource_id, + CASE + WHEN properties:siteConfig:minTlsVersion IS NULL OR properties:siteConfig:minTlsVersion is distinct from '1.2' + THEN 'fail' + ELSE 'pass' + END AS status +FROM azure_appservice_web_apps {% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/view_azure_nsg_dest_port_ranges.sql b/transformations/azure_compliance/macros/view_azure_nsg_dest_port_ranges.sql new file mode 100644 index 000000000..41b9c9cf5 --- /dev/null +++ b/transformations/azure_compliance/macros/view_azure_nsg_dest_port_ranges.sql @@ -0,0 +1,181 @@ +{% macro view_azure_nsg_dest_port_ranges() %} + {{ return(adapter.dispatch('view_azure_nsg_dest_port_ranges')()) }} +{% endmacro %} + +{% macro default__view_azure_nsg_dest_port_ranges() %}{% endmacro %} + +{% macro postgres__view_azure_nsg_dest_port_ranges() %} +WITH security_rules AS ( + SELECT + _cq_sync_time, + subscription_id, + security_rules->>'id' AS id, + security_rules->'properties'->>'access' AS access, + security_rules->'properties'->>'protocol' AS protocol, + security_rules->'properties'->>'direction' AS direction, + security_rules->'properties'->>'sourceAddressPrefix' AS sourceAddressPrefix, + security_rules->'properties'->>'destinationPortRange' AS destinationPortRange, + security_rules->'properties'->'destinationPortRanges' AS destinationPortRanges + FROM + azure_network_security_groups ansg, + jsonb_array_elements(ansg.properties->'securityRules') AS security_rules +), +dest_port AS ( + SELECT + id, + destinationPortRange + FROM + security_rules + WHERE + destinationPortRange <> '' OR destinationPortRange IS NOT NULL +), +unified_port_ranges AS ( + SELECT + id, + split_part(port_range, '-', 1) :: INT AS start_dest_port, + split_part(port_range, '-', 2) :: INT AS end_dest_port + FROM + security_rules AS sr, jsonb_array_elements_text(sr.destinationPortRanges) AS port_range + WHERE + port_range ~ '^[0-9]+-[0-9]+$' + UNION + SELECT + id, + port_range :: INT AS start_dest_port, + port_range :: INT AS end_dest_port + FROM + security_rules AS sr, jsonb_array_elements_text(sr.destinationPortRanges) AS port_range + WHERE + port_range ~ '^[0-9]+$' + UNION + SELECT + id, + 1 AS start_port, + 65535 AS end_port + FROM + dest_port + WHERE + destinationPortRange = '*' + UNION + SELECT + id, + destinationPortRange :: INT AS start_dest_port, + destinationPortRange :: INT AS end_dest_port + FROM + dest_port + WHERE + destinationPortRange ~ '^[0-9]+$' + UNION + SELECT + id, + split_part(destinationPortRange, '-', 1) :: INT AS start_dest_port, + split_part(destinationPortRange, '-', 2) :: INT AS end_dest_port + FROM + dest_port + WHERE + destinationPortRange ~ '^[0-9]+-[0-9]+$' +) +SELECT + sr._cq_sync_time, + sr.id AS id, + sr.subscription_id AS subscription_id, + access, + protocol, + direction, + sourceAddressPrefix, + start_dest_port, + end_dest_port +FROM + security_rules sr + JOIN unified_port_ranges + ON sr.id = unified_port_ranges.id +{% endmacro %} + +{% macro snowflake__view_azure_nsg_dest_port_ranges() %} +WITH security_rules AS ( + SELECT + _cq_sync_time, + subscription_id, + security_rules.value:id AS id, + security_rules.value:properties:access AS access, + security_rules.value:properties:protocol AS protocol, + security_rules.value:properties:direction AS direction, + security_rules.value:properties:sourceAddressPrefix AS sourceAddressPrefix, + security_rules.value:properties:destinationPortRange AS destinationPortRange, + security_rules.value:properties:destinationPortRanges AS destinationPortRanges + FROM + azure_network_security_groups ansg, + LATERAL FLATTEN(input => ansg.properties:securityRules) AS security_rules +), +dest_port AS ( + SELECT + id, + destinationPortRange + FROM + security_rules + WHERE + destinationPortRange <> '' OR destinationPortRange IS NOT NULL +), +unified_port_ranges AS ( + SELECT + id, + split_part(port_range.value, '-', 1) :: INT AS start_dest_port, + split_part(port_range.value, '-', 2) :: INT AS end_dest_port + FROM + security_rules AS sr, + LATERAL FLATTEN(input => sr.destinationPortRanges) AS port_range + WHERE + port_range.value REGEXP '^[0-9]+-[0-9]+$' + UNION + SELECT + id, + port_range.value :: INT AS start_dest_port, + port_range.value :: INT AS end_dest_port + FROM + security_rules AS sr, + LATERAL FLATTEN(input => sr.destinationPortRanges) AS port_range + WHERE + port_range.value REGEXP '^[0-9]+$' + UNION + SELECT + id, + 1 AS start_port, + 65535 AS end_port + FROM + dest_port + WHERE + destinationPortRange = '*' + UNION + SELECT + id, + destinationPortRange :: INT AS start_dest_port, + destinationPortRange :: INT AS end_dest_port + FROM + dest_port + WHERE + destinationPortRange REGEXP '^[0-9]+$' + UNION + SELECT + id, + split_part(destinationPortRange, '-', 1) :: INT AS start_dest_port, + split_part(destinationPortRange, '-', 2) :: INT AS end_dest_port + FROM + dest_port + WHERE + destinationPortRange REGEXP '^[0-9]+-[0-9]+$' +) +SELECT + sr._cq_sync_time, + sr.id AS id, + sr.subscription_id AS subscription_id, + access, + protocol, + direction, + sourceAddressPrefix, + start_dest_port, + end_dest_port +FROM + security_rules sr + JOIN unified_port_ranges + ON sr.id = unified_port_ranges.id +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/macros/view_azure_security_policy_parameters.sql b/transformations/azure_compliance/macros/view_azure_security_policy_parameters.sql new file mode 100644 index 000000000..b6d6c4568 --- /dev/null +++ b/transformations/azure_compliance/macros/view_azure_security_policy_parameters.sql @@ -0,0 +1,33 @@ +{% macro view_azure_security_policy_parameters() %} + {{ return(adapter.dispatch('view_azure_security_policy_parameters')()) }} +{% endmacro %} + +{% macro default__view_azure_security_policy_parameters() %}{% endmacro %} + +{% macro postgres__view_azure_security_policy_parameters() %} +SELECT + _cq_sync_time, + id, + azure_policy_assignments.subscription_id, + azure_policy_assignments."name", + parameters.*, + azure_policy_assignments.properties -> 'parameters' -> parameters.param ->> 'value' AS value +FROM + azure_policy_assignments, + jsonb_object_keys(azure_policy_assignments.properties -> 'parameters') AS parameters ("param") +WHERE azure_policy_assignments."name" = 'SecurityCenterBuiltIn' +{% endmacro %} + +{% macro snowflake__view_azure_security_policy_parameters() %} +SELECT + _cq_sync_time, + id, + azure_policy_assignments.subscription_id, + azure_policy_assignments.name, + parameters.key AS param, + parameters.value AS value +FROM + azure_policy_assignments, + LATERAL FLATTEN(input => azure_policy_assignments.properties:parameters) AS parameters +WHERE azure_policy_assignments.name = 'SecurityCenterBuiltIn' +{% endmacro %} \ No newline at end of file diff --git a/transformations/azure_compliance/models/free/view_azure_nsg_dest_port_ranges.sql b/transformations/azure_compliance/models/free/view_azure_nsg_dest_port_ranges.sql index de48012d7..eb1a45937 100644 --- a/transformations/azure_compliance/models/free/view_azure_nsg_dest_port_ranges.sql +++ b/transformations/azure_compliance/models/free/view_azure_nsg_dest_port_ranges.sql @@ -1,84 +1,7 @@ -WITH security_rules AS ( - SELECT - _cq_sync_time, - subscription_id, - security_rules->>'id' AS id, - security_rules->'properties'->>'access' AS access, - security_rules->'properties'->>'protocol' AS protocol, - security_rules->'properties'->>'direction' AS direction, - security_rules->'properties'->>'sourceAddressPrefix' AS sourceAddressPrefix, - security_rules->'properties'->>'destinationPortRange' AS destinationPortRange, - security_rules->'properties'->'destinationPortRanges' AS destinationPortRanges - FROM - azure_network_security_groups ansg, - jsonb_array_elements(ansg.properties->'securityRules') AS security_rules -), -dest_port AS ( - SELECT - id, - destinationPortRange - FROM - security_rules - WHERE - destinationPortRange <> '' OR destinationPortRange IS NOT NULL -), -unified_port_ranges AS ( - SELECT - id, - split_part(port_range, '-', 1) :: INT AS start_dest_port, - split_part(port_range, '-', 2) :: INT AS end_dest_port - FROM - security_rules AS sr, jsonb_array_elements_text(sr.destinationPortRanges) AS port_range - WHERE - port_range ~ '^[0-9]+-[0-9]+$' - UNION - SELECT - id, - port_range :: INT AS start_dest_port, - port_range :: INT AS end_dest_port - FROM - security_rules AS sr, jsonb_array_elements_text(sr.destinationPortRanges) AS port_range - WHERE - port_range ~ '^[0-9]+$' - UNION - SELECT - id, - 1 AS start_port, - 65535 AS end_port - FROM - dest_port - WHERE - destinationPortRange = '*' - UNION - SELECT - id, - destinationPortRange :: INT AS start_dest_port, - destinationPortRange :: INT AS end_dest_port - FROM - dest_port - WHERE - destinationPortRange ~ '^[0-9]+$' - UNION - SELECT - id, - split_part(destinationPortRange, '-', 1) :: INT AS start_dest_port, - split_part(destinationPortRange, '-', 2) :: INT AS end_dest_port - FROM - dest_port - WHERE - destinationPortRange ~ '^[0-9]+-[0-9]+$' -) -SELECT - sr._cq_sync_time, - sr.id AS id, - sr.subscription_id AS subscription_id, - access, - protocol, - direction, - sourceAddressPrefix, - start_dest_port, - end_dest_port -FROM - security_rules sr - JOIN unified_port_ranges - ON sr.id = unified_port_ranges.id \ No newline at end of file +with + aggregated as ( + ({{view_azure_nsg_dest_port_ranges()}}) + ) + +select * +from aggregated \ No newline at end of file diff --git a/transformations/azure_compliance/models/free/view_azure_security_policy_parameters.sql b/transformations/azure_compliance/models/free/view_azure_security_policy_parameters.sql index dce668201..e00be2b27 100644 --- a/transformations/azure_compliance/models/free/view_azure_security_policy_parameters.sql +++ b/transformations/azure_compliance/models/free/view_azure_security_policy_parameters.sql @@ -1,11 +1,7 @@ -SELECT - _cq_sync_time, - id, - azure_policy_assignments.subscription_id, - azure_policy_assignments."name", - parameters.*, - azure_policy_assignments.properties -> 'parameters' -> parameters.param ->> 'value' AS value -FROM - azure_policy_assignments, - jsonb_object_keys(azure_policy_assignments.properties -> 'parameters') AS parameters ("param") -WHERE azure_policy_assignments."name" = 'SecurityCenterBuiltIn' \ No newline at end of file +with + aggregated as ( + ({{view_azure_security_policy_parameters()}}) + ) + +select * +from aggregated \ No newline at end of file