diff --git a/ql/lib/codeql/bicep/Frameworks.qll b/ql/lib/codeql/bicep/Frameworks.qll index e6993a8..932d795 100644 --- a/ql/lib/codeql/bicep/Frameworks.qll +++ b/ql/lib/codeql/bicep/Frameworks.qll @@ -1,4 +1,6 @@ +import frameworks.Microsoft.Cache import frameworks.Microsoft.Compute +import frameworks.Microsoft.General import frameworks.Microsoft.Network import frameworks.Microsoft.Storage import frameworks.Microsoft.Databases diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Cache.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Cache.qll new file mode 100644 index 0000000..f87ffca --- /dev/null +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Cache.qll @@ -0,0 +1,239 @@ +private import bicep +private import codeql.bicep.Concepts + +module Cache { + abstract class CacheResource extends Resource { } + + /** + * Represents an Azure Cache for Redis resource. + */ + class RedisCacheResource extends CacheResource, Resource { + /** + * Constructs a RedisCacheResource for Microsoft.Cache/Redis resources. + */ + RedisCacheResource() { this.getResourceType().regexpMatch("^Microsoft.Cache/Redis@.*") } + + /** + * Returns the properties object for the Redis cache resource. + */ + CacheProperties::Properties getProperties() { result = this.getProperty("properties") } + + CacheProperties::RedisConfiguration getRedisConfiguration() { + result = this.getProperties().getProperty("redisConfiguration") + } + + /** + * Returns the SKU of the Redis cache. + */ + Sku getSku() { result = this.getProperty("sku") } + + /** + * Returns the Redis version. + */ + string redisVersion() { + result = this.getProperties().getProperty("redisVersion").(StringLiteral).getValue() + } + + /** + * Returns true if non-SSL port is enabled. + */ + boolean enableNonSslPort() { + result = this.getProperties().getProperty("enableNonSslPort").(Boolean).getBool() + } + + /** + * Returns the publicNetworkAccess property, if present. + */ + string publicNetworkAccess() { + result = this.getProperties().getProperty("publicNetworkAccess").(StringLiteral).getValue() + } + + /** + * Returns a string representation of the Redis cache resource. + */ + override string toString() { result = "RedisCacheResource" } + } + + /** + * Represents a public Azure Cache for Redis resource (public network access enabled). + */ + class PublicRedisCacheResource extends PublicResource { + private RedisCacheResource redisCache; + + /** + * Constructs a PublicRedisCacheResource if the Redis cache has public network access enabled. + */ + PublicRedisCacheResource() { + redisCache.publicNetworkAccess() = "Enabled" and + this = redisCache + } + + /** + * Returns the property that indicates public access for the Redis cache resource. + */ + override Expr getPublicAccessProperty() { + result = redisCache.getProperties().getProperty("publicNetworkAccess") + } + } + + module CacheProperties { + /** + * Represents the properties object for a Redis cache resource. + */ + class Properties extends Object { + private RedisCacheResource redisCache; + + /** + * Constructs a Properties object for the given Redis cache resource. + */ + Properties() { this = redisCache.getProperty("properties") } + + /** + * Returns the parent RedisCacheResource. + */ + RedisCacheResource getRedisCacheResource() { result = redisCache } + + string toString() { result = "CacheProperties" } + } + + /** + * Represents the redisConfiguration object for Azure Cache for Redis. + * Provides accessors for all supported redisConfiguration properties. + */ + class RedisConfiguration extends Object { + private Properties properties; + + /** + * Constructs a RedisConfiguration object for the given properties. + */ + RedisConfiguration() { this = properties.getProperty("redisConfiguration") } + + /** Returns the 'aad-enabled' property as a StringLiteral, if present. */ + StringLiteral getAadEnabled() { result = this.getProperty("aad-enabled") } + + /** Returns the 'aad-enabled' property as a string, if present. */ + string aadEnabled() { result = this.getAadEnabled().getValue() } + + /** Returns the 'aof-backup-enabled' property as a StringLiteral, if present. */ + StringLiteral getAofBackupEnabled() { result = this.getProperty("aof-backup-enabled") } + + /** Returns the 'aof-backup-enabled' property as a string, if present. */ + string aofBackupEnabled() { result = this.getAofBackupEnabled().getValue() } + + /** Returns the 'aof-storage-connection-string-0' property as a StringLiteral, if present. */ + StringLiteral getAofStorageConnectionString0() { + result = this.getProperty("aof-storage-connection-string-0") + } + + /** Returns the 'aof-storage-connection-string-0' property as a string, if present. */ + string aofStorageConnectionString0() { + result = this.getAofStorageConnectionString0().getValue() + } + + /** Returns the 'aof-storage-connection-string-1' property as a StringLiteral, if present. */ + StringLiteral getAofStorageConnectionString1() { + result = this.getProperty("aof-storage-connection-string-1") + } + + /** Returns the 'aof-storage-connection-string-1' property as a string, if present. */ + string aofStorageConnectionString1() { + result = this.getAofStorageConnectionString1().getValue() + } + + /** Returns the 'authnotrequired' property as a StringLiteral, if present. */ + StringLiteral getAuthNotRequired() { result = this.getProperty("authnotrequired") } + + /** Returns the 'authnotrequired' property as a string, if present. */ + string authNotRequired() { result = this.getAuthNotRequired().getValue() } + + /** Returns the 'maxfragmentationmemory-reserved' property as a StringLiteral, if present. */ + StringLiteral getMaxFragmentationMemoryReserved() { + result = this.getProperty("maxfragmentationmemory-reserved") + } + + /** Returns the 'maxfragmentationmemory-reserved' property as a string, if present. */ + string maxFragmentationMemoryReserved() { + result = this.getMaxFragmentationMemoryReserved().getValue() + } + + /** Returns the 'maxmemory-delta' property as a StringLiteral, if present. */ + StringLiteral getMaxMemoryDelta() { result = this.getProperty("maxmemory-delta") } + + /** Returns the 'maxmemory-delta' property as a string, if present. */ + string maxMemoryDelta() { result = this.getMaxMemoryDelta().getValue() } + + /** Returns the 'maxmemory-policy' property as a StringLiteral, if present. */ + StringLiteral getMaxMemoryPolicy() { result = this.getProperty("maxmemory-policy") } + + /** Returns the 'maxmemory-policy' property as a string, if present. */ + string maxMemoryPolicy() { result = this.getMaxMemoryPolicy().getValue() } + + /** Returns the 'maxmemory-reserved' property as a StringLiteral, if present. */ + StringLiteral getMaxMemoryReserved() { result = this.getProperty("maxmemory-reserved") } + + /** Returns the 'maxmemory-reserved' property as a string, if present. */ + string maxMemoryReserved() { result = this.getMaxMemoryReserved().getValue() } + + /** Returns the 'notify-keyspace-events' property as a StringLiteral, if present. */ + StringLiteral getNotifyKeyspaceEvents() { + result = this.getProperty("notify-keyspace-events") + } + + /** Returns the 'notify-keyspace-events' property as a string, if present. */ + string notifyKeyspaceEvents() { result = this.getNotifyKeyspaceEvents().getValue() } + + /** Returns the 'preferred-data-persistence-auth-method' property as a StringLiteral, if present. */ + StringLiteral getPreferredDataPersistenceAuthMethod() { + result = this.getProperty("preferred-data-persistence-auth-method") + } + + /** Returns the 'preferred-data-persistence-auth-method' property as a string, if present. */ + string preferredDataPersistenceAuthMethod() { + result = this.getPreferredDataPersistenceAuthMethod().getValue() + } + + /** Returns the 'rdb-backup-enabled' property as a StringLiteral, if present. */ + StringLiteral getRdbBackupEnabled() { result = this.getProperty("rdb-backup-enabled") } + + /** Returns the 'rdb-backup-enabled' property as a string, if present. */ + string rdbBackupEnabled() { result = this.getRdbBackupEnabled().getValue() } + + /** Returns the 'rdb-backup-frequency' property as a StringLiteral, if present. */ + StringLiteral getRdbBackupFrequency() { result = this.getProperty("rdb-backup-frequency") } + + /** Returns the 'rdb-backup-frequency' property as a string, if present. */ + string rdbBackupFrequency() { result = this.getRdbBackupFrequency().getValue() } + + /** Returns the 'rdb-backup-max-snapshot-count' property as a StringLiteral, if present. */ + StringLiteral getRdbBackupMaxSnapshotCount() { + result = this.getProperty("rdb-backup-max-snapshot-count") + } + + /** Returns the 'rdb-backup-max-snapshot-count' property as a string, if present. */ + string rdbBackupMaxSnapshotCount() { result = this.getRdbBackupMaxSnapshotCount().getValue() } + + /** Returns the 'rdb-storage-connection-string' property as a StringLiteral, if present. */ + StringLiteral getRdbStorageConnectionString() { + result = this.getProperty("rdb-storage-connection-string") + } + + /** Returns the 'rdb-storage-connection-string' property as a string, if present. */ + string rdbStorageConnectionString() { + result = this.getRdbStorageConnectionString().getValue() + } + + /** Returns the 'storage-subscription-id' property as a StringLiteral, if present. */ + StringLiteral getStorageSubscriptionId() { + result = this.getProperty("storage-subscription-id") + } + + /** Returns the 'storage-subscription-id' property as a string, if present. */ + string storageSubscriptionId() { result = this.getStorageSubscriptionId().getValue() } + + /** + * Returns a string representation of the RedisConfiguration object. + */ + string toString() { result = "RedisConfiguration" } + } + } +} diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll index 9df3365..5aec483 100644 --- a/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Databases.qll @@ -211,21 +211,6 @@ module Databases { 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. */ diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/General.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/General.qll new file mode 100644 index 0000000..16ae324 --- /dev/null +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/General.qll @@ -0,0 +1,28 @@ +private import bicep + +class Sku extends Object { + private Resource resource; + + /** + * Constructs a Sku object for the given resource. + */ + Sku() { this = resource.getProperty("sku") } + + /** + * Returns the SKU name (e.g., Basic, Standard, Premium). + */ + string getName() { + result = this.getProperty("name").(StringLiteral).getValue() + } + + /** + * Returns the SKU tier (e.g., Basic, Standard, Premium). + */ + string getTier() { + result = this.getProperty("tier").(StringLiteral).getValue() + } + + string toString() { + result = "SKU" + } +} diff --git a/ql/test/library-tests/frameworks/cache/Cache.expected b/ql/test/library-tests/frameworks/cache/Cache.expected new file mode 100644 index 0000000..e17dd42 --- /dev/null +++ b/ql/test/library-tests/frameworks/cache/Cache.expected @@ -0,0 +1,4 @@ +cache +| app.bicep:1:1:17:1 | RedisCacheResource | +cacheConfig +| app.bicep:1:1:17:1 | RedisCacheResource | app.bicep:13:25:15:5 | RedisConfiguration | diff --git a/ql/test/library-tests/frameworks/cache/Cache.ql b/ql/test/library-tests/frameworks/cache/Cache.ql new file mode 100644 index 0000000..d54986e --- /dev/null +++ b/ql/test/library-tests/frameworks/cache/Cache.ql @@ -0,0 +1,9 @@ +import bicep + +query predicate cache(Cache::CacheResource cache) { any() } + +query predicate cacheConfig( + Cache::RedisCacheResource cache, Cache::CacheProperties::RedisConfiguration config +) { + cache.getRedisConfiguration() = config +} diff --git a/ql/test/library-tests/frameworks/cache/app.bicep b/ql/test/library-tests/frameworks/cache/app.bicep new file mode 100644 index 0000000..4463004 --- /dev/null +++ b/ql/test/library-tests/frameworks/cache/app.bicep @@ -0,0 +1,17 @@ +resource redisCache 'Microsoft.Cache/Redis@2023-04-01' = { + name: 'myRedisCache' + location: resourceGroup().location + sku: { + name: 'Standard' + family: 'C' + capacity: 1 + } + properties: { + enableNonSslPort: false + minimumTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + redisConfiguration: { + maxmemory-policy: 'allkeys-lru' + } + } +} \ 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 index 5ff4516..8d97b73 100644 --- a/ql/test/library-tests/frameworks/databases/Databases.expected +++ b/ql/test/library-tests/frameworks/databases/Databases.expected @@ -8,6 +8,5 @@ | 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] | +| app.bicep:134:1:143:1 | DatabaseResource[kusto] | +| app.bicep:146:1:153: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 2246697..64d0a6f 100644 --- a/ql/test/library-tests/frameworks/databases/app.bicep +++ b/ql/test/library-tests/frameworks/databases/app.bicep @@ -130,19 +130,6 @@ resource datalakeStore 'Microsoft.DataLakeStore/accounts@2016-11-01' = { 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'