From e9bc41c3d3b9c0361776a1c09b9d41ae7462291a Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Tue, 10 Jun 2025 14:45:54 +0100 Subject: [PATCH 01/14] feat: Add Database support --- .../bicep/frameworks/Microsoft/Databases.qll | 106 ++++++++++++++++++ .../frameworks/databases/Databases.expected | 9 ++ .../frameworks/databases/Databases.ql | 5 + .../frameworks/databases/app.bicep | 93 +++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll create mode 100644 ql/test/library-tests/frameworks/databases/Databases.expected create mode 100644 ql/test/library-tests/frameworks/databases/Databases.ql create mode 100644 ql/test/library-tests/frameworks/databases/app.bicep diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll new file mode 100644 index 0000000..32960d1 --- /dev/null +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -0,0 +1,106 @@ +private import bicep + +module Databases { + /** + * Base class for all database resources in Azure. + */ + abstract class DatabaseResource extends Resource { + /** + * Returns the type of the database resource. + */ + abstract string databaseType(); + + override string toString() { + result = "DatabaseResource[" + this.databaseType() + "]" + } + } + + /** + * Azure SQL Database/Managed Instance + */ + class SqlServers extends DatabaseResource, Resource { + SqlServers() { this.getResourceType().regexpMatch("^Microsoft.Sql/servers@.*") } + + override string databaseType() { result = "sql" } + } + + /** + * Azure Cosmos DB + */ + class CosmosDBAccounts extends DatabaseResource, Resource { + CosmosDBAccounts() { + this.getResourceType().regexpMatch("^Microsoft.DocumentDB/databaseAccounts@.*") + } + + override string databaseType() { result = "cosmosdb" } + } + + /** + * Azure Database for PostgreSQL + */ + class PostgreSQLServers extends DatabaseResource, Resource { + PostgreSQLServers() { + this.getResourceType().regexpMatch("^Microsoft.DBforPostgreSQL/servers@.*") + } + + override string databaseType() { result = "postgresql" } + } + + /** + * Azure Database for MySQL + */ + class MySQLServers extends DatabaseResource, Resource { + MySQLServers() { this.getResourceType().regexpMatch("^Microsoft.DBforMySQL/servers@.*") } + + override string databaseType() { result = "mysql" } + } + + /** + * Azure Database for MariaDB + */ + class MariaDBServers extends DatabaseResource, Resource { + MariaDBServers() { this.getResourceType().regexpMatch("^Microsoft.DBforMariaDB/servers@.*") } + + override string databaseType() { result = "mariadb" } + } + + /** + * Azure Data Lake Store Gen1 + */ + class DataLakeStoreAccounts extends DatabaseResource, Resource { + DataLakeStoreAccounts() { + this.getResourceType().regexpMatch("^Microsoft.DataLakeStore/accounts@.*") + } + + override string databaseType() { result = "datalakestore" } + } + + /** + * Azure Cache for Redis + */ + class RedisCaches extends DatabaseResource, Resource { + RedisCaches() { this.getResourceType().regexpMatch("^Microsoft.Cache/Redis@.*") } + + override string databaseType() { result = "redis" } + } + + /** + * Azure Data Explorer (Kusto) + */ + class KustoClusters extends DatabaseResource, Resource { + KustoClusters() { this.getResourceType().regexpMatch("^Microsoft.Kusto/Clusters@.*") } + + override string databaseType() { result = "kusto" } + } + + /** + * Azure Arc-enabled SQL Managed Instance + */ + class ArcSqlManagedInstances extends DatabaseResource, Resource { + ArcSqlManagedInstances() { + this.getResourceType().regexpMatch("^Microsoft.AzureArcData/sqlManagedInstances@.*") + } + + override string databaseType() { result = "arc-sql-managed-instance" } + } +} diff --git a/ql/test/library-tests/frameworks/databases/Databases.expected b/ql/test/library-tests/frameworks/databases/Databases.expected new file mode 100644 index 0000000..9afc338 --- /dev/null +++ b/ql/test/library-tests/frameworks/databases/Databases.expected @@ -0,0 +1,9 @@ +| app.bicep:2:1:9:1 | DatabaseResource[sql] | +| app.bicep:12:1:18:1 | DatabaseResource[cosmosdb] | +| app.bicep:21:1:29:1 | DatabaseResource[postgresql] | +| app.bicep:32:1:40:1 | DatabaseResource[mysql] | +| app.bicep:43:1:51:1 | DatabaseResource[mariadb] | +| app.bicep:54:1:58:1 | DatabaseResource[datalakestore] | +| app.bicep:61:1:71:1 | DatabaseResource[redis] | +| app.bicep:74:1:83:1 | DatabaseResource[kusto] | +| app.bicep:86:1:93:1 | DatabaseResource[arc-sql-managed-instance] | diff --git a/ql/test/library-tests/frameworks/databases/Databases.ql b/ql/test/library-tests/frameworks/databases/Databases.ql new file mode 100644 index 0000000..7066bfd --- /dev/null +++ b/ql/test/library-tests/frameworks/databases/Databases.ql @@ -0,0 +1,5 @@ +import bicep + +query predicate databases(Databases::DatabaseResource dr) { + any() +} diff --git a/ql/test/library-tests/frameworks/databases/app.bicep b/ql/test/library-tests/frameworks/databases/app.bicep new file mode 100644 index 0000000..f735ea6 --- /dev/null +++ b/ql/test/library-tests/frameworks/databases/app.bicep @@ -0,0 +1,93 @@ +// Azure SQL Database +resource sqlDb 'Microsoft.Sql/servers@2022-02-01' = { + name: 'sqlserver1' + location: 'eastus' + properties: { + administratorLogin: 'adminuser' + administratorLoginPassword: 'P@ssw0rd!' + } +} + +// Azure Cosmos DB +resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2022-03-15' = { + name: 'cosmosdb1' + location: 'eastus' + properties: { + databaseAccountOfferType: 'Standard' + } +} + +// Azure Database for PostgreSQL +resource postgresqlDb 'Microsoft.DBforPostgreSQL/servers@2022-01-20' = { + name: 'pgserver1' + location: 'eastus' + properties: { + administratorLogin: 'pgadmin' + administratorLoginPassword: 'P@ssw0rd!' + version: '11' + } +} + +// Azure Database for MySQL +resource mysqlDb 'Microsoft.DBforMySQL/servers@2022-01-20' = { + name: 'mysqlserver1' + location: 'eastus' + properties: { + administratorLogin: 'mysqladmin' + administratorLoginPassword: 'P@ssw0rd!' + version: '5.7' + } +} + +// Azure Database for MariaDB +resource mariadbDb 'Microsoft.DBforMariaDB/servers@2018-06-01' = { + name: 'mariadbserver1' + location: 'eastus' + properties: { + administratorLogin: 'mariadbadmin' + administratorLoginPassword: 'P@ssw0rd!' + version: '10.2' + } +} + +// Azure Data Lake Store Gen1 +resource datalakeStore 'Microsoft.DataLakeStore/accounts@2016-11-01' = { + name: 'datalakestore1' + location: 'eastus' + properties: {} +} + +// Azure Cache for Redis +resource redisCache 'Microsoft.Cache/Redis@2023-04-01' = { + name: 'rediscache1' + location: 'eastus' + properties: { + sku: { + name: 'Basic' + family: 'C' + capacity: 0 + } + } +} + +// Azure Data Explorer (Kusto) +resource kustoCluster 'Microsoft.Kusto/Clusters@2023-05-02' = { + name: 'kustocluster1' + location: 'eastus' + properties: { + sku: { + name: 'Standard_D13_v2' + capacity: 2 + } + } +} + +// Azure Arc-enabled SQL Managed Instance +resource arcSqlMi 'Microsoft.AzureArcData/sqlManagedInstances@2022-03-01-preview' = { + name: 'arcsqlmi1' + location: 'eastus' + properties: { + administratorLogin: 'arcadmin' + administratorLoginPassword: 'P@ssw0rd!' + } +} From aceba46d63a632634e428351f26fa50bb86b36a0 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Tue, 10 Jun 2025 14:46:26 +0100 Subject: [PATCH 02/14] feat: Add Microsoft.Databases imports to Frameworks.qll --- ql/lib/codeql/bicep/Frameworks.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ql/lib/codeql/bicep/Frameworks.qll b/ql/lib/codeql/bicep/Frameworks.qll index 875e6e9..201e80b 100644 --- a/ql/lib/codeql/bicep/Frameworks.qll +++ b/ql/lib/codeql/bicep/Frameworks.qll @@ -1,3 +1,4 @@ import frameworks.Microsoft.Compute import frameworks.Microsoft.Network -import frameworks.Microsoft.Storage \ No newline at end of file +import frameworks.Microsoft.Storage +import frameworks.Microsoft.Databases \ No newline at end of file From 5ae3636ca2bdbc136e31d0630b28b47ba8b4ff6f Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Tue, 10 Jun 2025 15:15:01 +0100 Subject: [PATCH 03/14] feat: Update Databases support --- .../bicep/frameworks/Microsoft/Databases.qll | 89 ++++++++++++++++++- .../frameworks/databases/Databases.expected | 22 +++-- .../frameworks/databases/app.bicep | 79 +++++++++++++++- 3 files changed, 176 insertions(+), 14 deletions(-) diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll index 32960d1..18657e2 100644 --- a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -10,8 +10,28 @@ module Databases { */ abstract string databaseType(); - override string toString() { - result = "DatabaseResource[" + this.databaseType() + "]" + override string toString() { result = "DatabaseResource[" + this.databaseType() + "]" } + + DatabaseProperties::Properties getProperties() { result = this.getProperty("properties") } + + string version() { + result = this.getProperties().getProperty("version").(StringLiteral).getValue() + } + + string sslEnforcement() { + result = this.getProperties().getProperty("sslEnforcement").(StringLiteral).getValue() + } + + string infrastructureEncryption() { + result = this.getProperties().getProperty("infrastructureEncryption").(StringLiteral).getValue() + } + + string minimalTlsVersion() { + result = this.getProperties().getProperty("minimalTlsVersion").(StringLiteral).getValue() + } + + DatabaseProperties::StorageProfile getStorageProfile() { + result = this.getProperties().getProperty("storageProfile") } } @@ -33,6 +53,19 @@ module Databases { } override string databaseType() { result = "cosmosdb" } + + string databaseAccountOfferType() { + result = + this.getProperties().getProperty("databaseAccountOfferType").(StringLiteral).getValue() + } + + boolean isEnableMultipleWriteLocations() { + result = this.getProperties().getProperty("enableMultipleWriteLocations").(Boolean).getBool() + } + + DatabaseProperties::BackupPolicy getBackupPolicy() { + result = this.getProperties().getProperty("backupPolicy") + } } /** @@ -103,4 +136,56 @@ module Databases { override string databaseType() { result = "arc-sql-managed-instance" } } + + module DatabaseProperties { + class Properties extends Object { + private Resource resource; + + Properties() { this = resource.getProperty("properties") } + + Resource getResource() { result = resource } + } + + class Backup extends Object { + private Properties properties; + + Backup() { this = properties.getProperty("backup") } + + string toString() { result = "Backup" } + + string geoRedundantBackup() { + result = this.getProperty("geoRedundantBackup").(StringLiteral).getValue() + } + } + + class BackupPolicy extends Object { + private Properties properties; + + BackupPolicy() { this = properties.getProperty("backupPolicy") } + + string toString() { result = "BackupPolicy" } + + string getBackupPolicyType() { result = this.getProperty("type").(StringLiteral).getValue() } + + Expr getBackupRetentionDays() { result = this.getProperty("backupRetentionDays") } + + Expr getBackupStorageRedundancy() { result = this.getProperty("backupStorageRedundancy") } + } + + class StorageProfile extends Object { + private Properties properties; + + StorageProfile() { this = properties.getProperty("storageProfile") } + + string toString() { result = "StorageProfile" } + + int storageMB() { + result = this.getProperty("storageMB").(Number).getValue() + } + + string autoGrow() { + result = this.getProperty("autoGrow").(StringLiteral).getValue() + } + } + } } diff --git a/ql/test/library-tests/frameworks/databases/Databases.expected b/ql/test/library-tests/frameworks/databases/Databases.expected index 9afc338..5ff4516 100644 --- a/ql/test/library-tests/frameworks/databases/Databases.expected +++ b/ql/test/library-tests/frameworks/databases/Databases.expected @@ -1,9 +1,13 @@ -| app.bicep:2:1:9:1 | DatabaseResource[sql] | -| app.bicep:12:1:18:1 | DatabaseResource[cosmosdb] | -| app.bicep:21:1:29:1 | DatabaseResource[postgresql] | -| app.bicep:32:1:40:1 | DatabaseResource[mysql] | -| app.bicep:43:1:51:1 | DatabaseResource[mariadb] | -| app.bicep:54:1:58:1 | DatabaseResource[datalakestore] | -| app.bicep:61:1:71:1 | DatabaseResource[redis] | -| app.bicep:74:1:83:1 | DatabaseResource[kusto] | -| app.bicep:86:1:93:1 | DatabaseResource[arc-sql-managed-instance] | +| app.bicep:2:1:8:1 | DatabaseResource[cosmosdb] | +| app.bicep:11:1:30:1 | DatabaseResource[cosmosdb] | +| app.bicep:33:1:42:1 | DatabaseResource[cosmosdb] | +| app.bicep:45:1:58:1 | DatabaseResource[cosmosdb] | +| app.bicep:62:1:69:1 | DatabaseResource[sql] | +| app.bicep:72:1:78:1 | DatabaseResource[cosmosdb] | +| app.bicep:81:1:95:1 | DatabaseResource[postgresql] | +| app.bicep:98:1:111:1 | DatabaseResource[mysql] | +| app.bicep:114:1:124:1 | DatabaseResource[mariadb] | +| app.bicep:127:1:131:1 | DatabaseResource[datalakestore] | +| app.bicep:134:1:144:1 | DatabaseResource[redis] | +| app.bicep:147:1:156:1 | DatabaseResource[kusto] | +| app.bicep:159:1:166:1 | DatabaseResource[arc-sql-managed-instance] | diff --git a/ql/test/library-tests/frameworks/databases/app.bicep b/ql/test/library-tests/frameworks/databases/app.bicep index f735ea6..2246697 100644 --- a/ql/test/library-tests/frameworks/databases/app.bicep +++ b/ql/test/library-tests/frameworks/databases/app.bicep @@ -1,3 +1,63 @@ +// Cosmos DB Account with default settings +resource cosmosDbDefault 'Microsoft.DocumentDB/databaseAccounts@2022-03-15' = { + name: 'cosmosdb-default' + location: 'eastus' + properties: { + databaseAccountOfferType: 'Standard' + } +} + +// Cosmos DB Account with geo-redundancy and multi-region write +resource cosmosDbGeo 'Microsoft.DocumentDB/databaseAccounts@2022-03-15' = { + name: 'cosmosdb-geo' + location: 'eastus' + properties: { + databaseAccountOfferType: 'Standard' + enableMultipleWriteLocations: true + locations: [ + { + locationName: 'eastus' + failoverPriority: 0 + isZoneRedundant: false + } + { + locationName: 'westus' + failoverPriority: 1 + isZoneRedundant: true + } + ] + } +} + +// Cosmos DB Account with continuous backup +resource cosmosDbContinuousBackup 'Microsoft.DocumentDB/databaseAccounts@2022-03-15' = { + name: 'cosmosdb-continuous' + location: 'eastus' + properties: { + databaseAccountOfferType: 'Standard' + backupPolicy: { + type: 'Continuous' + } + } +} + +// Cosmos DB Account with periodic backup and custom retention +resource cosmosDbPeriodicBackup 'Microsoft.DocumentDB/databaseAccounts@2022-03-15' = { + name: 'cosmosdb-periodic' + location: 'eastus' + properties: { + databaseAccountOfferType: 'Standard' + backupPolicy: { + type: 'Periodic' + periodicModeProperties: { + backupIntervalInMinutes: 240 + backupRetentionIntervalInHours: 24 + } + } + } +} + + // Azure SQL Database resource sqlDb 'Microsoft.Sql/servers@2022-02-01' = { name: 'sqlserver1' @@ -17,7 +77,7 @@ resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2022-03-15' = { } } -// Azure Database for PostgreSQL +// Azure Database for PostgreSQL with geo-redundant backup and high availability resource postgresqlDb 'Microsoft.DBforPostgreSQL/servers@2022-01-20' = { name: 'pgserver1' location: 'eastus' @@ -25,10 +85,16 @@ resource postgresqlDb 'Microsoft.DBforPostgreSQL/servers@2022-01-20' = { administratorLogin: 'pgadmin' administratorLoginPassword: 'P@ssw0rd!' version: '11' + backup': { + geoRedundantBackup: 'Enabled' + } + highAvailability: { + mode: 'ZoneRedundant' + } } } -// Azure Database for MySQL +// Azure Database for MySQL with SSL enforcement and auto-grow resource mysqlDb 'Microsoft.DBforMySQL/servers@2022-01-20' = { name: 'mysqlserver1' location: 'eastus' @@ -36,10 +102,15 @@ resource mysqlDb 'Microsoft.DBforMySQL/servers@2022-01-20' = { administratorLogin: 'mysqladmin' administratorLoginPassword: 'P@ssw0rd!' version: '5.7' + sslEnforcement: 'Enabled' + storageProfile: { + storageMB: 51200 + autoGrow: 'Enabled' + } } } -// Azure Database for MariaDB +// Azure Database for MariaDB with backup retention and geo-redundancy resource mariadbDb 'Microsoft.DBforMariaDB/servers@2018-06-01' = { name: 'mariadbserver1' location: 'eastus' @@ -47,6 +118,8 @@ resource mariadbDb 'Microsoft.DBforMariaDB/servers@2018-06-01' = { administratorLogin: 'mariadbadmin' administratorLoginPassword: 'P@ssw0rd!' version: '10.2' + backupRetentionDays: 14 + geoRedundantBackup: 'Enabled' } } From dc071ccfc12819a1b01647910e76264c00ee05ce Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Tue, 10 Jun 2025 15:16:43 +0100 Subject: [PATCH 04/14] docs: Add Database docs --- .../bicep/frameworks/Microsoft/Databases.qll | 159 ++++++++++++++++-- 1 file changed, 149 insertions(+), 10 deletions(-) diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll index 18657e2..45b2103 100644 --- a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -3,186 +3,325 @@ private import bicep module Databases { /** * Base class for all database resources in Azure. + * Provides common properties and methods for Azure database resources. */ abstract class DatabaseResource extends Resource { /** - * Returns the type of the database resource. + * Returns the type of the database resource (e.g., sql, postgresql, etc). */ abstract string databaseType(); + /** + * Returns a string representation of the database resource. + */ override string toString() { result = "DatabaseResource[" + this.databaseType() + "]" } + /** + * Returns the properties object for the database resource. + */ DatabaseProperties::Properties getProperties() { result = this.getProperty("properties") } + /** + * Returns the version property of the database resource, if present. + */ string version() { result = this.getProperties().getProperty("version").(StringLiteral).getValue() } + /** + * Returns the sslEnforcement property of the database resource, if present. + */ string sslEnforcement() { result = this.getProperties().getProperty("sslEnforcement").(StringLiteral).getValue() } + /** + * Returns the infrastructureEncryption property of the database resource, if present. + */ string infrastructureEncryption() { result = this.getProperties().getProperty("infrastructureEncryption").(StringLiteral).getValue() } + /** + * Returns the minimalTlsVersion property of the database resource, if present. + */ string minimalTlsVersion() { result = this.getProperties().getProperty("minimalTlsVersion").(StringLiteral).getValue() } + /** + * Returns the storage profile for the database resource, if present. + */ DatabaseProperties::StorageProfile getStorageProfile() { result = this.getProperties().getProperty("storageProfile") } } /** - * Azure SQL Database/Managed Instance + * Represents an Azure SQL Database or Managed Instance resource. */ class SqlServers extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure SQL Database/Managed Instance resources. + */ SqlServers() { this.getResourceType().regexpMatch("^Microsoft.Sql/servers@.*") } + /** + * Returns the type of the database resource ("sql"). + */ override string databaseType() { result = "sql" } } /** - * Azure Cosmos DB + * Represents an Azure Cosmos DB account resource. */ class CosmosDBAccounts extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure Cosmos DB account resources. + */ CosmosDBAccounts() { this.getResourceType().regexpMatch("^Microsoft.DocumentDB/databaseAccounts@.*") } + /** + * Returns the type of the database resource ("cosmosdb"). + */ override string databaseType() { result = "cosmosdb" } + /** + * Returns the databaseAccountOfferType property of the Cosmos DB account. + */ string databaseAccountOfferType() { result = this.getProperties().getProperty("databaseAccountOfferType").(StringLiteral).getValue() } + /** + * Returns true if multiple write locations are enabled for the Cosmos DB account. + */ boolean isEnableMultipleWriteLocations() { result = this.getProperties().getProperty("enableMultipleWriteLocations").(Boolean).getBool() } + /** + * Returns the backup policy for the Cosmos DB account. + */ DatabaseProperties::BackupPolicy getBackupPolicy() { result = this.getProperties().getProperty("backupPolicy") } } /** - * Azure Database for PostgreSQL + * Represents an Azure Database for PostgreSQL server resource. */ class PostgreSQLServers extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure Database for PostgreSQL server resources. + */ PostgreSQLServers() { this.getResourceType().regexpMatch("^Microsoft.DBforPostgreSQL/servers@.*") } + /** + * Returns the type of the database resource ("postgresql"). + */ override string databaseType() { result = "postgresql" } } /** - * Azure Database for MySQL + * Represents an Azure Database for MySQL server resource. */ class MySQLServers extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure Database for MySQL server resources. + */ MySQLServers() { this.getResourceType().regexpMatch("^Microsoft.DBforMySQL/servers@.*") } + /** + * Returns the type of the database resource ("mysql"). + */ override string databaseType() { result = "mysql" } } /** - * Azure Database for MariaDB + * Represents an Azure Database for MariaDB server resource. */ class MariaDBServers extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure Database for MariaDB server resources. + */ MariaDBServers() { this.getResourceType().regexpMatch("^Microsoft.DBforMariaDB/servers@.*") } + /** + * Returns the type of the database resource ("mariadb"). + */ override string databaseType() { result = "mariadb" } } /** - * Azure Data Lake Store Gen1 + * Represents an Azure Data Lake Store Gen1 account resource. */ class DataLakeStoreAccounts extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure Data Lake Store Gen1 account resources. + */ DataLakeStoreAccounts() { this.getResourceType().regexpMatch("^Microsoft.DataLakeStore/accounts@.*") } + /** + * Returns the type of the database resource ("datalakestore"). + */ override string databaseType() { result = "datalakestore" } } /** - * Azure Cache for Redis + * Represents an Azure Cache for Redis resource. */ class RedisCaches extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure Cache for Redis resources. + */ RedisCaches() { this.getResourceType().regexpMatch("^Microsoft.Cache/Redis@.*") } + /** + * Returns the type of the database resource ("redis"). + */ override string databaseType() { result = "redis" } } /** - * Azure Data Explorer (Kusto) + * Represents an Azure Data Explorer (Kusto) cluster resource. */ class KustoClusters extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure Data Explorer (Kusto) cluster resources. + */ KustoClusters() { this.getResourceType().regexpMatch("^Microsoft.Kusto/Clusters@.*") } + /** + * Returns the type of the database resource ("kusto"). + */ override string databaseType() { result = "kusto" } } /** - * Azure Arc-enabled SQL Managed Instance + * Represents an Azure Arc-enabled SQL Managed Instance resource. */ class ArcSqlManagedInstances extends DatabaseResource, Resource { + /** + * Constructs an instance for Azure Arc-enabled SQL Managed Instance resources. + */ ArcSqlManagedInstances() { this.getResourceType().regexpMatch("^Microsoft.AzureArcData/sqlManagedInstances@.*") } + /** + * Returns the type of the database resource ("arc-sql-managed-instance"). + */ override string databaseType() { result = "arc-sql-managed-instance" } } module DatabaseProperties { + /** + * Represents the properties object for a database resource. + */ class Properties extends Object { private Resource resource; + /** + * Constructs a Properties object for the given resource. + */ Properties() { this = resource.getProperty("properties") } + /** + * Returns the underlying resource for these properties. + */ Resource getResource() { result = resource } } + /** + * Represents the backup object within database properties. + */ class Backup extends Object { private Properties properties; + /** + * Constructs a Backup object for the given properties. + */ Backup() { this = properties.getProperty("backup") } + /** + * Returns a string representation of the backup object. + */ string toString() { result = "Backup" } + /** + * Returns the geoRedundantBackup property of the backup object. + */ string geoRedundantBackup() { result = this.getProperty("geoRedundantBackup").(StringLiteral).getValue() } } + /** + * Represents the backup policy object within database properties. + */ class BackupPolicy extends Object { private Properties properties; + /** + * Constructs a BackupPolicy object for the given properties. + */ BackupPolicy() { this = properties.getProperty("backupPolicy") } + /** + * Returns a string representation of the backup policy object. + */ string toString() { result = "BackupPolicy" } + /** + * Returns the type of the backup policy. + */ string getBackupPolicyType() { result = this.getProperty("type").(StringLiteral).getValue() } + /** + * Returns the backupRetentionDays property of the backup policy. + */ Expr getBackupRetentionDays() { result = this.getProperty("backupRetentionDays") } + /** + * Returns the backupStorageRedundancy property of the backup policy. + */ Expr getBackupStorageRedundancy() { result = this.getProperty("backupStorageRedundancy") } } + /** + * Represents the storage profile object within database properties. + */ class StorageProfile extends Object { private Properties properties; + /** + * Constructs a StorageProfile object for the given properties. + */ StorageProfile() { this = properties.getProperty("storageProfile") } + /** + * Returns a string representation of the storage profile object. + */ string toString() { result = "StorageProfile" } + /** + * Returns the storageMB property of the storage profile. + */ int storageMB() { result = this.getProperty("storageMB").(Number).getValue() } + /** + * Returns the autoGrow property of the storage profile. + */ string autoGrow() { result = this.getProperty("autoGrow").(StringLiteral).getValue() } From 8763b8c0790d08fdf65b8fc1e85eed5b57062239 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 10:45:30 +0100 Subject: [PATCH 05/14] feat: Update Resource, Databases and add public resource concept --- ql/lib/codeql/bicep/Concepts.qll | 10 ++++++++++ ql/lib/codeql/bicep/ast/Resources.qll | 11 +++++++---- .../bicep/frameworks/Microsoft/Databases.qll | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/ql/lib/codeql/bicep/Concepts.qll b/ql/lib/codeql/bicep/Concepts.qll index 5ead729..3e3fc0d 100644 --- a/ql/lib/codeql/bicep/Concepts.qll +++ b/ql/lib/codeql/bicep/Concepts.qll @@ -1,2 +1,12 @@ private import codeql.bicep.AST private import codeql.bicep.CFG + +/** + * A Public Resource is a resource that is publicly accessible to the Internet. + */ +abstract class PublicResource extends Resource { + /** + * Returns the property that indicates public access. + */ + abstract Expr getPublicAccessProperty(); +} diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index 1f59998..436f641 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -3,7 +3,6 @@ private import codeql.Locations private import Expr private import Idents private import Literals - private import internal.ResourceDeclaration private import internal.ObjectProperty private import internal.Object @@ -79,7 +78,6 @@ Resource resolveResource(Expr expr) { ) } - class Resource extends TResource { private ResourceDeclaration resource; @@ -89,10 +87,15 @@ class Resource extends TResource { exists(StringLiteral sl | sl = resource.getName() | result = sl.getValue()) } - Expr getProperty(string name) { - result = resource.getProperty(name) + string getName() { + exists(StringLiteral name | + name = resource.getProperty("name") and + result = name.getValue() + ) } + Expr getProperty(string name) { result = resource.getProperty(name) } + Resource getParent() { result = resolveResource(this.getProperty("parent")) } string toString() { result = resource.toString() } diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll index 45b2103..7159971 100644 --- a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -1,4 +1,5 @@ private import bicep +private import codeql.bicep.Concepts module Databases { /** @@ -28,6 +29,10 @@ module Databases { result = this.getProperties().getProperty("version").(StringLiteral).getValue() } + string publicNetworkAccess() { + result = this.getProperties().getProperty("publicNetworkAccess").(StringLiteral).getValue() + } + /** * Returns the sslEnforcement property of the database resource, if present. */ @@ -222,6 +227,19 @@ module Databases { override string databaseType() { result = "arc-sql-managed-instance" } } + class PublicDatabaseResource extends PublicResource { + private DatabaseResource database; + + PublicDatabaseResource() { + database.publicNetworkAccess() = "Enabled" and + this = database + } + + override Expr getPublicAccessProperty() { + result = database.getProperties().getProperty("publicNetworkAccess") + } + } + module DatabaseProperties { /** * Represents the properties object for a database resource. From 67e4b704c91f7a8c934bab7e06253bae87c30c3a Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 10:46:01 +0100 Subject: [PATCH 06/14] feat(query): Add Public Resource --- ql/src/security/CWE-200/PublicResource.md | 27 +++++++++++++++++++ ql/src/security/CWE-200/PublicResource.ql | 18 +++++++++++++ .../PublicResource/PublicBucket.expected | 3 +++ .../CWE-200/PublicResource/PublicBucket.qlref | 1 + .../security/CWE-200/PublicResource/app.bicep | 10 +++++++ 5 files changed, 59 insertions(+) create mode 100644 ql/src/security/CWE-200/PublicResource.md create mode 100644 ql/src/security/CWE-200/PublicResource.ql create mode 100644 ql/test/queries-tests/security/CWE-200/PublicResource/PublicBucket.expected create mode 100644 ql/test/queries-tests/security/CWE-200/PublicResource/PublicBucket.qlref create mode 100644 ql/test/queries-tests/security/CWE-200/PublicResource/app.bicep diff --git a/ql/src/security/CWE-200/PublicResource.md b/ql/src/security/CWE-200/PublicResource.md new file mode 100644 index 0000000..f16e3c0 --- /dev/null +++ b/ql/src/security/CWE-200/PublicResource.md @@ -0,0 +1,27 @@ +# Internal Resource is Public to the Internet + +This query detects Azure resources that are inadvertently exposed to the public internet. Publicly accessible resources can be targeted by attackers, leading to data breaches, service disruption, or unauthorized access. It is a security best practice to restrict access to internal resources by using private endpoints, network security groups, or firewalls to limit exposure. + +## Bad Example: Publicly Accessible Resource + +```bicep +resource storage 'Microsoft.Storage/storageAccounts@2021-02-01' = { + name: 'publicstorage' + location: 'eastus' + properties: { + allowBlobPublicAccess: true // BAD: Public access is enabled + } +} +``` + +## Good Example: Internal-Only Resource + +```bicep +resource storage 'Microsoft.Storage/storageAccounts@2021-02-01' = { + name: 'privatestorage' + location: 'eastus' + properties: { + allowBlobPublicAccess: false // GOOD: Public access is disabled + } +} +``` diff --git a/ql/src/security/CWE-200/PublicResource.ql b/ql/src/security/CWE-200/PublicResource.ql new file mode 100644 index 0000000..0e03ac7 --- /dev/null +++ b/ql/src/security/CWE-200/PublicResource.ql @@ -0,0 +1,18 @@ +/** + * @name Internal Resource Public to the Internet + * @description Internal resources should not be publicly accessible to the Internet. + * @kind problem + * @problem.severity error + * @security-severity 8.0 + * @precision high + * @id bicep/public-resource + * @tags security + * bicep + * azure + */ + +import bicep + +from PublicResource resource +select resource.getPublicAccessProperty(), + "Resource '" + resource.getName() + "' is publicly accessible to the Internet." diff --git a/ql/test/queries-tests/security/CWE-200/PublicResource/PublicBucket.expected b/ql/test/queries-tests/security/CWE-200/PublicResource/PublicBucket.expected new file mode 100644 index 0000000..6191f85 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-200/PublicResource/PublicBucket.expected @@ -0,0 +1,3 @@ +| app.bicep:6:26:6:34 | String | Resource 'publicdbserver' is publicly accessible to the Internet. | +| app.bicep:6:26:6:34 | String | Resource 'publicdbserver' is publicly accessible to the Internet. | +| app.bicep:6:26:6:34 | String | Resource 'publicdbserver' is publicly accessible to the Internet. | diff --git a/ql/test/queries-tests/security/CWE-200/PublicResource/PublicBucket.qlref b/ql/test/queries-tests/security/CWE-200/PublicResource/PublicBucket.qlref new file mode 100644 index 0000000..ec8b741 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-200/PublicResource/PublicBucket.qlref @@ -0,0 +1 @@ +security/CWE-200/PublicResource.ql \ No newline at end of file diff --git a/ql/test/queries-tests/security/CWE-200/PublicResource/app.bicep b/ql/test/queries-tests/security/CWE-200/PublicResource/app.bicep new file mode 100644 index 0000000..877cd6d --- /dev/null +++ b/ql/test/queries-tests/security/CWE-200/PublicResource/app.bicep @@ -0,0 +1,10 @@ +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'publicdbserver' + location: 'eastus' + properties: { + version: '12.0' + publicNetworkAccess: 'Enabled' // BAD: Database is publicly accessible + minimalTlsVersion: '1.0' // BAD: Weak TLS version + sslEnforcement: 'Disabled' // BAD: SSL enforcement is disabled + } +} \ No newline at end of file From bfce75e184d0f42861c98c7a8eb06257ab8fa141 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 10:46:16 +0100 Subject: [PATCH 07/14] feat: Update Bicep library --- ql/lib/bicep.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ql/lib/bicep.qll b/ql/lib/bicep.qll index 1cb11d3..904cbc4 100644 --- a/ql/lib/bicep.qll +++ b/ql/lib/bicep.qll @@ -2,5 +2,8 @@ import codeql.Locations import codeql.files.FileSystem // AST import codeql.bicep.AST +// CFG +import codeql.bicep.CFG // Frameworks import codeql.bicep.Frameworks +import codeql.bicep.Concepts From 824d2b754f07de8ef53a34d1b98082dff27eabcf Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 11:07:07 +0100 Subject: [PATCH 08/14] feat: Update concepts --- ql/lib/codeql/bicep/Concepts.qll | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/ql/lib/codeql/bicep/Concepts.qll b/ql/lib/codeql/bicep/Concepts.qll index 3e3fc0d..00c1c8a 100644 --- a/ql/lib/codeql/bicep/Concepts.qll +++ b/ql/lib/codeql/bicep/Concepts.qll @@ -5,8 +5,26 @@ private import codeql.bicep.CFG * A Public Resource is a resource that is publicly accessible to the Internet. */ abstract class PublicResource extends Resource { + /** + * Returns the property that indicates public access. + */ + abstract Expr getPublicAccessProperty(); +} + +module Cryptography { + abstract class WeakTlsVersion extends Resource { + abstract Expr getWeakTlsVersionProperty(); + /** - * Returns the property that indicates public access. + * Returns true if the resource has a weak TLS version. + * + * 1.0 and 1.1 are considered weak TLS versions. */ - abstract Expr getPublicAccessProperty(); + predicate hasWeakTlsVersion() { + exists(StringLiteral literal | + literal = this.getWeakTlsVersionProperty() and + literal.getValue().regexpMatch("^(1\\.0|1\\.1)$") + ) + } + } } From 184bfbee16be79a75d55e937210278576be7ee9b Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 11:07:22 +0100 Subject: [PATCH 09/14] feat: Add Database TLS version --- ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll index 7159971..729db0b 100644 --- a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -240,6 +240,12 @@ module Databases { } } + class WeakDatabaseTlsVersion extends Cryptography::WeakTlsVersion instanceof DatabaseResource { + override Expr getWeakTlsVersionProperty() { + result = DatabaseResource.super.getProperties().getProperty("minimalTlsVersion") + } + } + module DatabaseProperties { /** * Represents the properties object for a database resource. From 7d231a7621085d6296b23a2935dea613563303c2 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 11:11:10 +0100 Subject: [PATCH 10/14] feat: Small update to concepts --- ql/lib/codeql/bicep/Concepts.qll | 2 +- ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ql/lib/codeql/bicep/Concepts.qll b/ql/lib/codeql/bicep/Concepts.qll index 00c1c8a..346c609 100644 --- a/ql/lib/codeql/bicep/Concepts.qll +++ b/ql/lib/codeql/bicep/Concepts.qll @@ -13,7 +13,7 @@ abstract class PublicResource extends Resource { module Cryptography { abstract class WeakTlsVersion extends Resource { - abstract Expr getWeakTlsVersionProperty(); + abstract StringLiteral getWeakTlsVersionProperty(); /** * Returns true if the resource has a weak TLS version. diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll index 729db0b..86ac606 100644 --- a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -241,7 +241,7 @@ module Databases { } class WeakDatabaseTlsVersion extends Cryptography::WeakTlsVersion instanceof DatabaseResource { - override Expr getWeakTlsVersionProperty() { + override StringLiteral getWeakTlsVersionProperty() { result = DatabaseResource.super.getProperties().getProperty("minimalTlsVersion") } } From 9f0114c73af48439b3182f30da8a5d4167767bde Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 11:11:33 +0100 Subject: [PATCH 11/14] feat(query): Add Weak TLS Version query --- ql/src/security/CWE-327/WeakTlsVersion.md | 27 +++++++++++++++++++ ql/src/security/CWE-327/WeakTlsVersion.ql | 19 +++++++++++++ .../WeakTlsVersion/WeakTlsVersion.expected | 1 + .../WeakTlsVersion/WeakTlsVersion.qlref | 1 + .../security/CWE-327/WeakTlsVersion/app.bicep | 21 +++++++++++++++ 5 files changed, 69 insertions(+) create mode 100644 ql/src/security/CWE-327/WeakTlsVersion.md create mode 100644 ql/src/security/CWE-327/WeakTlsVersion.ql create mode 100644 ql/test/queries-tests/security/CWE-327/WeakTlsVersion/WeakTlsVersion.expected create mode 100644 ql/test/queries-tests/security/CWE-327/WeakTlsVersion/WeakTlsVersion.qlref create mode 100644 ql/test/queries-tests/security/CWE-327/WeakTlsVersion/app.bicep diff --git a/ql/src/security/CWE-327/WeakTlsVersion.md b/ql/src/security/CWE-327/WeakTlsVersion.md new file mode 100644 index 0000000..807ee05 --- /dev/null +++ b/ql/src/security/CWE-327/WeakTlsVersion.md @@ -0,0 +1,27 @@ +# Weak TLS Version + +This query detects Azure resources that are configured to use weak or deprecated TLS versions, such as TLS 1.0 or TLS 1.1. Using outdated TLS versions exposes services to known vulnerabilities and increases the risk of data breaches or man-in-the-middle attacks. It is a security best practice to require the latest supported TLS version (such as TLS 1.2 or higher) for all resources. + +## Bad Example: Weak TLS Version Configured + +```bicep +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'weaktlsdb' + location: 'eastus' + properties: { + minimalTlsVersion: '1.0' // BAD: Weak TLS version + } +} +``` + +## Good Example: Strong TLS Version Configured + +```bicep +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'securetlsdb' + location: 'eastus' + properties: { + minimalTlsVersion: '1.2' // GOOD: Strong TLS version + } +} +``` \ No newline at end of file diff --git a/ql/src/security/CWE-327/WeakTlsVersion.ql b/ql/src/security/CWE-327/WeakTlsVersion.ql new file mode 100644 index 0000000..9d03914 --- /dev/null +++ b/ql/src/security/CWE-327/WeakTlsVersion.ql @@ -0,0 +1,19 @@ +/** + * @name Weak TLS Version + * @description Weak TLS versions (1.0 and 1.1) should not be used as they are considered insecure. + * @kind problem + * @problem.severity error + * @security-severity 8.0 + * @precision high + * @id bicep/weak-tls-version + * @tags security + * bicep + * azure + * cryptography + */ +import bicep + +from Cryptography::WeakTlsVersion resource +where + resource.hasWeakTlsVersion() +select resource.getWeakTlsVersionProperty(), "Weak TLS version detected" \ No newline at end of file diff --git a/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/WeakTlsVersion.expected b/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/WeakTlsVersion.expected new file mode 100644 index 0000000..6c22954 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/WeakTlsVersion.expected @@ -0,0 +1 @@ +| app.bicep:18:24:18:28 | String | Weak TLS version detected | diff --git a/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/WeakTlsVersion.qlref b/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/WeakTlsVersion.qlref new file mode 100644 index 0000000..4acbdcc --- /dev/null +++ b/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/WeakTlsVersion.qlref @@ -0,0 +1 @@ +security/CWE-327/WeakTlsVersion.ql \ No newline at end of file diff --git a/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/app.bicep b/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/app.bicep new file mode 100644 index 0000000..db2521e --- /dev/null +++ b/ql/test/queries-tests/security/CWE-327/WeakTlsVersion/app.bicep @@ -0,0 +1,21 @@ + +// Secure +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'securetlsdb' + location: 'eastus' + properties: { + minimalTlsVersion: '1.2' // GOOD: Strong TLS version + } +} + +// Bad +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'publicdbserver' + location: 'eastus' + properties: { + version: '12.0' + publicNetworkAccess: 'Enabled' // BAD: Database is publicly accessible + minimalTlsVersion: '1.0' // BAD: Weak TLS version + sslEnforcement: 'Disabled' // BAD: SSL enforcement is disabled + } +} From aca20849b55476b9559c44c2915d4ddcc9dd2c56 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 11:23:12 +0100 Subject: [PATCH 12/14] feat: Update databases --- .../bicep/frameworks/Microsoft/Databases.qll | 51 +++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll index 86ac606..9df3365 100644 --- a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -22,22 +22,46 @@ module Databases { */ DatabaseProperties::Properties getProperties() { result = this.getProperty("properties") } + /** + * Returns the version property as a StringLiteral, if present. + */ + StringLiteral getVersion() { + result = this.getProperties().getProperty("version") + } + /** * Returns the version property of the database resource, if present. */ string version() { - result = this.getProperties().getProperty("version").(StringLiteral).getValue() + result = this.getVersion().getValue() } + /** + * Returns the publicNetworkAccess property as a StringLiteral, if present. + */ + StringLiteral getPublicNetworkAccess() { + result = this.getProperties().getProperty("publicNetworkAccess") + } + + /** + * Returns the value of the publicNetworkAccess property, if present. + */ string publicNetworkAccess() { - result = this.getProperties().getProperty("publicNetworkAccess").(StringLiteral).getValue() + result = this.getPublicNetworkAccess().getValue() + } + + /** + * Returns the sslEnforcement property as a StringLiteral, if present. + */ + StringLiteral getSslEnforcement() { + result = this.getProperties().getProperty("sslEnforcement") } /** * Returns the sslEnforcement property of the database resource, if present. */ string sslEnforcement() { - result = this.getProperties().getProperty("sslEnforcement").(StringLiteral).getValue() + result = this.getSslEnforcement().getValue() } /** @@ -47,11 +71,18 @@ module Databases { result = this.getProperties().getProperty("infrastructureEncryption").(StringLiteral).getValue() } + /** + * Returns the minimalTlsVersion property as a StringLiteral, if present. + */ + StringLiteral getMinimalTlsVersion() { + result = this.getProperties().getProperty("minimalTlsVersion") + } + /** * Returns the minimalTlsVersion property of the database resource, if present. */ string minimalTlsVersion() { - result = this.getProperties().getProperty("minimalTlsVersion").(StringLiteral).getValue() + result = this.getMinimalTlsVersion().getValue() } /** @@ -230,17 +261,29 @@ module Databases { class PublicDatabaseResource extends PublicResource { private DatabaseResource database; + /** + * Constructs a PublicDatabaseResource if the database has public network access enabled. + */ PublicDatabaseResource() { database.publicNetworkAccess() = "Enabled" and this = database } + /** + * Returns the property that indicates public access for the database resource. + */ override Expr getPublicAccessProperty() { result = database.getProperties().getProperty("publicNetworkAccess") } } + /** + * Represents a database resource with a weak TLS version configuration. + */ class WeakDatabaseTlsVersion extends Cryptography::WeakTlsVersion instanceof DatabaseResource { + /** + * Returns the minimalTlsVersion property as a StringLiteral for weak TLS version detection. + */ override StringLiteral getWeakTlsVersionProperty() { result = DatabaseResource.super.getProperties().getProperty("minimalTlsVersion") } From 492325db0c6435a557f31444faaf6238c35f45f8 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 11:24:30 +0100 Subject: [PATCH 13/14] feat(query): Add SSL Enforement query --- ql/src/security/CWE-319/SslEnforement.md | 29 +++++++++++++++++++ ql/src/security/CWE-319/SslEnforement.ql | 22 ++++++++++++++ .../SslEnforement/SslEnforement.expected | 1 + .../CWE-319/SslEnforement/SslEnforement.qlref | 1 + .../security/CWE-319/SslEnforement/app.bicep | 21 ++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 ql/src/security/CWE-319/SslEnforement.md create mode 100644 ql/src/security/CWE-319/SslEnforement.ql create mode 100644 ql/test/queries-tests/security/CWE-319/SslEnforement/SslEnforement.expected create mode 100644 ql/test/queries-tests/security/CWE-319/SslEnforement/SslEnforement.qlref create mode 100644 ql/test/queries-tests/security/CWE-319/SslEnforement/app.bicep diff --git a/ql/src/security/CWE-319/SslEnforement.md b/ql/src/security/CWE-319/SslEnforement.md new file mode 100644 index 0000000..6aa56be --- /dev/null +++ b/ql/src/security/CWE-319/SslEnforement.md @@ -0,0 +1,29 @@ +# SSL / TLS not Enforced + +This query detects Azure database resources that have SSL/TLS enforcement disabled. Disabling SSL/TLS enforcement can expose sensitive data to interception and man-in-the-middle attacks, as connections to the database may be established without encryption. It is a security best practice to always require SSL/TLS for database connections to ensure data in transit is protected. + +## Bad Example: SSL Enforcement Disabled + +```bicep +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'mydbserver' + location: 'eastus' + properties: { + version: '12.0' + sslEnforcement: 'Disabled' // BAD: SSL enforcement is disabled + } +} +``` + +## Good Example: SSL Enforcement Enabled + +```bicep +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'mydbserver' + location: 'eastus' + properties: { + version: '12.0' + sslEnforcement: 'Enabled' // GOOD: SSL enforcement is enabled + } +} +``` diff --git a/ql/src/security/CWE-319/SslEnforement.ql b/ql/src/security/CWE-319/SslEnforement.ql new file mode 100644 index 0000000..6a56dfb --- /dev/null +++ b/ql/src/security/CWE-319/SslEnforement.ql @@ -0,0 +1,22 @@ +/** + * @name SSL / TLS not Enforced + * @description SSL / TLS should be enforced on resources to ensure secure communication. + * @kind problem + * @problem.severity error + * @security-severity 6.0 + * @precision high + * @id bicep/ssl-enforcement-disabled + * @tags security + * bicep + * azure + * cryptography + */ + + +import bicep +import codeql.bicep.frameworks.Microsoft.Databases::Databases + +from DatabaseResource db +where db.sslEnforcement() = "Disabled" +select db.getSslEnforcement(), + "SSL / TLS is not enforced on the database resource '" + db.getName() + "'." diff --git a/ql/test/queries-tests/security/CWE-319/SslEnforement/SslEnforement.expected b/ql/test/queries-tests/security/CWE-319/SslEnforement/SslEnforement.expected new file mode 100644 index 0000000..fdedee7 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-319/SslEnforement/SslEnforement.expected @@ -0,0 +1 @@ +| app.bicep:19:21:19:30 | String | SSL / TLS is not enforced on the database resource 'publicdbserver'. | diff --git a/ql/test/queries-tests/security/CWE-319/SslEnforement/SslEnforement.qlref b/ql/test/queries-tests/security/CWE-319/SslEnforement/SslEnforement.qlref new file mode 100644 index 0000000..75b3334 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-319/SslEnforement/SslEnforement.qlref @@ -0,0 +1 @@ +security/CWE-319/SslEnforement.ql \ No newline at end of file diff --git a/ql/test/queries-tests/security/CWE-319/SslEnforement/app.bicep b/ql/test/queries-tests/security/CWE-319/SslEnforement/app.bicep new file mode 100644 index 0000000..eb02556 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-319/SslEnforement/app.bicep @@ -0,0 +1,21 @@ + +// Secure +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'securetlsdb' + location: 'eastus' + properties: { + sslEnforcement: 'Enabled' // GOOD: Enforced + } +} + +// Bad +resource db 'Microsoft.Sql/servers@2021-02-01-preview' = { + name: 'publicdbserver' + location: 'eastus' + properties: { + version: '12.0' + publicNetworkAccess: 'Enabled' // BAD: Database is publicly accessible + minimalTlsVersion: '1.0' // BAD: Weak TLS version + sslEnforcement: 'Disabled' // BAD: SSL enforcement is disabled + } +} From 6c14d38555efa48f15c4b15673600c1a57e84e9e Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Wed, 11 Jun 2025 11:27:43 +0100 Subject: [PATCH 14/14] docs: Add small change to contributing docs --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c5839c..ef95efb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,6 +28,7 @@ If you have an idea for a new feature or enhancement, [please open an issue on G 3. Make your changes 4. Write tests for your changes (if applicable) 5. Run the tests to make sure everything is working +6. Submit a [pull request][pr] with a clear description of your changes ### Requirements