From eaad9c418265f232eb982284ebb343b84cb8b463 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:53:46 +0500 Subject: [PATCH] Revised dbTDEEnabled (#2110) * Revised dbTDEEnabled * Revised dbTDEEnabled * Apply suggestions from code review suggested fix Co-authored-by: Fatima <66124862+fatima99s@users.noreply.github.com> * lint resolve * lint resolve --------- Co-authored-by: AkhtarAmir Co-authored-by: Fatima <66124862+fatima99s@users.noreply.github.com> Co-authored-by: hamza --- plugins/azure/sqldatabases/dbTDEEnabled.js | 146 +++++++++----- .../azure/sqldatabases/dbTDEEnabled.spec.js | 179 ++++++++++++++---- 2 files changed, 236 insertions(+), 89 deletions(-) diff --git a/plugins/azure/sqldatabases/dbTDEEnabled.js b/plugins/azure/sqldatabases/dbTDEEnabled.js index 1352a00de8..f8a929b4c5 100644 --- a/plugins/azure/sqldatabases/dbTDEEnabled.js +++ b/plugins/azure/sqldatabases/dbTDEEnabled.js @@ -7,10 +7,10 @@ module.exports = { domain: 'Databases', severity: 'Medium', description: 'Ensure that Transparent Data Encryption (TDE) is enabled for SQL databases.', - 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.', + more_info: 'Transparent data encryption (TDE) helps protect Azure SQL Databases, Managed Instances, 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,107 @@ 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, + 'SQL Database: Transparent data encryption is enabled', location, database.id); + } else { + helpers.addResult(results, 2, + 'SQL Database: Transparent data encryption is not enabled', 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, + 'Managed Instance Database: Transparent data encryption is enabled', 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..a21a6218e0 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,17 +127,16 @@ 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].message).to.include('SQL Database: Transparent data encryption is enabled'); expect(results[0].region).to.equal('eastus'); done(); }; const cache = createCache( - servers, - databases, - transparentDataEncryptionEnabled + servers, databases, transparentDataEncryptionEnabled, + [], [] ); enableTransparentDataEncryption.run(cache, {}, callback); @@ -114,22 +144,73 @@ 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].message).to.include('SQL Database: Transparent data encryption is not enabled'); expect(results[0].region).to.equal('eastus'); done(); }; 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('Managed Instance Database: Transparent data encryption is enabled'); + 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);