From 0d789646e64816f60e2349f90179cbf9c1edde90 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 6 Nov 2024 14:59:52 +0500 Subject: [PATCH 1/3] Revised enabeDefenderForSqlServers Plugin Update --- helpers/azure/api.js | 7 + .../defender/enableDefenderForSqlServers.js | 88 +++++++-- .../enableDefenderForSqlServers.spec.js | 178 +++++++++++------- 3 files changed, 192 insertions(+), 81 deletions(-) diff --git a/helpers/azure/api.js b/helpers/azure/api.js index 000b92f186..c5c036cf55 100644 --- a/helpers/azure/api.js +++ b/helpers/azure/api.js @@ -675,6 +675,13 @@ var postcalls = { url: 'https://management.azure.com/{id}/securityAlertPolicies?api-version=2017-03-01-preview' } }, + serverSecurityAlertPolicies: { + listByServer: { + reliesOnPath: 'servers.listSql', + properties: ['id'], + url: 'https://management.azure.com/{id}/securityAlertPolicies?api-version=2017-03-01-preview' + } + }, advancedThreatProtectionSettings: { listByServer: { diff --git a/plugins/azure/defender/enableDefenderForSqlServers.js b/plugins/azure/defender/enableDefenderForSqlServers.js index a5809de370..6b7e12c444 100644 --- a/plugins/azure/defender/enableDefenderForSqlServers.js +++ b/plugins/azure/defender/enableDefenderForSqlServers.js @@ -6,39 +6,103 @@ module.exports = { category: 'Defender', domain: 'Management and Governance', severity: 'High', - description: 'Ensures that Microsoft Defender is enabled for Azure SQL Server Databases.', + description: 'Ensures that Microsoft Defender is enabled for Azure SQL Server Databases at subscription level or individual resource level.', more_info: 'Turning on Microsoft Defender for Azure SQL Server Databases enables threat detection for Azure SQL database servers, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.', recommended_action: 'Turning on Microsoft Defender for Azure SQL Databases incurs an additional cost per resource.', link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-detection-capabilities', - apis: ['pricings:list'], - realtime_triggers: ['microsoftsecurity:pricings:write','microsoftsecurity:pricings:delete'], + apis: ['pricings:list', 'servers:listSql', 'serverSecurityAlertPolicies:listByServer'], + settings: { + check_level: { + name: 'Defender Check Level', + description: 'Check for Defender at subscription level or resource level', + regex: '^(subscription|resource)$', + default: 'subscription' + } + }, + realtime_triggers: ['microsoftsecurity:pricings:write','microsoftsecurity:pricings:delete','microsoftsql:servers:securityalertpolicies:write'], run: function(cache, settings, callback) { var results = []; var source = {}; var locations = helpers.locations(settings.govcloud); - async.each(locations.pricings, function(location, rcb) { - var pricings = helpers.addSource(cache, source, - ['pricings', 'list', location]); + var config = { + check_level: settings.check_level || this.settings.check_level.default + }; - if (!pricings) return rcb(); + var serviceName = 'sqlservers'; + var serviceDisplayName = 'SQL Servers'; + + + if (config.check_level === 'subscription') { + var pricings = helpers.addSource(cache, source, ['pricings', 'list', 'global']); + + if (!pricings) return callback(null, results, source); if (pricings.err || !pricings.data) { helpers.addResult(results, 3, - 'Unable to query for Pricing: ' + helpers.addError(pricings), location); - return rcb(); + 'Unable to query Pricing information: ' + helpers.addError(pricings), 'global'); + return callback(null, results, source); } if (!pricings.data.length) { - helpers.addResult(results, 0, 'No Pricing information found', location); + helpers.addResult(results, 0, 'No Pricing information found', 'global'); + return callback(null, results, source); + } + + + let pricingData = pricings.data.find((pricing) => pricing.name.toLowerCase() === serviceName); + + if (pricingData && pricingData.pricingTier && pricingData.pricingTier.toLowerCase() === 'standard') { + helpers.addResult(results, 0, + `Azure Defender is enabled for ${serviceDisplayName} at subscription level`, 'global', pricingData.id); + } else { + helpers.addResult(results, 2, + `Azure Defender is not enabled for ${serviceDisplayName} at subscription level`, 'global'); + } + return callback(null, results, source); + } + + async.each(locations.servers, function(location, rcb) { + const 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(); } - helpers.checkMicrosoftDefender(pricings, 'sqlservers', 'SQL Server Databases', results, location); + if (!servers.data.length) { + helpers.addResult(results, 0, 'No SQL servers found', location); + return rcb(); + } + + servers.data.forEach(server => { + const securitySettings = helpers.addSource(cache, source, + ['serverSecurityAlertPolicies', 'listByServer', location, server.id]); + + if (!securitySettings || securitySettings.err || !securitySettings.data) { + helpers.addResult(results, 3, + 'Unable to query for SQL server security alert policies: ' + helpers.addError(securitySettings), + location, server.id); + } else { + securitySettings.data.forEach(setting => { + if (setting.state && setting.state.toLowerCase() === 'enabled') { + helpers.addResult(results, 0, + 'Azure Defender is enabled for SQL server', location, server.id); + } else { + helpers.addResult(results, 2, + 'Azure Defender is not enabled for SQL server', location, server.id); + } + }); + } + }); rcb(); - }, function(){ + }, function() { callback(null, results, source); }); } diff --git a/plugins/azure/defender/enableDefenderForSqlServers.spec.js b/plugins/azure/defender/enableDefenderForSqlServers.spec.js index 26a315964e..2c43f00caa 100644 --- a/plugins/azure/defender/enableDefenderForSqlServers.spec.js +++ b/plugins/azure/defender/enableDefenderForSqlServers.spec.js @@ -1,87 +1,127 @@ -var assert = require('assert'); -var expect = require('chai').expect; -var auth = require('./enableDefenderForSqlServers'); +const assert = require('assert'); +const expect = require('chai').expect; +const plugin = require('./enableDefenderForSqlServers'); -const createCache = (err, data) => { - return { - pricings: { - list: { - 'global': { - err: err, - data: data - } - } - } - } -}; - -describe('enableDefenderForSqlDatabases', function() { +describe('enableDefenderForSqlServers', function() { describe('run', function() { - it('should give passing result if no pricings found', function(done) { - const callback = (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('No Pricing information found'); - expect(results[0].region).to.equal('global'); - done() + it('should give passing result if Defender is enabled at subscription level', function(done) { + const cache = createCache([{ + id: '/subscriptions/123/providers/Microsoft.Security/pricings/SqlServers', + name: 'SqlServers', + pricingTier: 'Standard' + }]); + const settings = { + check_level: 'subscription' }; - const cache = createCache( - null, - [] - ); + plugin.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Azure Defender is enabled for SQL Servers at subscription level'); + done(); + }); + }); - auth.run(cache, {}, callback); - }); + it('should give failing result if Defender is not enabled at subscription level', function(done) { + const cache = createCache([{ + id: '/subscriptions/123/providers/Microsoft.Security/pricings/SqlServers', + name: 'SqlServers', + pricingTier: 'Free' + }]); + const settings = { + check_level: 'subscription' + }; - it('should give failing result if Azure Defender for SQL Server Databases is not enabled', function(done) { - const callback = (err, results) => { + plugin.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); - expect(results[0].message).to.include('Azure Defender is not enabled for SQL Server Databases'); - expect(results[0].region).to.equal('global'); - done() - }; + expect(results[0].message).to.include('Azure Defender is not enabled for SQL Servers at subscription level'); + done(); + }); + }); + it('should give passing result if Defender is enabled at resource level', function(done) { const cache = createCache( - null, - [ - { - "id": "/subscriptions/e79d9a03-3ab3-4481-bdcd-c5db1d55420a/providers/Microsoft.Security/pricings/default", - "name": "SqlServers", - "type": "Microsoft.Security/pricings", - "pricingTier": "free", - "location": "global" - } - ] + [{ + id: '/subscriptions/123/providers/Microsoft.Security/pricings/SqlServers', + name: 'SqlServers', + pricingTier: 'Free' + }], + [{ + id: '/subscriptions/123/servers/test-server' + }], + [{ + id: '/subscriptions/123/servers/test-server/security', + state: 'Enabled' + }] ); + const settings = { + check_level: 'resource' + }; - auth.run(cache, {}, callback); - }); - - it('should give passing result if Azure Defender for SQL Server Databases is enabled', function(done) { - const callback = (err, results) => { + plugin.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('Azure Defender is enabled for SQL Server Databases'); - expect(results[0].region).to.equal('global'); - done() - }; + expect(results[0].message).to.include('Azure Defender is enabled for SQL server'); + done(); + }); + }); + it('should give failing result if Defender is not enabled at resource level', function(done) { const cache = createCache( - null, - [ - { - "id": "/subscriptions/e79d9a03-3ab3-4481-bdcd-c5db1d55420a/providers/Microsoft.Security/pricings/default", - "name": "SqlServers", - "type": "Microsoft.Security/pricings", - "pricingTier": "Standard", - "location": "global" - } - ] + [{ + id: '/subscriptions/123/providers/Microsoft.Security/pricings/SqlServers', + name: 'SqlServers', + pricingTier: 'Free' + }], + [{ + id: '/subscriptions/123/servers/test-server' + }], + [{ + id: '/subscriptions/123/servers/test-server/security', + state: 'Disabled' + }] ); + const settings = { + check_level: 'resource' + }; + + plugin.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Azure Defender is not enabled for SQL server'); + done(); + }); + }); - auth.run(cache, {}, callback); - }) - }) -}); \ No newline at end of file + // Add other necessary test cases for error conditions + }); +}); + +function createCache(pricingData, serversData, securityData) { + return { + pricings: { + list: { + global: { + data: pricingData + } + } + }, + servers: { + listSql: { + 'eastus': { + data: serversData + } + } + }, + serverSecurityAlertPolicies: { + listByServer: { + 'eastus': { + '/subscriptions/123/servers/test-server': { + data: securityData + } + } + } + } + }; +} \ No newline at end of file From c2543934f6428baa72b2de7bee9d9cc178f98980 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 6 Nov 2024 15:02:33 +0500 Subject: [PATCH 2/3] Revised enabeDefenderForSqlServers Plugin Update (2) --- helpers/azure/api.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/helpers/azure/api.js b/helpers/azure/api.js index c5c036cf55..000b92f186 100644 --- a/helpers/azure/api.js +++ b/helpers/azure/api.js @@ -675,13 +675,6 @@ var postcalls = { url: 'https://management.azure.com/{id}/securityAlertPolicies?api-version=2017-03-01-preview' } }, - serverSecurityAlertPolicies: { - listByServer: { - reliesOnPath: 'servers.listSql', - properties: ['id'], - url: 'https://management.azure.com/{id}/securityAlertPolicies?api-version=2017-03-01-preview' - } - }, advancedThreatProtectionSettings: { listByServer: { From 67969e25a7bcb786fec82f79806123c7a95958cb Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Thu, 7 Nov 2024 17:23:15 +0500 Subject: [PATCH 3/3] Revised enableDefenderForSqlSever function --- plugins/azure/defender/enableDefenderForSqlServers.js | 11 +---------- .../defender/enableDefenderForSqlServers.spec.js | 4 ++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/plugins/azure/defender/enableDefenderForSqlServers.js b/plugins/azure/defender/enableDefenderForSqlServers.js index 6b7e12c444..b529d9665f 100644 --- a/plugins/azure/defender/enableDefenderForSqlServers.js +++ b/plugins/azure/defender/enableDefenderForSqlServers.js @@ -50,16 +50,7 @@ module.exports = { return callback(null, results, source); } - - let pricingData = pricings.data.find((pricing) => pricing.name.toLowerCase() === serviceName); - - if (pricingData && pricingData.pricingTier && pricingData.pricingTier.toLowerCase() === 'standard') { - helpers.addResult(results, 0, - `Azure Defender is enabled for ${serviceDisplayName} at subscription level`, 'global', pricingData.id); - } else { - helpers.addResult(results, 2, - `Azure Defender is not enabled for ${serviceDisplayName} at subscription level`, 'global'); - } + helpers.checkMicrosoftDefender(pricings, serviceName, serviceDisplayName, results, 'global'); return callback(null, results, source); } diff --git a/plugins/azure/defender/enableDefenderForSqlServers.spec.js b/plugins/azure/defender/enableDefenderForSqlServers.spec.js index 2c43f00caa..fd4808c7b7 100644 --- a/plugins/azure/defender/enableDefenderForSqlServers.spec.js +++ b/plugins/azure/defender/enableDefenderForSqlServers.spec.js @@ -17,7 +17,7 @@ describe('enableDefenderForSqlServers', function() { plugin.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('Azure Defender is enabled for SQL Servers at subscription level'); + expect(results[0].message).to.include('Azure Defender is enabled for SQL Servers'); done(); }); }); @@ -35,7 +35,7 @@ describe('enableDefenderForSqlServers', function() { plugin.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); - expect(results[0].message).to.include('Azure Defender is not enabled for SQL Servers at subscription level'); + expect(results[0].message).to.include('Azure Defender is not enabled for SQL Servers'); done(); }); });