From 0216263a14e29316aaaae87c9fa285b410c2a791 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 6 Nov 2024 17:16:58 +0500 Subject: [PATCH 1/5] Revised dbTDEEnabled --- plugins/azure/sqldatabases/dbTDEEnabled.js | 140 +++++++++----- .../azure/sqldatabases/dbTDEEnabled.spec.js | 175 ++++++++++++++---- 2 files changed, 229 insertions(+), 86 deletions(-) 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); From 7f6b3ce30bd24fca7a0d75e7958562c8fe52b362 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 6 Nov 2024 17:19:52 +0500 Subject: [PATCH 2/5] Revised dbTDEEnabled --- plugins/azure/sqldatabases/dbTDEEnabled.js | 8 +++++--- plugins/azure/sqldatabases/dbTDEEnabled.spec.js | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/azure/sqldatabases/dbTDEEnabled.js b/plugins/azure/sqldatabases/dbTDEEnabled.js index d23d7344a0..b63c4bb8d7 100644 --- a/plugins/azure/sqldatabases/dbTDEEnabled.js +++ b/plugins/azure/sqldatabases/dbTDEEnabled.js @@ -59,9 +59,11 @@ module.exports = { } 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); + helpers.addResult(results, 0, + 'SQL Database: Transparent data encryption is enabled', location, database.id); } else { - helpers.addResult(results, 2, 'Transparent data encryption is not enabled for SQL Database', location, database.id); + helpers.addResult(results, 2, + 'SQL Database: Transparent data encryption is not enabled', location, database.id); } } }); @@ -105,7 +107,7 @@ module.exports = { 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); + 'Managed Instance Database: Transparent data encryption is enabled', location, database.id); } }); } diff --git a/plugins/azure/sqldatabases/dbTDEEnabled.spec.js b/plugins/azure/sqldatabases/dbTDEEnabled.spec.js index fc45730589..a21a6218e0 100644 --- a/plugins/azure/sqldatabases/dbTDEEnabled.spec.js +++ b/plugins/azure/sqldatabases/dbTDEEnabled.spec.js @@ -129,7 +129,7 @@ describe('enableTransparentDataEncryption', function() { const callback = (err, results) => { 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(); }; @@ -146,7 +146,7 @@ describe('enableTransparentDataEncryption', function() { const callback = (err, results) => { 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(); }; @@ -203,7 +203,7 @@ describe('enableTransparentDataEncryption', function() { 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].message).to.include('Managed Instance Database: Transparent data encryption is enabled'); expect(results[1].region).to.equal('eastus'); done(); }; From f4c3d30527370f99c6b937268dbeea7423b74de6 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 7 Nov 2024 19:09:42 +0500 Subject: [PATCH 3/5] Apply suggestions from code review suggested fix Co-authored-by: Fatima <66124862+fatima99s@users.noreply.github.com> --- plugins/azure/sqldatabases/dbTDEEnabled.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/azure/sqldatabases/dbTDEEnabled.js b/plugins/azure/sqldatabases/dbTDEEnabled.js index b63c4bb8d7..69296a5573 100644 --- a/plugins/azure/sqldatabases/dbTDEEnabled.js +++ b/plugins/azure/sqldatabases/dbTDEEnabled.js @@ -51,9 +51,11 @@ module.exports = { databases.data.forEach(database => { if (database.name && database.name.toLowerCase() !== 'master') { - var transparentDataEncryption = helpers.addSource(cache, source, ['transparentDataEncryption', 'list', location, database.id]); + var transparentDataEncryption = helpers.addSource(cache, source, + ['transparentDataEncryption', 'list', location, database.id]); - if (!transparentDataEncryption || transparentDataEncryption.err || !transparentDataEncryption.data || !transparentDataEncryption.data.length) { + 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; } From cbd4206bfc03cc1a9adbd23e001158d15ccfc4f3 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Thu, 7 Nov 2024 20:10:02 +0500 Subject: [PATCH 4/5] lint resolve --- plugins/azure/sqldatabases/dbTDEEnabled.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/azure/sqldatabases/dbTDEEnabled.js b/plugins/azure/sqldatabases/dbTDEEnabled.js index b63c4bb8d7..e1314a28bc 100644 --- a/plugins/azure/sqldatabases/dbTDEEnabled.js +++ b/plugins/azure/sqldatabases/dbTDEEnabled.js @@ -7,7 +7,7 @@ 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', 'managedInstances:list', 'managedDatabases:listByInstance'], From 49f8b66aafda536162c1c16992a6b3c62732ff28 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Thu, 7 Nov 2024 20:19:57 +0500 Subject: [PATCH 5/5] lint resolve --- plugins/azure/sqldatabases/dbTDEEnabled.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/azure/sqldatabases/dbTDEEnabled.js b/plugins/azure/sqldatabases/dbTDEEnabled.js index 26834ffe80..f8a929b4c5 100644 --- a/plugins/azure/sqldatabases/dbTDEEnabled.js +++ b/plugins/azure/sqldatabases/dbTDEEnabled.js @@ -52,7 +52,7 @@ module.exports = { if (database.name && database.name.toLowerCase() !== 'master') { var transparentDataEncryption = helpers.addSource(cache, source, - ['transparentDataEncryption', 'list', location, database.id]); + ['transparentDataEncryption', 'list', location, database.id]); if (!transparentDataEncryption || transparentDataEncryption.err || !transparentDataEncryption.data || !transparentDataEncryption.data.length) {