diff --git a/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/query.rego b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/query.rego index 1d7a93664ba..07d915f3329 100644 --- a/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/query.rego +++ b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/query.rego @@ -3,85 +3,71 @@ package Cx import data.generic.common as common_lib import data.generic.azureresourcemanager as arm_lib -types := ["Microsoft.Sql/servers/databases/auditingSettings", "auditingSettings", "Microsoft.Sql/servers/auditingSettings"] dbTypes := ["Microsoft.Sql/servers/databases", "databases", "Microsoft.Sql/servers"] +types := ["Microsoft.Sql/servers/databases/auditingSettings", "auditingSettings", "Microsoft.Sql/servers/auditingSettings"] CxPolicy[result] { doc := input.document[i] - [path, value] = walk(doc) value.type == dbTypes[_] childrenArr := get_children(doc, value, path) - child := childrenArr[k].value[_] + child := childrenArr[k].value[i2] child.type == types[_] child_path := childrenArr[k].path [val, _] := arm_lib.getDefaultValueFromParametersIfPresent(doc, child.properties.state) lower(val) == "enabled" - not common_lib.valid_key(child.properties, "retentionDays") + + results := invalid_retention_days(doc, child, child_path, i2) + results != "" result := { "documentId": input.document[i].id, "resourceType": child.type, "resourceName": child.name, + "searchKey": results.searchKey, + "issueType": results.issueType, + "keyExpectedValue": results.keyExpectedValue, + "keyActualValue": results.keyActualValue, + "searchLine": results.searchLine + } +} + +invalid_retention_days(doc, child, child_path, i2) = results { + not common_lib.valid_key(child.properties, "retentionDays") + results := { "searchKey": sprintf("%s.name={{%s}}.properties", [common_lib.concat_path(child_path), child.name]), "issueType": "MissingAttribute", "keyExpectedValue": "'auditingSettings.properties.retentionDays' should be defined and above 90 days", "keyActualValue": "'auditingSettings.properties.retentionDays' is missing", - "searchLine": common_lib.build_search_line(child_path, ["properties"]), + "searchLine": get_searchLine(child_path, i2, ["properties"]) } -} - -CxPolicy[result] { - doc := input.document[i] - - [path, value] = walk(doc) - - value.type == dbTypes[_] - childrenArr := get_children(doc, value, path) - - child := childrenArr[k].value[_] - child.type == types[_] - - child_path := childrenArr[k].path - - [val, _] := arm_lib.getDefaultValueFromParametersIfPresent(doc, child.properties.state) - lower(val) == "enabled" +} else = results { [val_rd, val_rd_type] := arm_lib.getDefaultValueFromParametersIfPresent(doc, child.properties.retentionDays) - val_rd < 90 - result := { - "documentId": input.document[i].id, - "resourceType": child.type, - "resourceName": child.name, + results := { "searchKey": sprintf("%s.name={{%s}}.properties.retentionDays", [common_lib.concat_path(child_path), child.name]), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("'auditingSettings.properties.retentionDays' %s should be defined and above 90 days", [val_rd_type]), "keyActualValue": sprintf("'auditingSettings.properties.retentionDays' %s is %d", [val_rd_type, child.properties.retentionDays]), - "searchLine": common_lib.build_search_line(child_path, ["properties", "retentionDays"]), + "searchLine": get_searchLine(child_path, i2, ["properties", "retentionDays"]) } +} else = "" + +get_searchLine(path,index,path_to_field) = sk { + is_number(path[count(path) - 1]) + sk := common_lib.build_search_line(path, path_to_field) +} else = sk { + sk := common_lib.build_search_line(path, array.concat([index],path_to_field)) } get_children(doc, parent, path) = childArr { common_lib.valid_key(parent, "resources") childArr := [{"value": parent.resources, "path": array.concat(path, ["resources"])}] -} else = childArr { - not common_lib.valid_key(parent, "resources") - not common_lib.valid_key(parent, "dependsOn") - values := [x | - [path_child, value_child] := walk(doc) - value_child.name != parent.name - not common_lib.valid_key(value_child, "dependsOn") - startswith(value_child.name, parent.name) - x := {"value": [value_child], "path": path_child} - ] - count(values) > 0 - unique := {y | y := values[_]} - childArr := [y | y := unique[_]] } else = childArr { not common_lib.valid_key(parent, "resources") values := [x | @@ -89,9 +75,8 @@ get_children(doc, parent, path) = childArr { value_child.name != parent.name common_lib.valid_key(value_child, "dependsOn") d := value_child.dependsOn[_] - search_values := {"type": parent.type, "name": parent.name} - contains(d, search_values.type) - contains(d, search_values.name) + contains(d, parent.type) + contains(d, parent.name) x := {"value": [value_child], "path": path_child} ] count(values) > 0 diff --git a/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/negative4.bicep b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/negative4.bicep new file mode 100644 index 00000000000..08ec77b0b69 --- /dev/null +++ b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/negative4.bicep @@ -0,0 +1,21 @@ +resource sqlServer1 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'sqlServer1' + location: resourceGroup().location + tags: { + displayName: 'sqlServer1' + } + properties: { + administratorLogin: 'adminUsername' + administratorLoginPassword: 'adminPassword' + } +} + +resource auditingSetting 'Microsoft.Sql/servers/auditingSettings@2022-05-01-preview' = { + name: 'default' + parent: sqlServer1 + properties: { + state: 'Disabled' + isAzureMonitorTargetEnabled: true + retentionDays: 10 + } +} diff --git a/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/negative4.json b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/negative4.json new file mode 100644 index 00000000000..618fe456867 --- /dev/null +++ b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/negative4.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "8941966037535999376" + } + }, + "resources": [ + { + "type": "Microsoft.Sql/servers", + "apiVersion": "2021-02-01-preview", + "name": "sqlServer1", + "location": "[resourceGroup().location]", + "tags": { + "displayName": "sqlServer1" + }, + "properties": { + "administratorLogin": "adminUsername", + "administratorLoginPassword": "adminPassword" + } + }, + { + "type": "Microsoft.Sql/servers/auditingSettings", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}', 'sqlServer1', 'default')]", + "properties": { + "state": "Disabled", + "isAzureMonitorTargetEnabled": true, + "retentionDays": 10 + }, + "dependsOn": [ + "[resourceId('Microsoft.Sql/servers', 'sqlServer1')]" + ] + } + ] +} \ No newline at end of file diff --git a/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive7.bicep b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive7.bicep new file mode 100644 index 00000000000..d63a901007f --- /dev/null +++ b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive7.bicep @@ -0,0 +1,40 @@ +resource sqlServer1 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'sqlServer1' + location: resourceGroup().location + tags: { + displayName: 'sqlServer1' + } + properties: { + administratorLogin: 'adminUsername' + administratorLoginPassword: 'adminPassword' + } +} + +resource sqlServer1_sqlDatabase1 'Microsoft.Sql/servers/databases@2021-02-01-preview' = { + parent: sqlServer1 + name: 'sqlDatabase1' + location: resourceGroup().location + tags: { + displayName: 'sqlDatabase1' + } + properties: { + collation: 'SQL_Latin1_General_CP1_CI_AS' + edition: 'Basic' + maxSizeBytes: '1073741824' + requestedServiceObjectiveName: 'Basic' + } +} + +resource sqlServer1_sqlDatabase1_default 'Microsoft.Sql/servers/databases/auditingSettings@2021-02-01-preview' = { + parent: sqlServer1_sqlDatabase1 + name: 'default' + properties: { + auditActionsAndGroups: [ + 'DATABASE_LOGOUT_GROUP' + ] + isAzureMonitorTargetEnabled: true + isStorageSecondaryKeyInUse: true + queueDelayMs: 1000 + state: 'Enabled' + } +} diff --git a/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive7.json b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive7.json new file mode 100644 index 00000000000..577648bd646 --- /dev/null +++ b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive7.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "functions": [], + "variables": {}, + "resources": [ + { + "name": "sqlServer1", + "type": "Microsoft.Sql/servers", + "apiVersion": "2021-02-01-preview", + "location": "[resourceGroup().location]", + "tags": { + "displayName": "sqlServer1" + }, + "properties": { + "administratorLogin": "adminUsername", + "administratorLoginPassword": "adminPassword" + }, + "resources": [ + { + "name": "sqlServer1/sqlDatabase1", + "type": "Microsoft.Sql/servers/databases", + "apiVersion": "2021-02-01-preview", + "location": "[resourceGroup().location]", + "tags": { + "displayName": "sqlDatabase1" + }, + "properties": { + "collation": "SQL_Latin1_General_CP1_CI_AS", + "edition": "Basic", + "maxSizeBytes": "1073741824", + "requestedServiceObjectiveName": "Basic" + }, + "resources": [ + { + "type": "Microsoft.Sql/servers/databases/auditingSettings", + "apiVersion": "2021-02-01-preview", + "name": "sqlServer1/sqlDatabase1/default", + "properties": { + "auditActionsAndGroups": [ "DATABASE_LOGOUT_GROUP" ], + "isAzureMonitorTargetEnabled": true, + "isStorageSecondaryKeyInUse": true, + "queueDelayMs": 1000, + "state": "Enabled" + } + } + ] + } + ] + } + ], + "outputs": {} +} diff --git a/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive_expected_result.json b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive_expected_result.json index 380a046ce41..a3671a1324c 100644 --- a/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive_expected_result.json +++ b/assets/queries/azureResourceManager/sql_server_database_with_low_retention_days/test/positive_expected_result.json @@ -35,6 +35,12 @@ "line": 29, "filename": "positive6.json" }, + { + "queryName": "SQL Server Database With Unrecommended Retention Days", + "severity": "LOW", + "line": 40, + "filename": "positive7.json" + }, { "queryName": "SQL Server Database With Unrecommended Retention Days", "severity": "LOW", @@ -70,5 +76,11 @@ "severity": "LOW", "line": 16, "filename": "positive6.bicep" + }, + { + "queryName": "SQL Server Database With Unrecommended Retention Days", + "severity": "LOW", + "line": 31, + "filename": "positive7.bicep" } ] \ No newline at end of file