From 117bcd794567313e3b4bfd542d763420dd330a88 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Mon, 4 Nov 2024 19:29:54 +0500 Subject: [PATCH 1/2] TDE Protectors Encrypted Plugin Update --- .../azure/sqlserver/tdeProtectorEncrypted.js | 153 +++++++++----- .../sqlserver/tdeProtectorEncrypted.spec.js | 190 +++++++++--------- 2 files changed, 190 insertions(+), 153 deletions(-) diff --git a/plugins/azure/sqlserver/tdeProtectorEncrypted.js b/plugins/azure/sqlserver/tdeProtectorEncrypted.js index 1c1f6c324d..e8047a0a4b 100644 --- a/plugins/azure/sqlserver/tdeProtectorEncrypted.js +++ b/plugins/azure/sqlserver/tdeProtectorEncrypted.js @@ -8,82 +8,125 @@ module.exports = { severity: 'Medium', description: 'Ensures SQL Server TDE protector is encrypted with BYOK (Bring Your Own Key)', more_info: 'Enabling BYOK in the TDE protector allows for greater control and transparency, as well as increasing security by having full control of the encryption keys.', - recommended_action: 'Ensure that a BYOK key is set for the Transparent Data Encryption of each SQL Server.', + recommended_action: 'Ensure that a BYOK key is set for the Transparent Data Encryption of each SQL Server or Managed Instance.', link: 'https://learn.microsoft.com/en-us/azure/sql-database/transparent-data-encryption-byok-azure-sql', - apis: ['servers:listSql', 'encryptionProtectors:listByServer'], + apis: ['servers:listSql', 'encryptionProtectors:listByServer', 'managedInstances:list', 'managedInstanceEncryptionProtectors:listByInstance'], settings: { sql_tde_protector_encryption_key: { name: 'SQL Server TDE Protector Encryption Key Type', - description: 'Desired encryption key for SQL Server transparent data encryption; default=service-managed key, cmk=customer-managed key', + description: 'Desired encryption key for SQL Server and Managed Instance transparent data encryption; default=service-managed key, cmk=customer-managed key', regex: '(default|byok)', default: 'byok' } }, - realtime_triggers: ['microsoftsql:servers:write', 'microsoftsql:servers:delete', 'microsodtsql:servers:encryptionprotector:write'], + realtime_triggers: ['microsoftsql:servers:write', 'microsoftsql:servers:delete', 'microsoftsql:servers:encryptionprotector:write'], run: function(cache, settings, callback) { const results = []; const source = {}; const locations = helpers.locations(settings.govcloud); - + var config = { sql_tde_protector_encryption_key: settings.sql_tde_protector_encryption_key || this.settings.sql_tde_protector_encryption_key.default }; - - async.each(locations.servers, function(location, rcb) { - - var servers = helpers.addSource(cache, source, - ['servers', 'listSql', location]); - - if (!servers) return rcb(); - - if (servers.err || !servers.data) { - helpers.addResult(results, 3, - 'Unable to query for SQL servers: ' + helpers.addError(servers), location); - return rcb(); - } - - if (!servers.data.length) { - helpers.addResult(results, 0, 'No SQL servers found', location); - return rcb(); - } - - servers.data.forEach(function(server) { - const encryptionProtectors = helpers.addSource(cache, source, - ['encryptionProtectors', 'listByServer', location, server.id]); - - if (!encryptionProtectors || encryptionProtectors.err || !encryptionProtectors.data) { - helpers.addResult(results, 3, - 'Unable to query for SQL Server Encryption Protectors: ' + helpers.addError(encryptionProtectors), location, server.id); + + function checkEncryptionProtection(encryptionProtector, location, config) { + if (config.sql_tde_protector_encryption_key == 'byok') { + if ((encryptionProtector.kind && + encryptionProtector.kind.toLowerCase() != 'azurekeyvault') || + (encryptionProtector.serverKeyType && + encryptionProtector.serverKeyType.toLowerCase() != 'azurekeyvault') || + !encryptionProtector.uri) { + helpers.addResult(results, 2, + 'SQL Server TDE protector is not encrypted with BYOK', location, encryptionProtector.id); } else { - if (!encryptionProtectors.data.length) { - helpers.addResult(results, 0, 'No SQL Server Encryption Protectors found for server', location, server.id); - } else { - encryptionProtectors.data.forEach(encryptionProtector => { - if (config.sql_tde_protector_encryption_key == 'byok') { - if ((encryptionProtector.kind && - encryptionProtector.kind.toLowerCase() != 'azurekeyvault') || - (encryptionProtector.serverKeyType && - encryptionProtector.serverKeyType.toLowerCase() != 'azurekeyvault') || - !encryptionProtector.uri) { - helpers.addResult(results, 2, - 'SQL Server TDE protector is not encrypted with BYOK', location, encryptionProtector.id); - } else { - helpers.addResult(results, 0, - 'SQL Server TDE protector is encrypted with BYOK', location, encryptionProtector.id); - } - } else { - helpers.addResult(results, 0, - 'SQL Server TDE protector is encrypted with service-managed key', location, encryptionProtector.id); - } - }); + helpers.addResult(results, 0, + 'SQL Server TDE protector is encrypted with BYOK', location, encryptionProtector.id); + } + } else { + helpers.addResult(results, 0, + 'SQL Server TDE protector is encrypted with service-managed key', location, encryptionProtector.id); + } + } + + async.each(locations.servers, function(location, rcb) { + async.parallel([ + // Check SQL Servers + function(cb) { + const servers = helpers.addSource(cache, source, + ['servers', 'listSql', location]); + + if (!servers) return cb(); + + if (servers.err || !servers.data) { + helpers.addResult(results, 3, + 'Unable to query for SQL servers: ' + helpers.addError(servers), location); + return cb(); } + + if (!servers.data.length) { + helpers.addResult(results, 0, 'No SQL servers found', location); + return cb(); + } + + servers.data.forEach(server => { + const encryptionProtectors = helpers.addSource(cache, source, + ['encryptionProtectors', 'listByServer', location, server.id]); + + if (!encryptionProtectors || encryptionProtectors.err || !encryptionProtectors.data) { + helpers.addResult(results, 3, + 'Unable to query for SQL Server Encryption Protectors: ' + helpers.addError(encryptionProtectors), location, server.id); + } else if (!encryptionProtectors.data.length) { + helpers.addResult(results, 0, 'No SQL Server Encryption Protectors found', location, server.id); + } else { + encryptionProtectors.data.forEach(protector => { + checkEncryptionProtection(protector, location, config); + }); + } + }); + + cb(); + }, + // Check Managed Instances + function(cb) { + const managedInstances = helpers.addSource(cache, source, + ['managedInstances', 'list', location]); + + if (!managedInstances) return cb(); + + if (managedInstances.err || !managedInstances.data) { + helpers.addResult(results, 3, + 'Unable to query for managed instances: ' + helpers.addError(managedInstances), location); + return cb(); + } + + if (!managedInstances.data.length) { + helpers.addResult(results, 0, 'No managed instances found', location); + return cb(); + } + + managedInstances.data.forEach(instance => { + const managedInstanceEncryptionProtectors = helpers.addSource(cache, source, + ['managedInstanceEncryptionProtectors', 'listByInstance', location, instance.id]); + + if (!managedInstanceEncryptionProtectors || managedInstanceEncryptionProtectors.err || !managedInstanceEncryptionProtectors.data) { + helpers.addResult(results, 3, + 'Unable to query for Managed Instance Encryption Protectors: ' + helpers.addError(managedInstanceEncryptionProtectors), location, instance.id); + } else if (!managedInstanceEncryptionProtectors.data.length) { + helpers.addResult(results, 0, 'No Managed Instance Encryption Protectors found', location, instance.id); + } else { + managedInstanceEncryptionProtectors.data.forEach(protector => { + checkEncryptionProtection(protector, location, config); + }); + } + }); + + cb(); } + ], function() { + rcb(); }); - - rcb(); }, function() { - // Global checking goes here callback(null, results, source); }); } diff --git a/plugins/azure/sqlserver/tdeProtectorEncrypted.spec.js b/plugins/azure/sqlserver/tdeProtectorEncrypted.spec.js index 98f983dcde..58aeae3a94 100644 --- a/plugins/azure/sqlserver/tdeProtectorEncrypted.spec.js +++ b/plugins/azure/sqlserver/tdeProtectorEncrypted.spec.js @@ -1,56 +1,43 @@ -var expect = require('chai').expect; -var tdeProtectorEncrypted = require('./tdeProtectorEncrypted'); +const expect = require('chai').expect; +const tdeProtectorEncrypted = require('./tdeProtectorEncrypted'); const servers = [ { - "identity": { - "principalId": "86c16ef5-51e9-4ecb-aeda-5844b8e8eca0", - "type": "SystemAssigned", - "tenantId": "2d4f0836-5935-47f5-954c-14e713119ac2" - }, - "kind": "v12.0", - "location": "eastus", - "tags": {}, - "id": "/subscriptions/1234/resourceGroups/akhtar-rg/providers/Microsoft.Sql/servers/akhtar-server", - "name": "akhtar-server", - "type": "Microsoft.Sql/servers", - "administratorLogin": "akhtar", - "version": "12.0", - "state": "Ready", - "fullyQualifiedDomainName": "akhtar-server.database.windows.net", - "privateEndpointConnections": [], - "minimalTlsVersion": "1.2", - "publicNetworkAccess": "Enabled" - } + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server", + "name": "test-server", + "type": "Microsoft.Sql/servers" + } ]; -const encryptionProtectors = [ - { - "kind": "azurekeyvault", - "id": "/subscriptions/1234/resourceGroups/akhtar-rg/providers/Microsoft.Sql/servers/akhtar-server/encryptionProtector/current", - "name": "current", - "type": "Microsoft.Sql/servers/encryptionProtector", - "serverKeyName": "sadeed-vault_sadeed-key_b5e783becb4a4e789dcd1239441d7567", - "serverKeyType": "AzureKeyVault", - "uri": "https://sadeed-vault.vault.azure.net/keys/sadeed-key/b5e783becb4a4e789dcd1239441d7567" - }, +const managedInstances = [ { - "kind": "servicemanaged", - "id": "/subscriptions/1234/resourceGroups/akhtar-rg/providers/Microsoft.Sql/servers/akhtar-server/encryptionProtector/current", - "name": "current", - "type": "Microsoft.Sql/servers/encryptionProtector", - "serverKeyName": "ServiceManaged", - "serverKeyType": "ServiceManaged" + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/managedInstances/test-instance", + "name": "test-instance", + "type": "Microsoft.Sql/managedInstances" } ]; -const createCache = (servers, encrypted, serversErr, encryptedErr) => { +const byokEncryptionProtector = { + "kind": "azurekeyvault", + "serverKeyType": "AzureKeyVault", + "uri": "https://test-vault.vault.azure.net/keys/test-key/123", + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server/encryptionProtector/current" +}; + +const serviceEncryptionProtector = { + "kind": "servicemanaged", + "serverKeyType": "ServiceManaged", + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server/encryptionProtector/current" +}; + +const createCache = (servers, serverEncryption, managedInstances, managedInstanceEncryption) => { const serverId = (servers && servers.length) ? servers[0].id : null; + const managedInstanceId = (managedInstances && managedInstances.length) ? managedInstances[0].id : null; return { servers: { listSql: { 'eastus': { - err: serversErr, + err: null, data: servers } } @@ -59,119 +46,126 @@ const createCache = (servers, encrypted, serversErr, encryptedErr) => { listByServer: { 'eastus': { [serverId]: { - err: encryptedErr, - data: encrypted + err: null, + data: serverEncryption + } + } + } + }, + managedInstances: { + list: { + 'eastus': { + err: null, + data: managedInstances + } + } + }, + managedInstanceEncryptionProtectors: { + listByInstance: { + 'eastus': { + [managedInstanceId]: { + err: null, + data: managedInstanceEncryption } } } } - } + }; }; describe('tdeProtectorEncrypted', function() { describe('run', function() { - it('should give passing result if no SQL servers found', function(done) { + it('should give passing result if no SQL servers or managed instances found', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); expect(results[0].message).to.include('No SQL servers found'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('No managed instances found'); + done(); }; - const cache = createCache( - [] - ); - + const cache = createCache([], null, [], null); tdeProtectorEncrypted.run(cache, {}, callback); }); - it('should give passing result if No SQL Server Encryption Protectors found for server', function(done) { + it('should give passing result if no encryption protectors found', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('No SQL Server Encryption Protectors found for server'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[0].message).to.include('No SQL Server Encryption Protectors found'); + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('No Managed Instance Encryption Protectors found'); + done(); }; - const cache = createCache( - servers, - [] - ); - + const cache = createCache(servers, [], managedInstances, []); tdeProtectorEncrypted.run(cache, {}, callback); }); - it('should give failing result if SQL Server TDE protector is not encrypted with BYOK', function(done) { + it('should give failing result if TDE protector is not encrypted with BYOK', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('SQL Server TDE protector is not encrypted with BYOK'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[1].status).to.equal(2); + expect(results[1].message).to.include('SQL Server TDE protector is not encrypted with BYOK'); + done(); }; const cache = createCache( - servers, - [encryptionProtectors[1]] + servers, [serviceEncryptionProtector], + managedInstances, [serviceEncryptionProtector] ); - tdeProtectorEncrypted.run(cache, {}, callback); }); - it('should give passing result if SQL Server TDE protector is encrypted with BYOK', function(done) { + it('should give passing result if TDE protector is encrypted with BYOK', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); expect(results[0].message).to.include('SQL Server TDE protector is encrypted with BYOK'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('SQL Server TDE protector is encrypted with BYOK'); + done(); }; const cache = createCache( - servers, - [encryptionProtectors[0]] + servers, [byokEncryptionProtector], + managedInstances, [byokEncryptionProtector] ); - tdeProtectorEncrypted.run(cache, {}, callback); }); - it('should give unknown result if Unable to query for SQL servers', function(done) { + it('should give passing result if TDE protector is encrypted with service-managed key when that is allowed', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(3); - expect(results[0].message).to.include('Unable to query for SQL servers'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results.length).to.equal(2); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('SQL Server TDE protector is encrypted with service-managed key'); + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('SQL Server TDE protector is encrypted with service-managed key'); + done(); }; const cache = createCache( - servers, - [], - { message: 'unable to query servers'} + servers, [serviceEncryptionProtector], + managedInstances, [serviceEncryptionProtector] ); - - tdeProtectorEncrypted.run(cache, {}, callback); + tdeProtectorEncrypted.run(cache, { sql_tde_protector_encryption_key: 'default' }, callback); }); - it('should give unknown result if Unable to query for SQL Server Encryption Protectors', function(done) { + it('should give unknown result if unable to query SQL servers or managed instances', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(3); - expect(results[0].message).to.include('Unable to query for SQL Server Encryption Protectors'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[0].message).to.include('Unable to query for SQL servers'); + expect(results[1].status).to.equal(3); + expect(results[1].message).to.include('Unable to query for managed instances'); + done(); }; - const cache = createCache( - servers, - [], - null, - { message: 'Unable to query for Vulnerability Assessments setting'} - ); - + const cache = createCache(null, null, null, null); tdeProtectorEncrypted.run(cache, {}, callback); }); - }) + }); }); \ No newline at end of file From d4a0b3baca6ea42fde2bb4a1018ada5804beb540 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 5 Nov 2024 08:05:40 -0700 Subject: [PATCH 2/2] dbTDEEnabledHotfix Plugin Update --- helpers/azure/api.js | 15 +- plugins/azure/sqldatabases/dbTDEEnabled.js | 140 +++++++++----- .../azure/sqldatabases/dbTDEEnabled.spec.js | 175 ++++++++++++++---- 3 files changed, 242 insertions(+), 88 deletions(-) diff --git a/helpers/azure/api.js b/helpers/azure/api.js index 000b92f186..1fa19ab9d5 100644 --- a/helpers/azure/api.js +++ b/helpers/azure/api.js @@ -573,8 +573,12 @@ var calls = { listWorkspaces: { url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Synapse/workspaces?api-version=2021-06-01' } - } - + }, + managedInstances: { + list: { + url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Sql/managedInstances?api-version=2022-05-01-preview' + } + }, }; var postcalls = { @@ -1108,6 +1112,13 @@ var postcalls = { url: 'https://management.azure.com/{id}/encryptionScopes?api-version=2023-01-01' } }, + managedDatabases: { + get: { + reliesOnPath: 'managedDatabases.listByInstance', + properties: ['id'], + url: 'https://management.azure.com/{id}?api-version=2022-05-01-preview' + } + }, }; var tertiarycalls = { diff --git a/plugins/azure/sqldatabases/dbTDEEnabled.js b/plugins/azure/sqldatabases/dbTDEEnabled.js index 1352a00de8..d23d7344a0 100644 --- a/plugins/azure/sqldatabases/dbTDEEnabled.js +++ b/plugins/azure/sqldatabases/dbTDEEnabled.js @@ -10,7 +10,7 @@ module.exports = { more_info: 'Transparent data encryption (TDE) helps protect Azure SQL Database, Managed Instance, and Synapse Analytics against the threat of malicious offline activity by encrypting data at rest. It performs real-time encryption and decryption of the database, associated backups, and transaction log files at rest without requiring changes to the application.', recommended_action: 'Modify SQL database and enable Transparent Data Encryption (TDE).', link: 'https://docs.microsoft.com/en-us/sql/relational-databases/security/encryption/transparent-data-encryption?view=sql-server-ver15', - apis: ['servers:listSql', 'databases:listByServer', 'transparentDataEncryption:list'], + apis: ['servers:listSql', 'databases:listByServer', 'transparentDataEncryption:list', 'managedInstances:list', 'managedDatabases:listByInstance'], realtime_triggers: ['microsoftsql:servers:write', 'microsoftsql:servers:delete', 'microsoftsql:servers:databases:write', 'microsoftsql:servers:databases:transparentdataencryption:write', 'microsoftsql:servers:databases:delete'], run: function(cache, settings, callback) { @@ -19,55 +19,103 @@ module.exports = { var locations = helpers.locations(settings.govcloud); async.each(locations.servers, function(location, rcb) { - var servers = helpers.addSource(cache, source, ['servers', 'listSql', location]); - - if (!servers) return rcb(); - - if (servers.err || !servers.data) { - helpers.addResult(results, 3, 'Unable to query for SQL servers: ' + helpers.addError(servers), location); - return rcb(); - } - - if (!servers.data.length) { - helpers.addResult(results, 0, 'No SQL servers found', location); - return rcb(); - } - - servers.data.forEach(server => { - var databases = helpers.addSource(cache, source, - ['databases', 'listByServer', location, server.id]); - - if (!databases || databases.err || !databases.data) { - helpers.addResult(results, 3, - 'Unable to query for SQL server databases: ' + helpers.addError(databases), location, server.id); - } else { - if (!databases.data.length) { - helpers.addResult(results, 0, - 'No databases found for SQL server', location, server.id); - } else { - databases.data.forEach(database => { - - if (database.name && database.name.toLowerCase() !== 'master') { - var transparentDataEncryption = helpers.addSource(cache, source, ['transparentDataEncryption', 'list', location, database.id]); - - if (!transparentDataEncryption || transparentDataEncryption.err || !transparentDataEncryption.data || !transparentDataEncryption.data.length) { - helpers.addResult(results, 3, 'Unable to query transparent data encryption for SQL Database: ' + helpers.addError(transparentDataEncryption), location, database.id); - return; - } - var encryption = transparentDataEncryption.data[0]; - if (encryption.state && encryption.state.toLowerCase() == 'enabled') { - helpers.addResult(results, 0, 'Transparent data encryption is enabled for SQL Database', location, database.id); - } else { - helpers.addResult(results, 2, 'Transparent data encryption is not enabled for SQL Database', location, database.id); - } + async.parallel([ + // Check SQL Server Databases + function(cb) { + const servers = helpers.addSource(cache, source, ['servers', 'listSql', location]); + + if (!servers) return cb(); + + if (servers.err || !servers.data) { + helpers.addResult(results, 3, 'Unable to query for SQL servers: ' + helpers.addError(servers), location); + return cb(); + } + + if (!servers.data.length) { + helpers.addResult(results, 0, 'No SQL servers found', location); + return cb(); + } + + servers.data.forEach(server => { + var databases = helpers.addSource(cache, source, + ['databases', 'listByServer', location, server.id]); + + if (!databases || databases.err || !databases.data) { + helpers.addResult(results, 3, + 'Unable to query for SQL server databases: ' + helpers.addError(databases), location, server.id); + } else { + if (!databases.data.length) { + helpers.addResult(results, 0, + 'No databases found for SQL server', location, server.id); + } else { + databases.data.forEach(database => { + + if (database.name && database.name.toLowerCase() !== 'master') { + var transparentDataEncryption = helpers.addSource(cache, source, ['transparentDataEncryption', 'list', location, database.id]); + + if (!transparentDataEncryption || transparentDataEncryption.err || !transparentDataEncryption.data || !transparentDataEncryption.data.length) { + helpers.addResult(results, 3, 'Unable to query transparent data encryption for SQL Database: ' + helpers.addError(transparentDataEncryption), location, database.id); + return; + } + var encryption = transparentDataEncryption.data[0]; + if (encryption.state && encryption.state.toLowerCase() == 'enabled') { + helpers.addResult(results, 0, 'Transparent data encryption is enabled for SQL Database', location, database.id); + } else { + helpers.addResult(results, 2, 'Transparent data encryption is not enabled for SQL Database', location, database.id); + } + } + }); } - }); + } + + }); + + cb(); + }, + // Check Managed Instances + function(cb) { + const managedInstances = helpers.addSource(cache, source, + ['managedInstances', 'list', location]); + + if (!managedInstances) return cb(); + + if (managedInstances.err || !managedInstances.data) { + helpers.addResult(results, 3, + 'Unable to query for managed instances: ' + helpers.addError(managedInstances), location); + return cb(); } - } - }); + if (!managedInstances.data.length) { + helpers.addResult(results, 0, 'No managed instances found', location); + return cb(); + } - rcb(); + managedInstances.data.forEach(instance => { + const managedDatabases = helpers.addSource(cache, source, + ['managedDatabases', 'listByInstance', location, instance.id]); + + if (!managedDatabases || managedDatabases.err || !managedDatabases.data) { + helpers.addResult(results, 3, + 'Unable to query for managed instance databases: ' + helpers.addError(managedDatabases), location, instance.id); + } else if (!managedDatabases.data.length) { + helpers.addResult(results, 0, + 'No databases found for managed instance', location, instance.id); + } else { + managedDatabases.data.forEach(database => { + if (database.name && database.name.toLowerCase() !== 'master') { + // Managed instances have TDE enabled by default and cannot be disabled + helpers.addResult(results, 0, + 'Transparent data encryption is enabled for managed instance database', location, database.id); + } + }); + } + }); + + cb(); + } + ], function() { + rcb(); + }); }, function() { callback(null, results, source); }); diff --git a/plugins/azure/sqldatabases/dbTDEEnabled.spec.js b/plugins/azure/sqldatabases/dbTDEEnabled.spec.js index f85fca17ed..fc45730589 100644 --- a/plugins/azure/sqldatabases/dbTDEEnabled.spec.js +++ b/plugins/azure/sqldatabases/dbTDEEnabled.spec.js @@ -1,5 +1,5 @@ -var expect = require('chai').expect; -var enableTransparentDataEncryption = require('./dbTDEEnabled'); +const expect = require('chai').expect; +const enableTransparentDataEncryption = require('./dbTDEEnabled'); const servers = [ { @@ -7,6 +7,13 @@ const servers = [ } ]; +const managedInstances = [ + { + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/managedInstances/test-instance", + "name": "test-instance" + } +]; + const databases = [ { "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server/databases/test-database", @@ -14,6 +21,13 @@ const databases = [ } ]; +const managedDatabases = [ + { + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/managedInstances/test-instance/databases/test-database", + "name": "test-database" + } +]; + const transparentDataEncryptionEnabled = [ { "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server/databases/test-database/transparentDataEncryption/1", @@ -21,9 +35,10 @@ const transparentDataEncryptionEnabled = [ } ]; -const createCache = (servers, databases, transparentDataEncryption, serversErr, databasesErr, transparentDataEncryptionErr) => { +const createCache = (servers, databases, transparentDataEncryption, managedInstances, managedDatabases, serversErr, databasesErr, transparentDataEncryptionErr, managedInstancesErr, managedDatabasesErr) => { const serverId = (servers && servers.length) ? servers[0].id : null; const databaseId = (databases && databases.length) ? databases[0].id : null; + const managedInstanceId = (managedInstances && managedInstances.length) ? managedInstances[0].id : null; return { servers: { listSql: { @@ -52,6 +67,24 @@ const createCache = (servers, databases, transparentDataEncryption, serversErr, } } } + }, + managedInstances: { + list: { + 'eastus': { + err: managedInstancesErr, + data: managedInstances + } + } + }, + managedDatabases: { + listByInstance: { + 'eastus': { + [managedInstanceId]: { + err: managedDatabasesErr, + data: managedDatabases + } + } + } } }; }; @@ -60,7 +93,7 @@ describe('enableTransparentDataEncryption', function() { describe('run', function() { it('should give passing result if no SQL servers found', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); expect(results[0].message).to.include('No SQL servers found'); expect(results[0].region).to.equal('eastus'); @@ -68,9 +101,8 @@ describe('enableTransparentDataEncryption', function() { }; const cache = createCache( - [], - databases, - transparentDataEncryptionEnabled + [], databases, transparentDataEncryptionEnabled, + [], [] ); enableTransparentDataEncryption.run(cache, {}, callback); @@ -78,7 +110,7 @@ describe('enableTransparentDataEncryption', function() { it('should give passing result if no databases found for SQL server', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); expect(results[0].message).to.include('No databases found for SQL server'); expect(results[0].region).to.equal('eastus'); @@ -86,9 +118,8 @@ describe('enableTransparentDataEncryption', function() { }; const cache = createCache( - servers, - [], - transparentDataEncryptionEnabled + servers, [], transparentDataEncryptionEnabled, + [], [] ); enableTransparentDataEncryption.run(cache, {}, callback); @@ -96,7 +127,7 @@ describe('enableTransparentDataEncryption', function() { it('should give passing result if SQL Database transparent data encryption is enabled', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); expect(results[0].message).to.include('Transparent data encryption is enabled for SQL Database'); expect(results[0].region).to.equal('eastus'); @@ -104,9 +135,8 @@ describe('enableTransparentDataEncryption', function() { }; const cache = createCache( - servers, - databases, - transparentDataEncryptionEnabled + servers, databases, transparentDataEncryptionEnabled, + [], [] ); enableTransparentDataEncryption.run(cache, {}, callback); @@ -114,7 +144,7 @@ describe('enableTransparentDataEncryption', function() { it('should give failing result if SQL Database transparent data encryption is disabled', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('Transparent data encryption is not enabled for SQL Database'); expect(results[0].region).to.equal('eastus'); @@ -122,14 +152,65 @@ describe('enableTransparentDataEncryption', function() { }; const cache = createCache( - servers, - databases, + servers, databases, [ { "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server/databases/test-database/transparentDataEncryption/1", "state": "Disabled" } - ] + ], + [], [] + ); + + enableTransparentDataEncryption.run(cache, {}, callback); + }); + + it('should give passing result if no managed instances found', function(done) { + const callback = (err, results) => { + expect(results.length).to.equal(2); + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('No managed instances found'); + expect(results[1].region).to.equal('eastus'); + done(); + }; + + const cache = createCache( + [], [], [], + [], [] + ); + + enableTransparentDataEncryption.run(cache, {}, callback); + }); + + it('should give passing result if no databases found for managed instance', function(done) { + const callback = (err, results) => { + expect(results.length).to.equal(2); + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('No databases found for managed instance'); + expect(results[1].region).to.equal('eastus'); + done(); + }; + + const cache = createCache( + [], [], [], + managedInstances, [] + ); + + enableTransparentDataEncryption.run(cache, {}, callback); + }); + + it('should give passing result for managed instance database (TDE always enabled)', function(done) { + const callback = (err, results) => { + expect(results.length).to.equal(2); + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('Transparent data encryption is enabled for managed instance database'); + expect(results[1].region).to.equal('eastus'); + done(); + }; + + const cache = createCache( + [], [], [], + managedInstances, managedDatabases ); enableTransparentDataEncryption.run(cache, {}, callback); @@ -137,7 +218,7 @@ describe('enableTransparentDataEncryption', function() { it('should give unknown result if unable to query for SQL servers', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(3); expect(results[0].message).to.include('Unable to query for SQL servers'); expect(results[0].region).to.equal('eastus'); @@ -145,9 +226,8 @@ describe('enableTransparentDataEncryption', function() { }; const cache = createCache( - [], - databases, - transparentDataEncryptionEnabled, + [], [], [], + [], [], { message: 'unable to query servers' } ); @@ -156,7 +236,7 @@ describe('enableTransparentDataEncryption', function() { it('should give unknown result if unable to query for SQL server databases', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(3); expect(results[0].message).to.include('Unable to query for SQL server databases'); expect(results[0].region).to.equal('eastus'); @@ -164,32 +244,47 @@ describe('enableTransparentDataEncryption', function() { }; const cache = createCache( - servers, - [], - transparentDataEncryptionEnabled, - null, - { message: 'unable to query databases' } + servers, [], [], + [], [], + null, { message: 'unable to query databases' } ); enableTransparentDataEncryption.run(cache, {}, callback); }); - it('should give unknown result if unable to query for SQL Database transparent data encryption', function(done) { + it('should give unknown result if unable to query for managed instances', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(3); - expect(results[0].message).to.include('Unable to query transparent data encryption for SQL Database'); - expect(results[0].region).to.equal('eastus'); + expect(results.length).to.equal(2); + expect(results[1].status).to.equal(3); + expect(results[1].message).to.include('Unable to query for managed instances'); + expect(results[1].region).to.equal('eastus'); + done(); + }; + + const cache = createCache( + [], [], [], + [], [], + null, null, null, + { message: 'unable to query managed instances' } + ); + + enableTransparentDataEncryption.run(cache, {}, callback); + }); + + it('should give unknown result if unable to query for managed instance databases', function(done) { + const callback = (err, results) => { + expect(results.length).to.equal(2); + expect(results[1].status).to.equal(3); + expect(results[1].message).to.include('Unable to query for managed instance databases'); + expect(results[1].region).to.equal('eastus'); done(); }; const cache = createCache( - servers, - databases, - [], - null, - null, - { message: 'unable to query transparent data encryption' } + [], [], [], + managedInstances, [], + null, null, null, null, + { message: 'unable to query managed databases' } ); enableTransparentDataEncryption.run(cache, {}, callback);