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 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 diff --git a/ql/lib/codeql/bicep/Concepts.qll b/ql/lib/codeql/bicep/Concepts.qll index 5ead729..346c609 100644 --- a/ql/lib/codeql/bicep/Concepts.qll +++ b/ql/lib/codeql/bicep/Concepts.qll @@ -1,2 +1,30 @@ 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(); +} + +module Cryptography { + abstract class WeakTlsVersion extends Resource { + abstract StringLiteral getWeakTlsVersionProperty(); + + /** + * Returns true if the resource has a weak TLS version. + * + * 1.0 and 1.1 are considered weak TLS versions. + */ + predicate hasWeakTlsVersion() { + exists(StringLiteral literal | + literal = this.getWeakTlsVersionProperty() and + literal.getValue().regexpMatch("^(1\\.0|1\\.1)$") + ) + } + } +} 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 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 new file mode 100644 index 0000000..9df3365 --- /dev/null +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -0,0 +1,397 @@ +private import bicep +private import codeql.bicep.Concepts + +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 (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 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.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.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.getSslEnforcement().getValue() + } + + /** + * Returns the infrastructureEncryption property of the database resource, if present. + */ + string infrastructureEncryption() { + 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.getMinimalTlsVersion().getValue() + } + + /** + * Returns the storage profile for the database resource, if present. + */ + DatabaseProperties::StorageProfile getStorageProfile() { + result = this.getProperties().getProperty("storageProfile") + } + } + + /** + * 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" } + } + + /** + * 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") + } + } + + /** + * 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" } + } + + /** + * 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" } + } + + /** + * 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" } + } + + /** + * 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" } + } + + /** + * 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" } + } + + /** + * 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" } + } + + /** + * 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" } + } + + 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") + } + } + + 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() + } + } + } +} 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/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/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/library-tests/frameworks/databases/Databases.expected b/ql/test/library-tests/frameworks/databases/Databases.expected new file mode 100644 index 0000000..5ff4516 --- /dev/null +++ b/ql/test/library-tests/frameworks/databases/Databases.expected @@ -0,0 +1,13 @@ +| 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/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..2246697 --- /dev/null +++ b/ql/test/library-tests/frameworks/databases/app.bicep @@ -0,0 +1,166 @@ +// 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' + 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 with geo-redundant backup and high availability +resource postgresqlDb 'Microsoft.DBforPostgreSQL/servers@2022-01-20' = { + name: 'pgserver1' + location: 'eastus' + properties: { + administratorLogin: 'pgadmin' + administratorLoginPassword: 'P@ssw0rd!' + version: '11' + backup': { + geoRedundantBackup: 'Enabled' + } + highAvailability: { + mode: 'ZoneRedundant' + } + } +} + +// Azure Database for MySQL with SSL enforcement and auto-grow +resource mysqlDb 'Microsoft.DBforMySQL/servers@2022-01-20' = { + name: 'mysqlserver1' + location: 'eastus' + properties: { + administratorLogin: 'mysqladmin' + administratorLoginPassword: 'P@ssw0rd!' + version: '5.7' + sslEnforcement: 'Enabled' + storageProfile: { + storageMB: 51200 + autoGrow: 'Enabled' + } + } +} + +// Azure Database for MariaDB with backup retention and geo-redundancy +resource mariadbDb 'Microsoft.DBforMariaDB/servers@2018-06-01' = { + name: 'mariadbserver1' + location: 'eastus' + properties: { + administratorLogin: 'mariadbadmin' + administratorLoginPassword: 'P@ssw0rd!' + version: '10.2' + backupRetentionDays: 14 + geoRedundantBackup: 'Enabled' + } +} + +// 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!' + } +} 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 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 + } +} 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 + } +}