From 1f3b4209313a54c07d883dc39c8cda79fa00a742 Mon Sep 17 00:00:00 2001 From: Taha Date: Mon, 16 Feb 2026 15:43:31 +0500 Subject: [PATCH 01/19] Fix storage account public network access check to detect open CIDR ranges --- .../storageAccountPublicNetworkAccess.js | 30 +++++++++- .../storageAccountPublicNetworkAccess.spec.js | 56 ++++++++++++++++++- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js index 83dd52a9e4..cf9c250b12 100644 --- a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js +++ b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js @@ -17,6 +17,16 @@ module.exports = { var results = []; var source = {}; var locations = helpers.locations(settings.govcloud); + + function isOpenCidrRange(cidr) { + if (!cidr || typeof cidr !== 'string') return false; + + const trimmed = cidr.trim(); + // Check for exact matches that indicate fully open access + return trimmed === '0.0.0.0/0' || + trimmed === '::/0' || + trimmed === '0.0.0.0'; + } async.each(locations.storageAccounts, function(location, rcb) { var storageAccount = helpers.addSource(cache, source, @@ -37,9 +47,25 @@ module.exports = { for (let account of storageAccount.data) { if (!account.id) continue; - - if (account.publicNetworkAccess && (account.publicNetworkAccess.toLowerCase() == 'disabled' || account.publicNetworkAccess.toLowerCase() == 'securedbyperimeter')){ + const hasIpRules = account.networkAcls && account.networkAcls.ipRules && account.networkAcls.ipRules.length > 0; + let hasOpenCidr = false; + if (hasIpRules) { + for (let rule of account.networkAcls.ipRules) { + if (isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { + hasOpenCidr = true; + break; + } + } + } + if (account.publicNetworkAccess && (account.publicNetworkAccess.toLowerCase() == 'disabled' || account.publicNetworkAccess.toLowerCase() == 'securedbyperimeter' )){ helpers.addResult(results, 0, 'Storage account has public network access disabled', location, account.id); + } else if (account.publicNetworkAccess && account.publicNetworkAccess.toLowerCase() == 'enabled') { + if (account.networkAcls && account.networkAcls.defaultAction && account.networkAcls.defaultAction.toLowerCase() === 'deny' && !hasOpenCidr) { + helpers.addResult(results, 0, 'Storage account has public network access disabled', location, account.id); + } + else { + helpers.addResult(results, 2, 'Storage account has public network access enabled for all networks', location, account.id); + } } else { helpers.addResult(results, 2, 'Storage account does not have public network access disabled', location, account.id); } diff --git a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.spec.js b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.spec.js index 454522eda5..6673be93fa 100644 --- a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.spec.js +++ b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.spec.js @@ -22,6 +22,38 @@ const storageAccounts = [ 'name': 'acc', 'tags': {}, "publicNetworkAccess": "SecuredByPerimeter" + }, + { + 'id': '/subscriptions/123/resourceGroups/aqua-resource-group/providers/Microsoft.Storage/storageAccounts/acc', + 'location': 'eastus', + 'name': 'acc', + 'tags': {}, + "publicNetworkAccess": "Enabled", + "networkAcls": { + "defaultAction": "Deny", + "ipRules": [ + { + "value": "192.168.1.0/24", + "action": "Allow" + } + ] + } + }, + { + 'id': '/subscriptions/123/resourceGroups/aqua-resource-group/providers/Microsoft.Storage/storageAccounts/acc', + 'location': 'eastus', + 'name': 'acc', + 'tags': {}, + "publicNetworkAccess": "Enabled", + "networkAcls": { + "defaultAction": "Deny", + "ipRules": [ + { + "value": "0.0.0.0/0", + "action": "Allow" + } + ] + } } ]; @@ -87,7 +119,7 @@ describe('storageAccountPublicNetworkAccess', function() { storageAccountPublicNetworkAccess.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); - expect(results[0].message).to.include('Storage account does not have public network access disabled'); + expect(results[0].message).to.include('Storage account has public network access enabled for all networks'); expect(results[0].region).to.equal('eastus'); done(); }); @@ -103,5 +135,27 @@ describe('storageAccountPublicNetworkAccess', function() { done(); }); }); + + it('should give passing result if Storage account has public network access enabled but restricted by network ACLs', function(done) { + const cache = createCache([storageAccounts[3]]); + storageAccountPublicNetworkAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Storage account has public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Storage account has public network access enabled with 0.0.0.0/0', function(done) { + const cache = createCache([storageAccounts[4]]); + storageAccountPublicNetworkAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Storage account has public network access enabled for all networks'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); }); }); \ No newline at end of file From d713281326d67dca770a63a3ece399f1ec4ac2be Mon Sep 17 00:00:00 2001 From: Taha Date: Tue, 17 Feb 2026 14:14:33 +0500 Subject: [PATCH 02/19] fixed some issues --- .../storageAccountPublicNetworkAccess.js | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js index cf9c250b12..3dd1b2033a 100644 --- a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js +++ b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js @@ -47,9 +47,15 @@ module.exports = { for (let account of storageAccount.data) { if (!account.id) continue; - const hasIpRules = account.networkAcls && account.networkAcls.ipRules && account.networkAcls.ipRules.length > 0; - let hasOpenCidr = false; + + if (account.publicNetworkAccess && (account.publicNetworkAccess.toLowerCase() == 'disabled' || account.publicNetworkAccess.toLowerCase() == 'securedbyperimeter' )){ + helpers.addResult(results, 0, 'Storage account has public network access disabled', location, account.id); + } else { + const hasIpRules = account.networkAcls && account.networkAcls.ipRules && account.networkAcls.ipRules.length > 0; + let hasOpenCidr = false; + if (hasIpRules) { + for (let rule of account.networkAcls.ipRules) { if (isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { hasOpenCidr = true; @@ -57,18 +63,16 @@ module.exports = { } } } - if (account.publicNetworkAccess && (account.publicNetworkAccess.toLowerCase() == 'disabled' || account.publicNetworkAccess.toLowerCase() == 'securedbyperimeter' )){ - helpers.addResult(results, 0, 'Storage account has public network access disabled', location, account.id); - } else if (account.publicNetworkAccess && account.publicNetworkAccess.toLowerCase() == 'enabled') { - if (account.networkAcls && account.networkAcls.defaultAction && account.networkAcls.defaultAction.toLowerCase() === 'deny' && !hasOpenCidr) { + + const restricted = account.networkAcls && account.networkAcls.defaultAction && account.networkAcls.defaultAction.toLowerCase() === 'deny'; + + if ( restricted && !hasOpenCidr) { helpers.addResult(results, 0, 'Storage account has public network access disabled', location, account.id); } else { helpers.addResult(results, 2, 'Storage account has public network access enabled for all networks', location, account.id); } - } else { - helpers.addResult(results, 2, 'Storage account does not have public network access disabled', location, account.id); - } + } } rcb(); From 1f694322e67b12181253bcea9760caa0293bd205 Mon Sep 17 00:00:00 2001 From: Taha Date: Tue, 17 Feb 2026 18:02:47 +0500 Subject: [PATCH 03/19] Fixed App config public access --- .../azure/appConfigurations/appConfigurationPublicAccess.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/azure/appConfigurations/appConfigurationPublicAccess.js b/plugins/azure/appConfigurations/appConfigurationPublicAccess.js index 57a7080ec3..655b1822ae 100644 --- a/plugins/azure/appConfigurations/appConfigurationPublicAccess.js +++ b/plugins/azure/appConfigurations/appConfigurationPublicAccess.js @@ -36,8 +36,9 @@ module.exports = { for (let appConfiguration of appConfigurations.data) { if (!appConfiguration.id) continue; - - if (appConfiguration.publicNetworkAccess && appConfiguration.publicNetworkAccess.toLowerCase() === 'disabled') { + const hasPrivateEndpoint = appConfiguration.privateEndpointConnections + && appConfiguration.privateEndpointConnections.length > 0; + if ((appConfiguration.publicNetworkAccess && appConfiguration.publicNetworkAccess.toLowerCase() === 'disabled') || hasPrivateEndpoint) { helpers.addResult(results, 0, 'App Configuration has public network access disabled', location, appConfiguration.id); } else { helpers.addResult(results, 2, 'App Configuration does not have public network access disabled', location, appConfiguration.id); From 774b0648b0424e93850a553aeeb23549299f47b1 Mon Sep 17 00:00:00 2001 From: Taha Date: Tue, 17 Feb 2026 20:13:48 +0500 Subject: [PATCH 04/19] Fixed the specsheet for appConfig --- .../appConfigurationPublicAccess.spec.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/plugins/azure/appConfigurations/appConfigurationPublicAccess.spec.js b/plugins/azure/appConfigurations/appConfigurationPublicAccess.spec.js index 7503b92375..42e710903c 100644 --- a/plugins/azure/appConfigurations/appConfigurationPublicAccess.spec.js +++ b/plugins/azure/appConfigurations/appConfigurationPublicAccess.spec.js @@ -48,6 +48,40 @@ const appConfigurations = [ } } } + }, + { + "type": "Microsoft.AppConfiguration/configurationStores", + "location": "eastus", + "provisioningState": "Succeeded", + "creationDate": "2023-12-27T09:26:54+00:00", + "endpoint": "https://dummy-test-rg.azconfig.io", + "encryption": { + "keyVaultProperties": null + }, + "privateEndpointConnections": [ + { + "id": "/subscriptions/123/resourceGroups/dummy-rg/providers/Microsoft.AppConfiguration/configurationStores/dummy-test-rg/privateEndpointConnections/dummyConnection", + "name": "dummyConnection", + "type": "Microsoft.AppConfiguration/configurationStores/privateEndpointConnections", + "properties": { + "provisioningState": "Succeeded", + "privateEndpoint": { + "id": "/subscriptions/123/resourceGroups/dummy-rg/providers/Microsoft.Network/privateEndpoints/dummyEndpoint" + }, + "privateLinkServiceConnectionState": { + "status": "Approved", + "description": "Auto approved" + } + } + } + ], + "publicNetworkAccess": "Enabled", + "disableLocalAuth": false, + "softDeleteRetentionInDays": 0, + "enablePurgeProtection": false, + "id": "/subscriptions/123/resourceGroups/dummy-rg/providers/Microsoft.AppConfiguration/configurationStores/dummy-test-rg-private", + "name": "dummy-test-rg-private", + "tags": {} } ]; @@ -110,5 +144,16 @@ describe('appConfigurationPublicAccess', function () { done(); }); }); + + it('should give passing result if App Configuration has private endpoint connections', function (done) { + const cache = createCache([appConfigurations[2]]); + appConfigurationPublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('App Configuration has public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); }); }); \ No newline at end of file From ee55b7d332c1c4b1d3c2b34c20872628d689cdde Mon Sep 17 00:00:00 2001 From: Taha Date: Tue, 17 Feb 2026 20:54:12 +0500 Subject: [PATCH 05/19] Fixed App Service public access --- .../appservice/appServicePublicAccess.js | 36 ++++++++++++-- .../appservice/appServicePublicAccess.spec.js | 48 +++++++++++++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/plugins/azure/appservice/appServicePublicAccess.js b/plugins/azure/appservice/appServicePublicAccess.js index 246ca91c29..a4d3170a32 100644 --- a/plugins/azure/appservice/appServicePublicAccess.js +++ b/plugins/azure/appservice/appServicePublicAccess.js @@ -1,5 +1,6 @@ var async = require('async'); var helpers = require('../../../helpers/azure'); +const cidrHelper = require('../../../helpers/azure/cidr'); module.exports = { title: 'App Service Public Network Access Disabled', @@ -50,14 +51,39 @@ module.exports = { var config = webConfigs.data[0]; - if (config.publicNetworkAccess && config.publicNetworkAccess.toLowerCase() === 'disabled') { + if (config.publicNetworkAccess && + config.publicNetworkAccess.toLowerCase() === 'disabled') { + helpers.addResult(results, 0, 'App Service has public network access disabled', location, webApp.id); - } else { - helpers.addResult(results, 2, - 'App Service does not have public network access disabled', - location, webApp.id); + return; + } + else { + let hasOpenCidr = false; + + if (config.ipSecurityRestrictions && config.ipSecurityRestrictions.length) { + for (let rule of config.ipSecurityRestrictions) { + if (cidrHelper.isOpenCidrRange(rule.ipAddress)) { + hasOpenCidr = true; + break; + } + } + } + + const restricted = + config.ipSecurityRestrictionsDefaultAction && + config.ipSecurityRestrictionsDefaultAction.toLowerCase() === 'deny' && !hasOpenCidr; + + if (restricted) { + helpers.addResult(results, 0, + 'App Service has public network access disabled', + location, webApp.id); + } else { + helpers.addResult(results, 2, + 'App Service does not have public network access disabled', + location, webApp.id); + } } }); diff --git a/plugins/azure/appservice/appServicePublicAccess.spec.js b/plugins/azure/appservice/appServicePublicAccess.spec.js index cd732733d9..d00f2d9583 100644 --- a/plugins/azure/appservice/appServicePublicAccess.spec.js +++ b/plugins/azure/appservice/appServicePublicAccess.spec.js @@ -39,6 +39,32 @@ const listConfigurations = [ { 'id': '/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Web/sites/test-app-3/config/web', 'name': 'web' + }, + { + 'id': '/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Web/sites/test-app-4/config/web', + 'name': 'web', + 'publicNetworkAccess': 'Enabled', + 'ipSecurityRestrictionsDefaultAction': 'Deny', + 'ipSecurityRestrictions': [ + { + 'ipAddress': '192.168.1.0/24', + 'action': 'Allow', + 'name': 'AllowInternal' + } + ] + }, + { + 'id': '/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Web/sites/test-app-5/config/web', + 'name': 'web', + 'publicNetworkAccess': 'Enabled', + 'ipSecurityRestrictionsDefaultAction': 'Deny', + 'ipSecurityRestrictions': [ + { + 'ipAddress': '0.0.0.0/0', + 'action': 'Allow', + 'name': 'AllowAll' + } + ] } ]; @@ -147,5 +173,27 @@ describe('appServicePublicAccess', function () { done(); }); }); + + it('should give passing result if App Service has restricted IP security with no open CIDR', function (done) { + const cache = createCache([webApps[0]], [listConfigurations[3]]); + appServicePublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('App Service has public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if App Service has open CIDR range in IP security restrictions', function (done) { + const cache = createCache([webApps[1]], [listConfigurations[4]]); + appServicePublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('App Service does not have public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); }); }); \ No newline at end of file From 2bad15307493bc7dedd9f6d24380f28a80ec8e3f Mon Sep 17 00:00:00 2001 From: Taha Date: Wed, 18 Feb 2026 14:52:05 +0500 Subject: [PATCH 06/19] Fixed automation account public access --- .../automationAcctPublicAccess.js | 7 ++++- .../automationAcctPublicAccess.spec.js | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/plugins/azure/automationAccounts/automationAcctPublicAccess.js b/plugins/azure/automationAccounts/automationAcctPublicAccess.js index 47e9d730f5..04bb628383 100644 --- a/plugins/azure/automationAccounts/automationAcctPublicAccess.js +++ b/plugins/azure/automationAccounts/automationAcctPublicAccess.js @@ -47,7 +47,12 @@ module.exports = { } if (Object.prototype.hasOwnProperty.call(describeAcct.data, 'publicNetworkAccess')) { - if (describeAcct.data.publicNetworkAccess) { + const hasActivePrivateEndpoint = describeAcct.data.privateEndpointConnections && + describeAcct.data.privateEndpointConnections.length > 0 && + describeAcct.data.privateEndpointConnections.some(conn => + conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + ); + if (describeAcct.data.publicNetworkAccess && !hasActivePrivateEndpoint) { helpers.addResult(results, 2, 'Automation account does not have public network access disabled', location, account.id); } else { helpers.addResult(results, 0, 'Automation account has public network access disabled', location, account.id); diff --git a/plugins/azure/automationAccounts/automationAcctPublicAccess.spec.js b/plugins/azure/automationAccounts/automationAcctPublicAccess.spec.js index 5d4ce4e11e..2ae8b11990 100644 --- a/plugins/azure/automationAccounts/automationAcctPublicAccess.spec.js +++ b/plugins/azure/automationAccounts/automationAcctPublicAccess.spec.js @@ -36,6 +36,26 @@ const account = [ "type": "Microsoft.Automation/AutomationAccounts", "tags": {}, "publicNetworkAccess": true, + }, + { + "id": "/subscriptions/12345/resourceGroups/DefaultResourceGroup-WUS/providers/Microsoft.Automation/automationAccounts/Automate-12345-WUS", + "location": "westus", + "name": "Automate-12345-WUS", + "type": "Microsoft.Automation/AutomationAccounts", + "tags": {}, + "publicNetworkAccess": true, + "privateEndpointConnections": [ + { + "id": "/subscriptions/12345/resourceGroups/DefaultResourceGroup-WUS/providers/Microsoft.Automation/automationAccounts/Automate-12345-WUS/privateEndpointConnections/pe-conn-1", + "name": "pe-conn-1", + "properties": { + "privateLinkServiceConnectionState": { + "status": "Approved", + "description": "Auto-approved" + } + } + } + ] } ]; @@ -110,5 +130,16 @@ describe('automationAcctPublicAccess', function () { done(); }); }); + + it('should give passing result if automation account has public network access enabled but with active private endpoint', function (done) { + const cache = createCache(automationAccounts, account[2]); + automationAcctPublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Automation account has public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); }); }); \ No newline at end of file From 8f0347944004b669f762e4de6960dfa115099721 Mon Sep 17 00:00:00 2001 From: Taha Date: Wed, 18 Feb 2026 14:54:11 +0500 Subject: [PATCH 07/19] Used the helper function --- helpers/azure/cidr.js | 10 ++++++++++ .../storageAccountPublicNetworkAccess.js | 13 ++----------- 2 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 helpers/azure/cidr.js diff --git a/helpers/azure/cidr.js b/helpers/azure/cidr.js new file mode 100644 index 0000000000..4262d736c9 --- /dev/null +++ b/helpers/azure/cidr.js @@ -0,0 +1,10 @@ +module.exports = { + isOpenCidrRange: function(cidr) { + if (!cidr || typeof cidr !== 'string') return false; + + const trimmed = cidr.trim(); + return trimmed === '0.0.0.0/0' || + trimmed === '::/0' || + trimmed === '0.0.0.0'; + }, +}; \ No newline at end of file diff --git a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js index 3dd1b2033a..604bfd0ef4 100644 --- a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js +++ b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js @@ -1,5 +1,6 @@ var async = require('async'); var helpers = require('../../../helpers/azure/'); +var cidr = require('../../../helpers/azure/cidr'); module.exports = { title: 'Storage Account Public Network Access', @@ -17,16 +18,6 @@ module.exports = { var results = []; var source = {}; var locations = helpers.locations(settings.govcloud); - - function isOpenCidrRange(cidr) { - if (!cidr || typeof cidr !== 'string') return false; - - const trimmed = cidr.trim(); - // Check for exact matches that indicate fully open access - return trimmed === '0.0.0.0/0' || - trimmed === '::/0' || - trimmed === '0.0.0.0'; - } async.each(locations.storageAccounts, function(location, rcb) { var storageAccount = helpers.addSource(cache, source, @@ -57,7 +48,7 @@ module.exports = { if (hasIpRules) { for (let rule of account.networkAcls.ipRules) { - if (isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { + if (cidr.isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { hasOpenCidr = true; break; } From cd8a1214ad9bc17c9920a793413019ee87f313a9 Mon Sep 17 00:00:00 2001 From: Taha Date: Wed, 18 Feb 2026 17:02:28 +0500 Subject: [PATCH 08/19] Fixed batch account public access --- .../batchAccountsPublicAccess.js | 11 +++++-- .../batchAccountsPublicAccess.spec.js | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/plugins/azure/batchAccounts/batchAccountsPublicAccess.js b/plugins/azure/batchAccounts/batchAccountsPublicAccess.js index 505ed92048..3c427818e6 100644 --- a/plugins/azure/batchAccounts/batchAccountsPublicAccess.js +++ b/plugins/azure/batchAccounts/batchAccountsPublicAccess.js @@ -36,9 +36,14 @@ module.exports = { for (let batchAccount of batchAccounts.data) { if (!batchAccount.id) continue; - - if (batchAccount.publicNetworkAccess && - batchAccount.publicNetworkAccess.toLowerCase() === 'enabled') { + + const hasActivePrivateEndpoint = batchAccount.privateEndpointConnections && + batchAccount.privateEndpointConnections.length > 0 && + batchAccount.privateEndpointConnections.some(conn => + conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + ); + if ((batchAccount.publicNetworkAccess && + batchAccount.publicNetworkAccess.toLowerCase() === 'enabled') && !hasActivePrivateEndpoint) { helpers.addResult(results, 2, 'Batch account is publicly accessible', location, batchAccount.id); } else { helpers.addResult(results, 0, 'Batch account is not publicly accessible', location, batchAccount.id); diff --git a/plugins/azure/batchAccounts/batchAccountsPublicAccess.spec.js b/plugins/azure/batchAccounts/batchAccountsPublicAccess.spec.js index ca54e124fe..7274c013ed 100644 --- a/plugins/azure/batchAccounts/batchAccountsPublicAccess.spec.js +++ b/plugins/azure/batchAccounts/batchAccountsPublicAccess.spec.js @@ -21,6 +21,27 @@ const batchAccounts = [ "nodeManagementEndpoint": "123456789.eastus.service.batch.azure.com", "publicNetworkAccess": "Enabled" }, + { + "id": "/subscriptions/1234566/resourceGroups/dummy/providers/Microsoft.Batch/batchAccounts/test-private", + "name": "test-private", + "type": "Microsoft.Batch/batchAccounts", + "location": "eastus", + "accountEndpoint": "test-private.eastus.batch.azure.com", + "nodeManagementEndpoint": "123456789.eastus.service.batch.azure.com", + "publicNetworkAccess": "Enabled", + "privateEndpointConnections": [ + { + "id": "/subscriptions/1234566/resourceGroups/dummy/providers/Microsoft.Batch/batchAccounts/test-private/privateEndpointConnections/connection1", + "name": "connection1", + "type": "Microsoft.Batch/batchAccounts/privateEndpointConnections", + "properties": { + "privateLinkServiceConnectionState": { + "status": "Approved" + } + } + } + ] + }, ]; const createCache = (batchAccounts) => { @@ -91,5 +112,16 @@ describe('batchAccountsPublicAccess', function () { done(); }); }); + + it('should give passing result if Batch account is publicly accessible but has active private endpoint', function (done) { + const cache = createCache([batchAccounts[2]]); + batchAccountsPublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Batch account is not publicly accessible'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); }); }); \ No newline at end of file From 6e16e2387ad0d53e780293dee8f0c1598ec1b5e7 Mon Sep 17 00:00:00 2001 From: Taha Date: Wed, 18 Feb 2026 19:11:17 +0500 Subject: [PATCH 09/19] Fixed ACR public access --- .../containerregistry/acrPublicAccess.js | 9 +- .../containerregistry/acrPublicAccess.spec.js | 96 +++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/plugins/azure/containerregistry/acrPublicAccess.js b/plugins/azure/containerregistry/acrPublicAccess.js index 61da38f39b..1ab9f3e8a0 100644 --- a/plugins/azure/containerregistry/acrPublicAccess.js +++ b/plugins/azure/containerregistry/acrPublicAccess.js @@ -38,7 +38,14 @@ module.exports = { for (let registry of registries.data){ if (!registry.id) continue; - if (registry.publicNetworkAccess && registry.publicNetworkAccess.toLowerCase() === 'enabled'){ + const hasPrivateEndpoint = registry.privateEndpointConnections && + registry.privateEndpointConnections.length > 0; + + const hasSelectedNetworks = registry.networkRuleSet && + registry.networkRuleSet.defaultAction && + registry.networkRuleSet.defaultAction.toLowerCase() === 'deny'; + + if ((!registry.publicNetworkAccess || registry.publicNetworkAccess.toLowerCase() !== 'disabled') && !hasPrivateEndpoint && !hasSelectedNetworks) { helpers.addResult(results, 2, 'Container registry is publicly accessible', location, registry.id); } else { helpers.addResult(results, 0, 'Container registry is not publicly accessible', location, registry.id); diff --git a/plugins/azure/containerregistry/acrPublicAccess.spec.js b/plugins/azure/containerregistry/acrPublicAccess.spec.js index 37e42f6954..afaa36ec48 100644 --- a/plugins/azure/containerregistry/acrPublicAccess.spec.js +++ b/plugins/azure/containerregistry/acrPublicAccess.spec.js @@ -99,6 +99,102 @@ describe('acrPublicAccess', function() { ] ); + acrPublicAccess.run(cache, {}, callback); + }); + + it('should give passing result if selected networks are configured', 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('Container registry is not publicly accessible'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + const cache = createCache( + null, + [ + { + "id": "/subscriptions/ade0e01e-f9cd-49d3-bba7-d5a5362a3414/resourceGroups/devresourcegroup/providers/Microsoft.ContainerRegistry/registries/testregistry12543", + "name": "testregistry12543", + "type": "Microsoft.ContainerRegistry/registries", + "location": "eastus", + "publicNetworkAccess": "Enabled", + "networkRuleSet": { + "defaultAction": "Deny", + "ipRules": [ + { + "action": "Allow", + "ipAddressOrCidr": "103.177.240.106" + } + ] + } + } + ] + ); + + acrPublicAccess.run(cache, {}, callback); + }); + + it('should give failing result if all networks are selected', function(done) { + const callback = (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Container registry is publicly accessible'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + const cache = createCache( + null, + [ + { + "id": "/subscriptions/ade0e01e-f9cd-49d3-bba7-d5a5362a3414/resourceGroups/devresourcegroup/providers/Microsoft.ContainerRegistry/registries/testregistry12543", + "name": "testregistry12543", + "type": "Microsoft.ContainerRegistry/registries", + "location": "eastus", + "publicNetworkAccess": "Enabled", + "networkRuleSet": { + "defaultAction": "Allow" + } + } + ] + ); + + acrPublicAccess.run(cache, {}, callback); + }); + + it('should give passing result if private endpoint is approved', 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('Container registry is not publicly accessible'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + const cache = createCache( + null, + [ + { + "id": "/subscriptions/ade0e01e-f9cd-49d3-bba7-d5a5362a3414/resourceGroups/devresourcegroup/providers/Microsoft.ContainerRegistry/registries/testregistry12543", + "name": "testregistry12543", + "type": "Microsoft.ContainerRegistry/registries", + "location": "eastus", + "publicNetworkAccess": "Enabled", + "privateEndpointConnections": [ + { + "properties": { + "privateLinkServiceConnectionState": { + "status": "Approved" + } + } + } + ] + } + ] + ); + acrPublicAccess.run(cache, {}, callback); }) }) From a5270db536cc4002e3a6ece8d22307fe8f8e11be Mon Sep 17 00:00:00 2001 From: Taha Date: Wed, 18 Feb 2026 19:56:46 +0500 Subject: [PATCH 10/19] Fixing Domain public access --- plugins/azure/eventGrid/domainPublicAccess.js | 35 +++++++++- .../eventGrid/domainPublicAccess.spec.js | 67 +++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/plugins/azure/eventGrid/domainPublicAccess.js b/plugins/azure/eventGrid/domainPublicAccess.js index 3db87c8cc0..7d4cf772ab 100644 --- a/plugins/azure/eventGrid/domainPublicAccess.js +++ b/plugins/azure/eventGrid/domainPublicAccess.js @@ -1,5 +1,6 @@ const async = require('async'); const helpers = require('../../../helpers/azure'); +const cidrHelper = require('../../../helpers/azure/cidr'); module.exports = { title: 'Event Grid Domain Public Access', @@ -38,10 +39,38 @@ module.exports = { for (let domain of domains.data) { if (!domain.id) continue; - if (domain.publicNetworkAccess && domain.publicNetworkAccess.toLowerCase() === 'enabled') { - helpers.addResult(results, 2, 'Event Grid domain has public network access enabled', location, domain.id); - } else { + if (domain.publicNetworkAccess && domain.publicNetworkAccess.toLowerCase() === 'disabled') { helpers.addResult(results, 0, 'Event Grid domain does not have public network access enabled', location, domain.id); + } else { + const hasPrivateEndpoint = domain.privateEndpointConnections && + domain.privateEndpointConnections.length > 0 && + domain.privateEndpointConnections.some(conn => + conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + ); + + if (hasPrivateEndpoint) { + helpers.addResult(results, 0, 'Event Grid domain does not have public network access enabled', location, domain.id); + } else { + let hasOpenCidr = false; + + if (domain.inboundIpRules && domain.inboundIpRules.length) { + for (let rule of domain.inboundIpRules) { + if (cidrHelper.isOpenCidrRange(rule.ipMask)) { + hasOpenCidr = true; + break; + } + } + } + + const restricted = domain.inboundIpRules && + domain.inboundIpRules.length > 0 && !hasOpenCidr; + + if (restricted) { + helpers.addResult(results, 0, 'Event Grid domain does not have public network access enabled', location, domain.id); + } else { + helpers.addResult(results, 2, 'Event Grid domain has public network access enabled', location, domain.id); + } + } } } rcb(); diff --git a/plugins/azure/eventGrid/domainPublicAccess.spec.js b/plugins/azure/eventGrid/domainPublicAccess.spec.js index fcd6be09dd..e8086c6eb2 100644 --- a/plugins/azure/eventGrid/domainPublicAccess.spec.js +++ b/plugins/azure/eventGrid/domainPublicAccess.spec.js @@ -21,6 +21,40 @@ const domains = [ "location": "westus2", "name": "exampledomain1", "publicNetworkAccess": "Disabled" + }, + { + "id": "/subscriptions/8f6b6269-84f2-4d09-9e31-1127efcd1e40/resourceGroups/examplerg/providers/Microsoft.EventGrid/domains/exampledomain1", + "location": "westus2", + "name": "exampledomain1", + "publicNetworkAccess": "Enabled", + "privateEndpointConnections": [ + { + "properties": { + "privateLinkServiceConnectionState": { + "status": "Approved" + } + } + } + ] + }, + { + "id": "/subscriptions/8f6b6269-84f2-4d09-9e31-1127efcd1e40/resourceGroups/examplerg/providers/Microsoft.EventGrid/domains/exampledomain1", + "location": "westus2", + "name": "exampledomain1", + "publicNetworkAccess": "Enabled", + "inboundIpRules": [ + { "ipMask": "12.0.0.0", "action": "Allow" } + ] + }, + { + "id": "/subscriptions/8f6b6269-84f2-4d09-9e31-1127efcd1e40/resourceGroups/examplerg/providers/Microsoft.EventGrid/domains/exampledomain1", + "location": "westus2", + "name": "exampledomain1", + "publicNetworkAccess": "Enabled", + "inboundIpRules": [ + { "ipMask": "12.0.0.0", "action": "Allow" }, + { "ipMask": "0.0.0.0", "action": "Allow" } + ] } ] const createCache = (data) => { @@ -80,5 +114,38 @@ describe("domainPublicAccess", function () { done(); }); }); + + it("should give passing result if public access enabled but private endpoint is approved", function (done) { + const cache = createCache([domains[2]]); + domainPublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include("Event Grid domain does not have public network access enabled"); + expect(results[0].region).to.equal("eastus"); + done(); + }); + }); + + it("should give passing result if public access enabled but inbound IP rules are restricted", function (done) { + const cache = createCache([domains[3]]); + domainPublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include("Event Grid domain does not have public network access enabled"); + expect(results[0].region).to.equal("eastus"); + done(); + }); + }); + + it("should give failing result if public access enabled and inbound IP rules contain open CIDR", function (done) { + const cache = createCache([domains[4]]); + domainPublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include("Event Grid domain has public network access enabled"); + expect(results[0].region).to.equal("eastus"); + done(); + }); + }); }); }); From 91d49856bcad53088104ce1c4927b6b27a6fa530 Mon Sep 17 00:00:00 2001 From: Taha Date: Wed, 18 Feb 2026 20:25:02 +0500 Subject: [PATCH 11/19] Fixing ML registry public access --- .../machinelearning/mlRegistryPublicAccess.js | 12 ++++- .../mlRegistryPublicAccess.spec.js | 47 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/plugins/azure/machinelearning/mlRegistryPublicAccess.js b/plugins/azure/machinelearning/mlRegistryPublicAccess.js index db08920738..ad6409cfac 100644 --- a/plugins/azure/machinelearning/mlRegistryPublicAccess.js +++ b/plugins/azure/machinelearning/mlRegistryPublicAccess.js @@ -38,7 +38,17 @@ module.exports = { for (let registry of machineLearningRegistries.data) { if (!registry.id) continue; - if (registry.publicNetworkAccess && registry.publicNetworkAccess.toLowerCase()=='disabled') { + const hasPrivateEndpoint = registry.privateEndpointConnections && + registry.privateEndpointConnections.length > 0; + + const hasSelectedNetworks = registry.networkRuleSet && + registry.networkRuleSet.defaultAction && + registry.networkRuleSet.defaultAction.toLowerCase() === 'deny'; + + if (registry.publicNetworkAccess && registry.publicNetworkAccess.toLowerCase() === 'disabled') { + helpers.addResult(results, 0, + 'Machine Learning registry has public network access disabled', location, registry.id); + } else if (hasPrivateEndpoint || hasSelectedNetworks) { helpers.addResult(results, 0, 'Machine Learning registry has public network access disabled', location, registry.id); } else { diff --git a/plugins/azure/machinelearning/mlRegistryPublicAccess.spec.js b/plugins/azure/machinelearning/mlRegistryPublicAccess.spec.js index a3c21ad047..c35ce9e036 100644 --- a/plugins/azure/machinelearning/mlRegistryPublicAccess.spec.js +++ b/plugins/azure/machinelearning/mlRegistryPublicAccess.spec.js @@ -18,7 +18,30 @@ const registry = [ "type": "Microsoft.MachineLearningServices/registries", "publicNetworkAccess" : "Enabled" }, - + { + "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/registries/test1", + "name": "test", + "type": "Microsoft.MachineLearningServices/registries", + "publicNetworkAccess": "Enabled", + "privateEndpointConnections": [ + { + "properties": { + "privateLinkServiceConnectionState": { + "status": "Approved" + } + } + } + ] + }, + { + "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/registries/test1", + "name": "test", + "type": "Microsoft.MachineLearningServices/registries", + "publicNetworkAccess": "Enabled", + "networkRuleSet": { + "defaultAction": "Deny" + } + } ]; const createCache = (registries) => { @@ -90,5 +113,27 @@ describe('mlRegistryPublicAccess', function() { }); }); + it('should give passing result if Machine Learning registry has public access enabled but private endpoint is approved', function(done) { + const cache = createCache([registry[2]]); + mlRegistryPublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Machine Learning registry has public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Machine Learning registry has public access enabled but selected networks are configured', function(done) { + const cache = createCache([registry[3]]); + mlRegistryPublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Machine Learning registry has public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); }); \ No newline at end of file From 51109f1fb58ffc044e5f142b4ee58525abfced83 Mon Sep 17 00:00:00 2001 From: Taha Date: Wed, 18 Feb 2026 21:07:05 +0500 Subject: [PATCH 12/19] Fixing ML workspace public access --- .../workspacePublicAccessDisabled.js | 36 ++++++++- .../workspacePublicAccessDisabled.spec.js | 75 ++++++++++++++++++- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js index 81ddbbb611..bf779d1941 100644 --- a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js +++ b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js @@ -1,5 +1,6 @@ var async = require('async'); var helpers = require('../../../helpers/azure'); +const cidrHelper = require('../../../helpers/azure/cidr'); module.exports = { title: 'Machine Learning Workspace Public Access Disabled', @@ -38,12 +39,41 @@ module.exports = { for (let workspace of machineLearningWorkspaces.data) { if (!workspace.id) continue; - if (workspace.publicNetworkAccess && workspace.publicNetworkAccess.toLowerCase()=='disabled') { + if (workspace.publicNetworkAccess && workspace.publicNetworkAccess.toLowerCase() === 'disabled') { helpers.addResult(results, 0, 'Machine Learning workspace has public network access disabled', location, workspace.id); } else { - helpers.addResult(results, 2, - 'Machine Learning workspace has public network access enabled', location, workspace.id); + const hasPrivateEndpoint = workspace.privateEndpointConnections && + workspace.privateEndpointConnections.length > 0 && + workspace.privateEndpointConnections.some(conn => + conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + ); + + const hasIpRules = workspace.networkAcls && workspace.networkAcls.ipRules && + workspace.networkAcls.ipRules.length > 0; + let hasOpenCidr = false; + + if (hasIpRules) { + for (let rule of workspace.networkAcls.ipRules) { + if (cidrHelper.isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { + hasOpenCidr = true; + break; + } + } + } + + const restricted = workspace.networkAcls && + workspace.networkAcls.defaultAction && + workspace.networkAcls.defaultAction.toLowerCase() === 'deny' && + !hasOpenCidr; + + if (hasPrivateEndpoint || restricted) { + helpers.addResult(results, 0, + 'Machine Learning workspace has public network access disabled', location, workspace.id); + } else { + helpers.addResult(results, 2, + 'Machine Learning workspace has public network access enabled', location, workspace.id); + } } } diff --git a/plugins/azure/machinelearning/workspacePublicAccessDisabled.spec.js b/plugins/azure/machinelearning/workspacePublicAccessDisabled.spec.js index 1108bff0d8..d185a2cd4a 100644 --- a/plugins/azure/machinelearning/workspacePublicAccessDisabled.spec.js +++ b/plugins/azure/machinelearning/workspacePublicAccessDisabled.spec.js @@ -19,9 +19,47 @@ const workspaces = [ "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/workspaces/test1", "name": "test", "type": "Microsoft.MachineLearningServices/workspaces", - "publicNetworkAccess" : "Enabled" + "publicNetworkAccess" : "Enabled", + "networkAcls": { + "defaultAction": "Allow", + "ipRules": [] + } + }, + { + "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/workspaces/test1", + "name": "test", + "type": "Microsoft.MachineLearningServices/workspaces", + "publicNetworkAccess": "Enabled", + "privateEndpointConnections": [ + { + "properties": { + "privateLinkServiceConnectionState": { + "status": "Approved" + } + } + } + ] }, - + { + "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/workspaces/test1", + "name": "test", + "type": "Microsoft.MachineLearningServices/workspaces", + "publicNetworkAccess": "Enabled", + "networkAcls": { + "defaultAction": "Deny", + "ipRules": [{"value": "123.2.21.1/32"}, {"value": "103.177.240.106/32"}] + } + }, + { + "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/workspaces/test1", + "name": "test", + "type": "Microsoft.MachineLearningServices/workspaces", + "publicNetworkAccess": "Enabled", + "networkAcls": { + "defaultAction": "Deny", + "ipRules": [{"value": "0.0.0.0/0"}] + } + } ]; const createCache = (workspaces) => { @@ -93,5 +131,38 @@ describe('workspacePublicAccessDisabled', function() { }); }); + it('should give passing result if Machine Learning workspace has public access enabled but private endpoint is approved', function(done) { + const cache = createCache([workspaces[2]]); + workspacePublicAccessDisabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Machine Learning workspace has public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Machine Learning workspace has selected networks with restricted IP ranges', function(done) { + const cache = createCache([workspaces[3]]); + workspacePublicAccessDisabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Machine Learning workspace has public network access disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Machine Learning workspace has selected networks but IP rules contain open CIDR', function(done) { + const cache = createCache([workspaces[4]]); + workspacePublicAccessDisabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Machine Learning workspace has public network access enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); }); \ No newline at end of file From 58c87a7193ba6e8a5aaed481bddc3006da171582 Mon Sep 17 00:00:00 2001 From: Taha Date: Thu, 19 Feb 2026 12:52:44 +0500 Subject: [PATCH 13/19] Fixing ML workspace public access issues --- .../workspacePublicAccessDisabled.js | 14 ++----------- .../workspacePublicAccessDisabled.spec.js | 21 ------------------- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js index bf779d1941..444da9e282 100644 --- a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js +++ b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js @@ -50,22 +50,12 @@ module.exports = { ); const hasIpRules = workspace.networkAcls && workspace.networkAcls.ipRules && - workspace.networkAcls.ipRules.length > 0; - let hasOpenCidr = false; - - if (hasIpRules) { - for (let rule of workspace.networkAcls.ipRules) { - if (cidrHelper.isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { - hasOpenCidr = true; - break; - } - } - } + workspace.networkAcls.ipRules.length > 0; const restricted = workspace.networkAcls && workspace.networkAcls.defaultAction && workspace.networkAcls.defaultAction.toLowerCase() === 'deny' && - !hasOpenCidr; + hasIpRules; if (hasPrivateEndpoint || restricted) { helpers.addResult(results, 0, diff --git a/plugins/azure/machinelearning/workspacePublicAccessDisabled.spec.js b/plugins/azure/machinelearning/workspacePublicAccessDisabled.spec.js index d185a2cd4a..8d1be83016 100644 --- a/plugins/azure/machinelearning/workspacePublicAccessDisabled.spec.js +++ b/plugins/azure/machinelearning/workspacePublicAccessDisabled.spec.js @@ -50,16 +50,6 @@ const workspaces = [ "ipRules": [{"value": "123.2.21.1/32"}, {"value": "103.177.240.106/32"}] } }, - { - "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/workspaces/test1", - "name": "test", - "type": "Microsoft.MachineLearningServices/workspaces", - "publicNetworkAccess": "Enabled", - "networkAcls": { - "defaultAction": "Deny", - "ipRules": [{"value": "0.0.0.0/0"}] - } - } ]; const createCache = (workspaces) => { @@ -153,16 +143,5 @@ describe('workspacePublicAccessDisabled', function() { }); }); - it('should give failing result if Machine Learning workspace has selected networks but IP rules contain open CIDR', function(done) { - const cache = createCache([workspaces[4]]); - workspacePublicAccessDisabled.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(2); - expect(results[0].message).to.include('Machine Learning workspace has public network access enabled'); - expect(results[0].region).to.equal('eastus'); - done(); - }); - }); - }); }); \ No newline at end of file From ec52f7daf93838d0e2cc66aa196b56f92944239a Mon Sep 17 00:00:00 2001 From: Taha Date: Thu, 19 Feb 2026 15:27:57 +0500 Subject: [PATCH 14/19] Fixed Azure openai account public access and fixed some other issues --- helpers/azure/cidr.js | 10 ---- helpers/azure/functions.js | 14 ++++- .../appservice/appServicePublicAccess.js | 2 +- plugins/azure/eventGrid/domainPublicAccess.js | 2 +- .../workspacePublicAccessDisabled.js | 1 - .../openai/accountPublicAccessDisabled.js | 29 ++++++++-- .../accountPublicAccessDisabled.spec.js | 55 +++++++++++++++++-- .../storageAccountPublicNetworkAccess.js | 4 +- 8 files changed, 89 insertions(+), 28 deletions(-) delete mode 100644 helpers/azure/cidr.js diff --git a/helpers/azure/cidr.js b/helpers/azure/cidr.js deleted file mode 100644 index 4262d736c9..0000000000 --- a/helpers/azure/cidr.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - isOpenCidrRange: function(cidr) { - if (!cidr || typeof cidr !== 'string') return false; - - const trimmed = cidr.trim(); - return trimmed === '0.0.0.0/0' || - trimmed === '::/0' || - trimmed === '0.0.0.0'; - }, -}; \ No newline at end of file diff --git a/helpers/azure/functions.js b/helpers/azure/functions.js index d52565a212..f7216a027d 100644 --- a/helpers/azure/functions.js +++ b/helpers/azure/functions.js @@ -855,6 +855,16 @@ function checkNetworkExposure(cache, source, networkInterfaces, securityGroups, return exposedPath; } + +function isOpenCidrRange(cidr) { + if (!cidr || typeof cidr !== 'string') return false; + + const trimmed = cidr.trim(); + return trimmed === '0.0.0.0/0' || + trimmed === '::/0' || + trimmed === '0.0.0.0'; +} + module.exports = { addResult: addResult, findOpenPorts: findOpenPorts, @@ -868,7 +878,7 @@ module.exports = { remediateOpenPortsHelper: remediateOpenPortsHelper, checkMicrosoftDefender: checkMicrosoftDefender, checkFlexibleServerConfigs:checkFlexibleServerConfigs, - checkNetworkExposure: checkNetworkExposure - + checkNetworkExposure: checkNetworkExposure, + isOpenCidrRange: isOpenCidrRange }; diff --git a/plugins/azure/appservice/appServicePublicAccess.js b/plugins/azure/appservice/appServicePublicAccess.js index a4d3170a32..fe707bf6ab 100644 --- a/plugins/azure/appservice/appServicePublicAccess.js +++ b/plugins/azure/appservice/appServicePublicAccess.js @@ -1,6 +1,6 @@ var async = require('async'); var helpers = require('../../../helpers/azure'); -const cidrHelper = require('../../../helpers/azure/cidr'); +var cidrHelper = require('../../../helpers/azure/functions'); module.exports = { title: 'App Service Public Network Access Disabled', diff --git a/plugins/azure/eventGrid/domainPublicAccess.js b/plugins/azure/eventGrid/domainPublicAccess.js index 7d4cf772ab..a1542f422c 100644 --- a/plugins/azure/eventGrid/domainPublicAccess.js +++ b/plugins/azure/eventGrid/domainPublicAccess.js @@ -1,6 +1,6 @@ const async = require('async'); const helpers = require('../../../helpers/azure'); -const cidrHelper = require('../../../helpers/azure/cidr'); +var cidrHelper = require('../../../helpers/azure/functions'); module.exports = { title: 'Event Grid Domain Public Access', diff --git a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js index 444da9e282..abbff34552 100644 --- a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js +++ b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js @@ -1,6 +1,5 @@ var async = require('async'); var helpers = require('../../../helpers/azure'); -const cidrHelper = require('../../../helpers/azure/cidr'); module.exports = { title: 'Machine Learning Workspace Public Access Disabled', diff --git a/plugins/azure/openai/accountPublicAccessDisabled.js b/plugins/azure/openai/accountPublicAccessDisabled.js index e5ab13c2c5..09ca23e229 100644 --- a/plugins/azure/openai/accountPublicAccessDisabled.js +++ b/plugins/azure/openai/accountPublicAccessDisabled.js @@ -37,14 +37,31 @@ module.exports = { for (let account of accounts.data) { - if (account.properties && - account.properties.publicNetworkAccess && - account.properties.publicNetworkAccess.toLowerCase() == 'enabled') { - helpers.addResult(results, 2, - 'OpenAI Account is publicly accessible', location, account.id); - } else { + if (!account.id) continue; + + const publicAccess = account.publicNetworkAccess && account.publicNetworkAccess.toLowerCase(); + + if (publicAccess === 'disabled') { helpers.addResult(results, 0, 'OpenAI Account is not publicly accessible', location, account.id); + } else { + const hasPrivateEndpoint = account.privateEndpointConnections && + account.privateEndpointConnections.length > 0 && + account.privateEndpointConnections.some(conn => + conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + ); + + const restricted = account.networkAcls && + account.networkAcls.defaultAction && + account.networkAcls.defaultAction.toLowerCase() === 'deny'; + + if (hasPrivateEndpoint || restricted) { + helpers.addResult(results, 0, + 'OpenAI Account is not publicly accessible', location, account.id); + } else { + helpers.addResult(results, 2, + 'OpenAI Account is publicly accessible', location, account.id); + } } } diff --git a/plugins/azure/openai/accountPublicAccessDisabled.spec.js b/plugins/azure/openai/accountPublicAccessDisabled.spec.js index 28c74f3ea2..836869f0da 100644 --- a/plugins/azure/openai/accountPublicAccessDisabled.spec.js +++ b/plugins/azure/openai/accountPublicAccessDisabled.spec.js @@ -7,17 +7,40 @@ const accounts = [ "name": "acc1", "type": "Microsoft.CognitiveServices/accounts", "location": "eastus", - "properties": { - "publicNetworkAccess": 'Disabled' - } + "publicNetworkAccess": 'Disabled' }, { "id": "/subscriptions/12424/resourceGroups/bvttest/providers/Microsoft.CognitiveServices/accounts/acc2", "name": "acc2", "type": "Microsoft.CognitiveServices/accounts", "location": "eastus", - "properties": { - "publicNetworkAccess": 'Enabled' + "publicNetworkAccess": 'Enabled' + }, + { + "id": "/subscriptions/12424/resourceGroups/bvttest/providers/Microsoft.CognitiveServices/accounts/acc3", + "name": "acc3", + "type": "Microsoft.CognitiveServices/accounts", + "location": "eastus", + "publicNetworkAccess": 'Enabled', + "privateEndpointConnections": [ + { + "properties": { + "privateLinkServiceConnectionState": { + "status": "Approved" + } + } + } + ] + }, + { + "id": "/subscriptions/12424/resourceGroups/bvttest/providers/Microsoft.CognitiveServices/accounts/acc4", + "name": "acc4", + "type": "Microsoft.CognitiveServices/accounts", + "location": "eastus", + "publicNetworkAccess": 'Enabled', + "networkAcls": { + "defaultAction": "Deny", + "ipRules": [{"value": "192.168.1.0/24"}] } }, @@ -93,5 +116,27 @@ describe('accountPublicAccessDisabled', function() { }); }); + it('should give passing result if openai account has approved private endpoint', function(done) { + const cache = createCache([accounts[2]]); + accountPublicAccessDisabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('OpenAI Account is not publicly accessible'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if openai account has selected networks', function(done) { + const cache = createCache([accounts[3]]); + accountPublicAccessDisabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('OpenAI Account is not publicly accessible'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); }); \ No newline at end of file diff --git a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js index 604bfd0ef4..aceef9a622 100644 --- a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js +++ b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js @@ -1,6 +1,6 @@ var async = require('async'); var helpers = require('../../../helpers/azure/'); -var cidr = require('../../../helpers/azure/cidr'); +var cidrHelper = require('../../../helpers/azure/functions'); module.exports = { title: 'Storage Account Public Network Access', @@ -48,7 +48,7 @@ module.exports = { if (hasIpRules) { for (let rule of account.networkAcls.ipRules) { - if (cidr.isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { + if (cidrHelper.isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { hasOpenCidr = true; break; } From 5c9c7ecaaa9a83075d17d8bd25a0ab786b1f0965 Mon Sep 17 00:00:00 2001 From: Taha Date: Fri, 20 Feb 2026 15:14:17 +0500 Subject: [PATCH 15/19] Fixed Namespace Public Access --- helpers/azure/api.js | 9 +- helpers/azure/resources.js | 3 +- .../azure/servicebus/namespacePublicAccess.js | 44 +++++- .../servicebus/namespacePublicAccess.spec.js | 135 +++++++++++++++--- 4 files changed, 166 insertions(+), 25 deletions(-) diff --git a/helpers/azure/api.js b/helpers/azure/api.js index 2f71d06782..ec4affa724 100644 --- a/helpers/azure/api.js +++ b/helpers/azure/api.js @@ -627,7 +627,7 @@ var calls = { }, serviceBus: { listNamespacesBySubscription: { - url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.ServiceBus/namespaces?api-version=2022-10-01-preview' + url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.ServiceBus/namespaces?api-version=2025-05-01-preview' } }, mediaServices:{ @@ -1270,6 +1270,13 @@ var postcalls = { properties: ['id'], url: 'https://management.azure.com/{id}/networkRuleSets/default?api-version=2022-10-01-preview' } + }, + serviceBus: { + getNamespaceNetworkRuleSet: { + reliesOnPath: 'serviceBus.listNamespacesBySubscription', + properties: ['id'], + url: 'https://management.azure.com/{id}/networkRuleSets/default?api-version=2021-11-01' + } } }; diff --git a/helpers/azure/resources.js b/helpers/azure/resources.js index 7c05970efa..f49b4c053f 100644 --- a/helpers/azure/resources.js +++ b/helpers/azure/resources.js @@ -269,7 +269,8 @@ module.exports = { list: 'id' }, serviceBus:{ - listNamespacesBySubscription: 'id' + listNamespacesBySubscription: 'id', + getNamespaceNetworkRuleSet: 'id' }, flexibleServersConfigurations:{ listByPostgresServer: 'id' diff --git a/plugins/azure/servicebus/namespacePublicAccess.js b/plugins/azure/servicebus/namespacePublicAccess.js index dcf2105919..26b750a28e 100644 --- a/plugins/azure/servicebus/namespacePublicAccess.js +++ b/plugins/azure/servicebus/namespacePublicAccess.js @@ -1,5 +1,6 @@ var async = require('async'); var helpers = require('../../../helpers/azure'); +var cidrHelper = require('../../../helpers/azure/functions'); module.exports = { title: 'Namespace Public Access', @@ -10,7 +11,7 @@ module.exports = { more_info: 'Using private endpoints for Azure Service Bus namespace improve security by enabling private network access, encrypting communication, and enhancing performance. They seamlessly integrate with virtual networks, ensuring compliance and suitability for hybrid cloud scenarios.', recommended_action: 'Ensure that Azure Service Bus namespaces are only accessible through private endpoints.', link: 'https://learn.microsoft.com/en-us/azure/service-bus-messaging/private-link-service', - apis: ['serviceBus:listNamespacesBySubscription'], + apis: ['serviceBus:listNamespacesBySubscription', 'serviceBus:getNamespaceNetworkRuleSet'], realtime_triggers: ['microsoftservicebus:namespaces:write','microsoftservicebus:namespaces:delete'], run: function(cache, settings, callback) { @@ -36,15 +37,46 @@ module.exports = { } for (let namespace of namespaces.data) { - if (namespace.sku && namespace.sku.tier && namespace.sku.tier.toLowerCase() !== 'premium') { - helpers.addResult(results, 0, 'Service Bus Namespace is not a premium namespace', location, namespace.id); - } else if (namespace.publicNetworkAccess && namespace.publicNetworkAccess.toLowerCase() === 'enabled') { - helpers.addResult(results, 2, 'Service bus namespace is publicly accessible', location, namespace.id); + + const networkRules = helpers.addSource(cache, source, + ['serviceBus', 'getNamespaceNetworkRuleSet', location, namespace.id]); + + if (networkRules && networkRules.err) { + helpers.addResult(results, 3, 'Unable to query network rules for namespace: ' + helpers.addError(networkRules), location, namespace.id); + continue; + } + + if (namespace.publicNetworkAccess && namespace.publicNetworkAccess.toLowerCase() === 'enabled') { + + if (namespace.sku && namespace.sku.tier && namespace.sku.tier.toLowerCase() === 'premium' && + namespace.privateEndpointConnections && namespace.privateEndpointConnections.length > 0 && + namespace.privateEndpointConnections.some(conn => + conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + )) { + helpers.addResult(results, 0, 'Service bus namespace is only accessible through private endpoints', location, namespace.id); + } else { + let hasOpenCidr = false; + let hasIpRules = networkRules && networkRules.data && networkRules.data.ipRules && networkRules.data.ipRules.length > 0; + + if (hasIpRules) { + for (let rule of networkRules.data.ipRules) { + if (cidrHelper.isOpenCidrRange(rule.ipMask || rule.ipAddressOrRange)) { + hasOpenCidr = true; + break; + } + } + } + + if (hasIpRules && !hasOpenCidr) { + helpers.addResult(results, 0, 'Service bus namespace is only accessible through private endpoints', location, namespace.id); + } else { + helpers.addResult(results, 2, 'Service bus namespace is publicly accessible', location, namespace.id); + } + } } else { helpers.addResult(results, 0, 'Service bus namespace is only accessible through private endpoints', location, namespace.id); } } - rcb(); }, function() { // Global checking goes here diff --git a/plugins/azure/servicebus/namespacePublicAccess.spec.js b/plugins/azure/servicebus/namespacePublicAccess.spec.js index 4caed1c4e8..e071ea441e 100644 --- a/plugins/azure/servicebus/namespacePublicAccess.spec.js +++ b/plugins/azure/servicebus/namespacePublicAccess.spec.js @@ -22,16 +22,12 @@ const namespaces = [ publicNetworkAccess: 'Disabled', disableLocalAuth: true, provisioningState: 'Succeeded', - status: 'Active', - encryption: { - keySource: 'Microsoft.KeyVault', - requireInfrastructureEncryption: false - }, + status: 'Active' }, { sku: { name: 'Basic', tier: 'Basic' }, id: '/subscriptions/234/myrg/providers/Microsoft.ServiceBus/namespaces/test3', - name: 'test2', + name: 'test3', type: 'Microsoft.ServiceBus/Namespaces', location: 'East US', publicNetworkAccess: 'Enabled', @@ -39,21 +35,81 @@ const namespaces = [ provisioningState: 'Succeeded', status: 'Active' }, + { + sku: { name: 'Premium', tier: 'Premium', capacity: 1 }, + id: '/subscriptions/234/myrg/providers/Microsoft.ServiceBus/namespaces/test4', + name: 'test4', + type: 'Microsoft.ServiceBus/Namespaces', + location: 'East US', + publicNetworkAccess: 'Enabled', + disableLocalAuth: false, + provisioningState: 'Succeeded', + status: 'Active', + privateEndpointConnections: [ + { + id: '/subscriptions/234/myrg/providers/Microsoft.ServiceBus/namespaces/test4/privateEndpointConnections/test-pec', + name: 'test-pec', + properties: { + privateLinkServiceConnectionState: { + status: 'Approved' + } + } + } + ] + }, + { + sku: { name: 'Premium', tier: 'Premium', capacity: 1 }, + id: '/subscriptions/234/myrg/providers/Microsoft.ServiceBus/namespaces/test5', + name: 'test5', + type: 'Microsoft.ServiceBus/Namespaces', + location: 'East US', + publicNetworkAccess: 'Enabled', + disableLocalAuth: false, + provisioningState: 'Succeeded', + status: 'Active' + } ]; +const networkRules = { + restricted: { + data: { + ipRules: [{ ipMask: '10.0.0.1/32' }] + } + }, + openCidr: { + data: { + ipRules: [{ ipMask: '0.0.0.0/0' }] + } + }, + empty: { + data: { + ipRules: [] + } + } +}; -const createCache = (namespaces, err) => { - - return { +const createCache = (namespaces, err, networkRulesData = null) => { + const cache = { serviceBus: { listNamespacesBySubscription: { 'eastus': { data: namespaces, err: err } + }, + getNamespaceNetworkRuleSet: { + 'eastus': {} } } }; + + if (namespaces && namespaces.length > 0) { + namespaces.forEach((ns) => { + cache.serviceBus.getNamespaceNetworkRuleSet['eastus'][ns.id] = networkRulesData || networkRules.empty; + }); + } + + return cache; }; describe('namespacePublicAccess', function () { @@ -81,20 +137,52 @@ describe('namespacePublicAccess', function () { }); }); - - it('should give passing result if namespace is not using premium tier', function (done) { + it('should give passing result if namespace is not publicly accessible', function (done) { + const cache = createCache([namespaces[1]], null); + namespacePublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Service bus namespace is only accessible through private endpoints'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if namespace is publicly accessible without protection', function (done) { + const cache = createCache([namespaces[0]], null); + namespacePublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Service bus namespace is publicly accessible'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if basic tier namespace is publicly accessible', function (done) { const cache = createCache([namespaces[2]], null); + namespacePublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Service bus namespace is publicly accessible'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if premium namespace has approved private endpoints', function (done) { + const cache = createCache([namespaces[3]], null); namespacePublicAccess.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('Service Bus Namespace is not a premium namespace'); + expect(results[0].message).to.include('Service bus namespace is only accessible through private endpoints'); expect(results[0].region).to.equal('eastus'); done(); }); }); - it('should give passing result if namespace is not publicly accessible', function (done) { - const cache = createCache([namespaces[1]], null); + it('should give passing result if namespace has public access enabled with restricted IP rules', function (done) { + const cache = createCache([namespaces[4]], null, networkRules.restricted); namespacePublicAccess.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -104,8 +192,8 @@ describe('namespacePublicAccess', function () { }); }); - it('should give failing result if namespace is publicly accessible', function (done) { - const cache = createCache([namespaces[0]], null); + it('should give failing result if namespace has public access enabled with open CIDR 0.0.0.0/0', function (done) { + const cache = createCache([namespaces[4]], null, networkRules.openCidr); namespacePublicAccess.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); @@ -114,5 +202,18 @@ describe('namespacePublicAccess', function () { done(); }); }); + + it('should give unknown result if unable to query network rules for namespace', function (done) { + const cache = createCache([namespaces[0]], null); + cache.serviceBus.getNamespaceNetworkRuleSet['eastus'][namespaces[0].id] = { err: 'error fetching network rules' }; + namespacePublicAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + expect(results[0].message).to.include('Unable to query network rules for namespace'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); }); -}); \ No newline at end of file +}); + From c83fb83e010f9fea3ab772feffa26c1c93e4c4fa Mon Sep 17 00:00:00 2001 From: Taha Date: Fri, 20 Feb 2026 15:55:00 +0500 Subject: [PATCH 16/19] Fixed CIDR helper call --- helpers/azure/resources.js | 1 - plugins/azure/appservice/appServicePublicAccess.js | 3 +-- plugins/azure/eventGrid/domainPublicAccess.js | 3 +-- .../azure/storageaccounts/storageAccountPublicNetworkAccess.js | 3 +-- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/helpers/azure/resources.js b/helpers/azure/resources.js index f49b4c053f..2f24f593a9 100644 --- a/helpers/azure/resources.js +++ b/helpers/azure/resources.js @@ -270,7 +270,6 @@ module.exports = { }, serviceBus:{ listNamespacesBySubscription: 'id', - getNamespaceNetworkRuleSet: 'id' }, flexibleServersConfigurations:{ listByPostgresServer: 'id' diff --git a/plugins/azure/appservice/appServicePublicAccess.js b/plugins/azure/appservice/appServicePublicAccess.js index fe707bf6ab..758b085912 100644 --- a/plugins/azure/appservice/appServicePublicAccess.js +++ b/plugins/azure/appservice/appServicePublicAccess.js @@ -1,6 +1,5 @@ var async = require('async'); var helpers = require('../../../helpers/azure'); -var cidrHelper = require('../../../helpers/azure/functions'); module.exports = { title: 'App Service Public Network Access Disabled', @@ -64,7 +63,7 @@ module.exports = { if (config.ipSecurityRestrictions && config.ipSecurityRestrictions.length) { for (let rule of config.ipSecurityRestrictions) { - if (cidrHelper.isOpenCidrRange(rule.ipAddress)) { + if (helpers.isOpenCidrRange(rule.ipAddress)) { hasOpenCidr = true; break; } diff --git a/plugins/azure/eventGrid/domainPublicAccess.js b/plugins/azure/eventGrid/domainPublicAccess.js index a1542f422c..e9959583bb 100644 --- a/plugins/azure/eventGrid/domainPublicAccess.js +++ b/plugins/azure/eventGrid/domainPublicAccess.js @@ -1,6 +1,5 @@ const async = require('async'); const helpers = require('../../../helpers/azure'); -var cidrHelper = require('../../../helpers/azure/functions'); module.exports = { title: 'Event Grid Domain Public Access', @@ -55,7 +54,7 @@ module.exports = { if (domain.inboundIpRules && domain.inboundIpRules.length) { for (let rule of domain.inboundIpRules) { - if (cidrHelper.isOpenCidrRange(rule.ipMask)) { + if (helpers.isOpenCidrRange(rule.ipMask)) { hasOpenCidr = true; break; } diff --git a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js index aceef9a622..fbb6c97ce6 100644 --- a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js +++ b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js @@ -1,6 +1,5 @@ var async = require('async'); var helpers = require('../../../helpers/azure/'); -var cidrHelper = require('../../../helpers/azure/functions'); module.exports = { title: 'Storage Account Public Network Access', @@ -48,7 +47,7 @@ module.exports = { if (hasIpRules) { for (let rule of account.networkAcls.ipRules) { - if (cidrHelper.isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { + if (helpers.isOpenCidrRange(rule.value || rule.ipAddressOrRange)) { hasOpenCidr = true; break; } From d79581faf20fd08f8fd2b6319acf4f3694c6530a Mon Sep 17 00:00:00 2001 From: Taha Date: Fri, 20 Feb 2026 16:24:51 +0500 Subject: [PATCH 17/19] Fixed some issues --- plugins/azure/appservice/appServicePublicAccess.js | 3 +-- plugins/azure/automationAccounts/automationAcctPublicAccess.js | 2 +- plugins/azure/batchAccounts/batchAccountsPublicAccess.js | 2 +- plugins/azure/eventGrid/domainPublicAccess.js | 2 +- plugins/azure/machinelearning/workspacePublicAccessDisabled.js | 2 +- plugins/azure/openai/accountPublicAccessDisabled.js | 2 +- plugins/azure/servicebus/namespacePublicAccess.js | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/azure/appservice/appServicePublicAccess.js b/plugins/azure/appservice/appServicePublicAccess.js index 758b085912..cc827f4849 100644 --- a/plugins/azure/appservice/appServicePublicAccess.js +++ b/plugins/azure/appservice/appServicePublicAccess.js @@ -57,8 +57,7 @@ module.exports = { 'App Service has public network access disabled', location, webApp.id); return; - } - else { + } else { let hasOpenCidr = false; if (config.ipSecurityRestrictions && config.ipSecurityRestrictions.length) { diff --git a/plugins/azure/automationAccounts/automationAcctPublicAccess.js b/plugins/azure/automationAccounts/automationAcctPublicAccess.js index 04bb628383..e47047e966 100644 --- a/plugins/azure/automationAccounts/automationAcctPublicAccess.js +++ b/plugins/azure/automationAccounts/automationAcctPublicAccess.js @@ -50,7 +50,7 @@ module.exports = { const hasActivePrivateEndpoint = describeAcct.data.privateEndpointConnections && describeAcct.data.privateEndpointConnections.length > 0 && describeAcct.data.privateEndpointConnections.some(conn => - conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + conn.properties && conn.properties.privateLinkServiceConnectionState && conn.properties.privateLinkServiceConnectionState.status === 'Approved' ); if (describeAcct.data.publicNetworkAccess && !hasActivePrivateEndpoint) { helpers.addResult(results, 2, 'Automation account does not have public network access disabled', location, account.id); diff --git a/plugins/azure/batchAccounts/batchAccountsPublicAccess.js b/plugins/azure/batchAccounts/batchAccountsPublicAccess.js index 3c427818e6..e1beac8c4b 100644 --- a/plugins/azure/batchAccounts/batchAccountsPublicAccess.js +++ b/plugins/azure/batchAccounts/batchAccountsPublicAccess.js @@ -40,7 +40,7 @@ module.exports = { const hasActivePrivateEndpoint = batchAccount.privateEndpointConnections && batchAccount.privateEndpointConnections.length > 0 && batchAccount.privateEndpointConnections.some(conn => - conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + conn.properties && conn.properties.privateLinkServiceConnectionState && conn.properties.privateLinkServiceConnectionState.status === 'Approved' ); if ((batchAccount.publicNetworkAccess && batchAccount.publicNetworkAccess.toLowerCase() === 'enabled') && !hasActivePrivateEndpoint) { diff --git a/plugins/azure/eventGrid/domainPublicAccess.js b/plugins/azure/eventGrid/domainPublicAccess.js index e9959583bb..c0ab36ea37 100644 --- a/plugins/azure/eventGrid/domainPublicAccess.js +++ b/plugins/azure/eventGrid/domainPublicAccess.js @@ -44,7 +44,7 @@ module.exports = { const hasPrivateEndpoint = domain.privateEndpointConnections && domain.privateEndpointConnections.length > 0 && domain.privateEndpointConnections.some(conn => - conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + conn.properties && conn.properties.privateLinkServiceConnectionState && conn.properties.privateLinkServiceConnectionState.status === 'Approved' ); if (hasPrivateEndpoint) { diff --git a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js index abbff34552..515853a235 100644 --- a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js +++ b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js @@ -45,7 +45,7 @@ module.exports = { const hasPrivateEndpoint = workspace.privateEndpointConnections && workspace.privateEndpointConnections.length > 0 && workspace.privateEndpointConnections.some(conn => - conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + conn.properties && conn.properties.privateLinkServiceConnectionState && conn.properties.privateLinkServiceConnectionState.status === 'Approved' ); const hasIpRules = workspace.networkAcls && workspace.networkAcls.ipRules && diff --git a/plugins/azure/openai/accountPublicAccessDisabled.js b/plugins/azure/openai/accountPublicAccessDisabled.js index 09ca23e229..b7f8ad7649 100644 --- a/plugins/azure/openai/accountPublicAccessDisabled.js +++ b/plugins/azure/openai/accountPublicAccessDisabled.js @@ -48,7 +48,7 @@ module.exports = { const hasPrivateEndpoint = account.privateEndpointConnections && account.privateEndpointConnections.length > 0 && account.privateEndpointConnections.some(conn => - conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + conn.properties && conn.properties.privateLinkServiceConnectionState && conn.properties.privateLinkServiceConnectionState.status === 'Approved' ); const restricted = account.networkAcls && diff --git a/plugins/azure/servicebus/namespacePublicAccess.js b/plugins/azure/servicebus/namespacePublicAccess.js index 26b750a28e..2cdb1f7714 100644 --- a/plugins/azure/servicebus/namespacePublicAccess.js +++ b/plugins/azure/servicebus/namespacePublicAccess.js @@ -51,7 +51,7 @@ module.exports = { if (namespace.sku && namespace.sku.tier && namespace.sku.tier.toLowerCase() === 'premium' && namespace.privateEndpointConnections && namespace.privateEndpointConnections.length > 0 && namespace.privateEndpointConnections.some(conn => - conn.properties?.privateLinkServiceConnectionState?.status === 'Approved' + conn.properties && conn.properties.privateLinkServiceConnectionState && conn.properties.privateLinkServiceConnectionState.status === 'Approved' )) { helpers.addResult(results, 0, 'Service bus namespace is only accessible through private endpoints', location, namespace.id); } else { From 49b5ee9ad5ec54a18f135bc8e5d56428efd191bc Mon Sep 17 00:00:00 2001 From: Taha Date: Fri, 20 Feb 2026 16:28:27 +0500 Subject: [PATCH 18/19] Fixed some issues --- .../azure/storageaccounts/storageAccountPublicNetworkAccess.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js index fbb6c97ce6..c034e0d63b 100644 --- a/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js +++ b/plugins/azure/storageaccounts/storageAccountPublicNetworkAccess.js @@ -58,8 +58,7 @@ module.exports = { if ( restricted && !hasOpenCidr) { helpers.addResult(results, 0, 'Storage account has public network access disabled', location, account.id); - } - else { + } else { helpers.addResult(results, 2, 'Storage account has public network access enabled for all networks', location, account.id); } } From 99059c7f5a50929bd80481c5fcf9529e4764b276 Mon Sep 17 00:00:00 2001 From: alphadev4 <113519745+alphadev4@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:32:28 +0000 Subject: [PATCH 19/19] Apply suggestion from @alphadev4 --- helpers/azure/resources.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/azure/resources.js b/helpers/azure/resources.js index 2f24f593a9..7c05970efa 100644 --- a/helpers/azure/resources.js +++ b/helpers/azure/resources.js @@ -269,7 +269,7 @@ module.exports = { list: 'id' }, serviceBus:{ - listNamespacesBySubscription: 'id', + listNamespacesBySubscription: 'id' }, flexibleServersConfigurations:{ listByPostgresServer: 'id'