From 117bcd794567313e3b4bfd542d763420dd330a88 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Mon, 4 Nov 2024 19:29:54 +0500 Subject: [PATCH 1/3] TDE Protectors Encrypted Plugin Update --- .../azure/sqlserver/tdeProtectorEncrypted.js | 153 +++++++++----- .../sqlserver/tdeProtectorEncrypted.spec.js | 190 +++++++++--------- 2 files changed, 190 insertions(+), 153 deletions(-) diff --git a/plugins/azure/sqlserver/tdeProtectorEncrypted.js b/plugins/azure/sqlserver/tdeProtectorEncrypted.js index 1c1f6c324d..e8047a0a4b 100644 --- a/plugins/azure/sqlserver/tdeProtectorEncrypted.js +++ b/plugins/azure/sqlserver/tdeProtectorEncrypted.js @@ -8,82 +8,125 @@ module.exports = { severity: 'Medium', description: 'Ensures SQL Server TDE protector is encrypted with BYOK (Bring Your Own Key)', more_info: 'Enabling BYOK in the TDE protector allows for greater control and transparency, as well as increasing security by having full control of the encryption keys.', - recommended_action: 'Ensure that a BYOK key is set for the Transparent Data Encryption of each SQL Server.', + recommended_action: 'Ensure that a BYOK key is set for the Transparent Data Encryption of each SQL Server or Managed Instance.', link: 'https://learn.microsoft.com/en-us/azure/sql-database/transparent-data-encryption-byok-azure-sql', - apis: ['servers:listSql', 'encryptionProtectors:listByServer'], + apis: ['servers:listSql', 'encryptionProtectors:listByServer', 'managedInstances:list', 'managedInstanceEncryptionProtectors:listByInstance'], settings: { sql_tde_protector_encryption_key: { name: 'SQL Server TDE Protector Encryption Key Type', - description: 'Desired encryption key for SQL Server transparent data encryption; default=service-managed key, cmk=customer-managed key', + description: 'Desired encryption key for SQL Server and Managed Instance transparent data encryption; default=service-managed key, cmk=customer-managed key', regex: '(default|byok)', default: 'byok' } }, - realtime_triggers: ['microsoftsql:servers:write', 'microsoftsql:servers:delete', 'microsodtsql:servers:encryptionprotector:write'], + realtime_triggers: ['microsoftsql:servers:write', 'microsoftsql:servers:delete', 'microsoftsql:servers:encryptionprotector:write'], run: function(cache, settings, callback) { const results = []; const source = {}; const locations = helpers.locations(settings.govcloud); - + var config = { sql_tde_protector_encryption_key: settings.sql_tde_protector_encryption_key || this.settings.sql_tde_protector_encryption_key.default }; - - async.each(locations.servers, function(location, rcb) { - - var servers = helpers.addSource(cache, source, - ['servers', 'listSql', location]); - - if (!servers) return rcb(); - - if (servers.err || !servers.data) { - helpers.addResult(results, 3, - 'Unable to query for SQL servers: ' + helpers.addError(servers), location); - return rcb(); - } - - if (!servers.data.length) { - helpers.addResult(results, 0, 'No SQL servers found', location); - return rcb(); - } - - servers.data.forEach(function(server) { - const encryptionProtectors = helpers.addSource(cache, source, - ['encryptionProtectors', 'listByServer', location, server.id]); - - if (!encryptionProtectors || encryptionProtectors.err || !encryptionProtectors.data) { - helpers.addResult(results, 3, - 'Unable to query for SQL Server Encryption Protectors: ' + helpers.addError(encryptionProtectors), location, server.id); + + function checkEncryptionProtection(encryptionProtector, location, config) { + if (config.sql_tde_protector_encryption_key == 'byok') { + if ((encryptionProtector.kind && + encryptionProtector.kind.toLowerCase() != 'azurekeyvault') || + (encryptionProtector.serverKeyType && + encryptionProtector.serverKeyType.toLowerCase() != 'azurekeyvault') || + !encryptionProtector.uri) { + helpers.addResult(results, 2, + 'SQL Server TDE protector is not encrypted with BYOK', location, encryptionProtector.id); } else { - if (!encryptionProtectors.data.length) { - helpers.addResult(results, 0, 'No SQL Server Encryption Protectors found for server', location, server.id); - } else { - encryptionProtectors.data.forEach(encryptionProtector => { - if (config.sql_tde_protector_encryption_key == 'byok') { - if ((encryptionProtector.kind && - encryptionProtector.kind.toLowerCase() != 'azurekeyvault') || - (encryptionProtector.serverKeyType && - encryptionProtector.serverKeyType.toLowerCase() != 'azurekeyvault') || - !encryptionProtector.uri) { - helpers.addResult(results, 2, - 'SQL Server TDE protector is not encrypted with BYOK', location, encryptionProtector.id); - } else { - helpers.addResult(results, 0, - 'SQL Server TDE protector is encrypted with BYOK', location, encryptionProtector.id); - } - } else { - helpers.addResult(results, 0, - 'SQL Server TDE protector is encrypted with service-managed key', location, encryptionProtector.id); - } - }); + helpers.addResult(results, 0, + 'SQL Server TDE protector is encrypted with BYOK', location, encryptionProtector.id); + } + } else { + helpers.addResult(results, 0, + 'SQL Server TDE protector is encrypted with service-managed key', location, encryptionProtector.id); + } + } + + async.each(locations.servers, function(location, rcb) { + async.parallel([ + // Check SQL Servers + function(cb) { + const servers = helpers.addSource(cache, source, + ['servers', 'listSql', location]); + + if (!servers) return cb(); + + if (servers.err || !servers.data) { + helpers.addResult(results, 3, + 'Unable to query for SQL servers: ' + helpers.addError(servers), location); + return cb(); } + + if (!servers.data.length) { + helpers.addResult(results, 0, 'No SQL servers found', location); + return cb(); + } + + servers.data.forEach(server => { + const encryptionProtectors = helpers.addSource(cache, source, + ['encryptionProtectors', 'listByServer', location, server.id]); + + if (!encryptionProtectors || encryptionProtectors.err || !encryptionProtectors.data) { + helpers.addResult(results, 3, + 'Unable to query for SQL Server Encryption Protectors: ' + helpers.addError(encryptionProtectors), location, server.id); + } else if (!encryptionProtectors.data.length) { + helpers.addResult(results, 0, 'No SQL Server Encryption Protectors found', location, server.id); + } else { + encryptionProtectors.data.forEach(protector => { + checkEncryptionProtection(protector, location, config); + }); + } + }); + + cb(); + }, + // Check Managed Instances + function(cb) { + const managedInstances = helpers.addSource(cache, source, + ['managedInstances', 'list', location]); + + if (!managedInstances) return cb(); + + if (managedInstances.err || !managedInstances.data) { + helpers.addResult(results, 3, + 'Unable to query for managed instances: ' + helpers.addError(managedInstances), location); + return cb(); + } + + if (!managedInstances.data.length) { + helpers.addResult(results, 0, 'No managed instances found', location); + return cb(); + } + + managedInstances.data.forEach(instance => { + const managedInstanceEncryptionProtectors = helpers.addSource(cache, source, + ['managedInstanceEncryptionProtectors', 'listByInstance', location, instance.id]); + + if (!managedInstanceEncryptionProtectors || managedInstanceEncryptionProtectors.err || !managedInstanceEncryptionProtectors.data) { + helpers.addResult(results, 3, + 'Unable to query for Managed Instance Encryption Protectors: ' + helpers.addError(managedInstanceEncryptionProtectors), location, instance.id); + } else if (!managedInstanceEncryptionProtectors.data.length) { + helpers.addResult(results, 0, 'No Managed Instance Encryption Protectors found', location, instance.id); + } else { + managedInstanceEncryptionProtectors.data.forEach(protector => { + checkEncryptionProtection(protector, location, config); + }); + } + }); + + cb(); } + ], function() { + rcb(); }); - - rcb(); }, function() { - // Global checking goes here callback(null, results, source); }); } diff --git a/plugins/azure/sqlserver/tdeProtectorEncrypted.spec.js b/plugins/azure/sqlserver/tdeProtectorEncrypted.spec.js index 98f983dcde..58aeae3a94 100644 --- a/plugins/azure/sqlserver/tdeProtectorEncrypted.spec.js +++ b/plugins/azure/sqlserver/tdeProtectorEncrypted.spec.js @@ -1,56 +1,43 @@ -var expect = require('chai').expect; -var tdeProtectorEncrypted = require('./tdeProtectorEncrypted'); +const expect = require('chai').expect; +const tdeProtectorEncrypted = require('./tdeProtectorEncrypted'); const servers = [ { - "identity": { - "principalId": "86c16ef5-51e9-4ecb-aeda-5844b8e8eca0", - "type": "SystemAssigned", - "tenantId": "2d4f0836-5935-47f5-954c-14e713119ac2" - }, - "kind": "v12.0", - "location": "eastus", - "tags": {}, - "id": "/subscriptions/1234/resourceGroups/akhtar-rg/providers/Microsoft.Sql/servers/akhtar-server", - "name": "akhtar-server", - "type": "Microsoft.Sql/servers", - "administratorLogin": "akhtar", - "version": "12.0", - "state": "Ready", - "fullyQualifiedDomainName": "akhtar-server.database.windows.net", - "privateEndpointConnections": [], - "minimalTlsVersion": "1.2", - "publicNetworkAccess": "Enabled" - } + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server", + "name": "test-server", + "type": "Microsoft.Sql/servers" + } ]; -const encryptionProtectors = [ - { - "kind": "azurekeyvault", - "id": "/subscriptions/1234/resourceGroups/akhtar-rg/providers/Microsoft.Sql/servers/akhtar-server/encryptionProtector/current", - "name": "current", - "type": "Microsoft.Sql/servers/encryptionProtector", - "serverKeyName": "sadeed-vault_sadeed-key_b5e783becb4a4e789dcd1239441d7567", - "serverKeyType": "AzureKeyVault", - "uri": "https://sadeed-vault.vault.azure.net/keys/sadeed-key/b5e783becb4a4e789dcd1239441d7567" - }, +const managedInstances = [ { - "kind": "servicemanaged", - "id": "/subscriptions/1234/resourceGroups/akhtar-rg/providers/Microsoft.Sql/servers/akhtar-server/encryptionProtector/current", - "name": "current", - "type": "Microsoft.Sql/servers/encryptionProtector", - "serverKeyName": "ServiceManaged", - "serverKeyType": "ServiceManaged" + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/managedInstances/test-instance", + "name": "test-instance", + "type": "Microsoft.Sql/managedInstances" } ]; -const createCache = (servers, encrypted, serversErr, encryptedErr) => { +const byokEncryptionProtector = { + "kind": "azurekeyvault", + "serverKeyType": "AzureKeyVault", + "uri": "https://test-vault.vault.azure.net/keys/test-key/123", + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server/encryptionProtector/current" +}; + +const serviceEncryptionProtector = { + "kind": "servicemanaged", + "serverKeyType": "ServiceManaged", + "id": "/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.Sql/servers/test-server/encryptionProtector/current" +}; + +const createCache = (servers, serverEncryption, managedInstances, managedInstanceEncryption) => { const serverId = (servers && servers.length) ? servers[0].id : null; + const managedInstanceId = (managedInstances && managedInstances.length) ? managedInstances[0].id : null; return { servers: { listSql: { 'eastus': { - err: serversErr, + err: null, data: servers } } @@ -59,119 +46,126 @@ const createCache = (servers, encrypted, serversErr, encryptedErr) => { listByServer: { 'eastus': { [serverId]: { - err: encryptedErr, - data: encrypted + err: null, + data: serverEncryption + } + } + } + }, + managedInstances: { + list: { + 'eastus': { + err: null, + data: managedInstances + } + } + }, + managedInstanceEncryptionProtectors: { + listByInstance: { + 'eastus': { + [managedInstanceId]: { + err: null, + data: managedInstanceEncryption } } } } - } + }; }; describe('tdeProtectorEncrypted', function() { describe('run', function() { - it('should give passing result if no SQL servers found', function(done) { + it('should give passing result if no SQL servers or managed instances found', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); expect(results[0].message).to.include('No SQL servers found'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('No managed instances found'); + done(); }; - const cache = createCache( - [] - ); - + const cache = createCache([], null, [], null); tdeProtectorEncrypted.run(cache, {}, callback); }); - it('should give passing result if No SQL Server Encryption Protectors found for server', function(done) { + it('should give passing result if no encryption protectors found', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('No SQL Server Encryption Protectors found for server'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[0].message).to.include('No SQL Server Encryption Protectors found'); + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('No Managed Instance Encryption Protectors found'); + done(); }; - const cache = createCache( - servers, - [] - ); - + const cache = createCache(servers, [], managedInstances, []); tdeProtectorEncrypted.run(cache, {}, callback); }); - it('should give failing result if SQL Server TDE protector is not encrypted with BYOK', function(done) { + it('should give failing result if TDE protector is not encrypted with BYOK', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('SQL Server TDE protector is not encrypted with BYOK'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[1].status).to.equal(2); + expect(results[1].message).to.include('SQL Server TDE protector is not encrypted with BYOK'); + done(); }; const cache = createCache( - servers, - [encryptionProtectors[1]] + servers, [serviceEncryptionProtector], + managedInstances, [serviceEncryptionProtector] ); - tdeProtectorEncrypted.run(cache, {}, callback); }); - it('should give passing result if SQL Server TDE protector is encrypted with BYOK', function(done) { + it('should give passing result if TDE protector is encrypted with BYOK', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(0); expect(results[0].message).to.include('SQL Server TDE protector is encrypted with BYOK'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('SQL Server TDE protector is encrypted with BYOK'); + done(); }; const cache = createCache( - servers, - [encryptionProtectors[0]] + servers, [byokEncryptionProtector], + managedInstances, [byokEncryptionProtector] ); - tdeProtectorEncrypted.run(cache, {}, callback); }); - it('should give unknown result if Unable to query for SQL servers', function(done) { + it('should give passing result if TDE protector is encrypted with service-managed key when that is allowed', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(3); - expect(results[0].message).to.include('Unable to query for SQL servers'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results.length).to.equal(2); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('SQL Server TDE protector is encrypted with service-managed key'); + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('SQL Server TDE protector is encrypted with service-managed key'); + done(); }; const cache = createCache( - servers, - [], - { message: 'unable to query servers'} + servers, [serviceEncryptionProtector], + managedInstances, [serviceEncryptionProtector] ); - - tdeProtectorEncrypted.run(cache, {}, callback); + tdeProtectorEncrypted.run(cache, { sql_tde_protector_encryption_key: 'default' }, callback); }); - it('should give unknown result if Unable to query for SQL Server Encryption Protectors', function(done) { + it('should give unknown result if unable to query SQL servers or managed instances', function(done) { const callback = (err, results) => { - expect(results.length).to.equal(1); + expect(results.length).to.equal(2); expect(results[0].status).to.equal(3); - expect(results[0].message).to.include('Unable to query for SQL Server Encryption Protectors'); - expect(results[0].region).to.equal('eastus'); - done() + expect(results[0].message).to.include('Unable to query for SQL servers'); + expect(results[1].status).to.equal(3); + expect(results[1].message).to.include('Unable to query for managed instances'); + done(); }; - const cache = createCache( - servers, - [], - null, - { message: 'Unable to query for Vulnerability Assessments setting'} - ); - + const cache = createCache(null, null, null, null); tdeProtectorEncrypted.run(cache, {}, callback); }); - }) + }); }); \ No newline at end of file From 7cdd34b08c27b6d81bcdea23dd11c5183275a724 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 5 Nov 2024 15:11:29 +0500 Subject: [PATCH 2/3] keyVaultSecretExpiryRbac Plugin Update --- .../keyvaults/keyVaultSecretExpiryRbac.js | 90 ++++++++ .../keyVaultSecretExpiryRbac.spec.js | 215 ++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 plugins/azure/keyvaults/keyVaultSecretExpiryRbac.js create mode 100644 plugins/azure/keyvaults/keyVaultSecretExpiryRbac.spec.js diff --git a/plugins/azure/keyvaults/keyVaultSecretExpiryRbac.js b/plugins/azure/keyvaults/keyVaultSecretExpiryRbac.js new file mode 100644 index 0000000000..9b7f99dedf --- /dev/null +++ b/plugins/azure/keyvaults/keyVaultSecretExpiryRbac.js @@ -0,0 +1,90 @@ +var async = require('async'); +var helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Key Vault Secret Expiry RBAC', + category: 'Key Vaults', + domain: 'Application Integration', + severity: 'High', + description: 'Proactively check for Key Vault secrets expiry date and rotate them before expiry date is reached.', + more_info: 'After the expiry date has reached for Key Vault secret, it cannot be used for storing sensitive and confidential data such as passwords and database connection strings anymore.', + recommended_action: 'Ensure that Key Vault secrets are rotated before they get expired.', + link: 'https://learn.microsoft.com/en-us/azure/secret-vault/about-secrets-secrets-and-certificates', + apis: ['vaults:list', 'vaults:getSecrets'], + settings: { + key_vault_secret_expiry_fail: { + name: 'Key Vault Secret Expiry Fail', + description: 'Return a failing result when secret expiration date is within this number of days in the future', + regex: '^[1-9]{1}[0-9]{0,3}$', + default: '30' + } + }, + realtime_triggers: ['microsoftkeyvault:vaults:write', 'microsoftkeyvault:vaults:delete'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var locations = helpers.locations(settings.govcloud); + var config = { + key_vault_secret_expiry_fail: parseInt(settings.key_vault_secret_expiry_fail || this.settings.key_vault_secret_expiry_fail.default) + }; + + async.each(locations.vaults, function(location, rcb) { + var vaults = helpers.addSource(cache, source, + ['vaults', 'list', location]); + + if (!vaults) return rcb(); + + if (vaults.err || !vaults.data) { + helpers.addResult(results, 3, 'Unable to query for Key Vaults: ' + helpers.addError(vaults), location); + return rcb(); + } + + if (!vaults.data.length) { + helpers.addResult(results, 0, 'No Key Vaults found', location); + return rcb(); + } + + vaults.data.forEach(function(vault) { + var secrets = helpers.addSource(cache, source, + ['vaults', 'getSecrets', location, vault.id]); + + if (!secrets || secrets.err || !secrets.data) { + helpers.addResult(results, 3, 'Unable to query for Key Vault secrets: ' + helpers.addError(secrets), location, vault.id); + } else if (!secrets.data.length) { + helpers.addResult(results, 0, 'No Key Vault secrets found', location, vault.id); + } else { + secrets.data.forEach(function(secret) { + var secretName = secret.id.substring(secret.id.lastIndexOf('/') + 1); + var secretId = `${vault.id}/secrets/${secretName}`; + + if (!secret.attributes || !secret.attributes.enabled) { + helpers.addResult(results, 0, 'Secret is not enabled', location, secretId); + } else if (secret.attributes && (secret.attributes.exp || secret.attributes.expiry)) { + let attributes = secret.attributes; + let secretExpiry = attributes.exp ? attributes.exp * 1000 : attributes.expiry; + let difference = Math.round((new Date(secretExpiry).getTime() - (new Date).getTime())/(24*60*60*1000)); + if (difference > config.key_vault_secret_expiry_fail) { + helpers.addResult(results, 0, + `Secret expires in ${difference} days`, location, secretId); + } else if (difference > 0){ + helpers.addResult(results, 2, + `Secret expires in ${difference} days`, location, secretId); + } else { + helpers.addResult(results, 2, + `Secret expired ${Math.abs(difference)} days ago`, location, secretId); + } + } else { + helpers.addResult(results, 0, + 'Secret expiration is not enabled', location, secretId); + } + }); + } + }); + + rcb(); + }, function() { + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/keyvaults/keyVaultSecretExpiryRbac.spec.js b/plugins/azure/keyvaults/keyVaultSecretExpiryRbac.spec.js new file mode 100644 index 0000000000..d08c57a3c9 --- /dev/null +++ b/plugins/azure/keyvaults/keyVaultSecretExpiryRbac.spec.js @@ -0,0 +1,215 @@ +var expect = require('chai').expect; +var auth = require('./keyVaultSecretExpiry'); + +var secretExpiryPass = new Date(); +secretExpiryPass.setMonth(secretExpiryPass.getMonth() + 2); + +var secretExpiryFail = new Date(); +secretExpiryFail.setMonth(secretExpiryFail.getMonth() + 1); + +var secretExpired = new Date(); +secretExpired.setMonth(secretExpired.getMonth() - 1); + +const listKeyVaults = [ + { + "id": "/subscriptions/abcdef123-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/Default-ActivityLogAlerts/providers/Microsoft.KeyVault/vaults/testvault", + "name": "testvault", + "type": "Microsoft.KeyVault/vaults", + "location": "eastus", + "tags": {}, + "sku": { + "family": "A", + "name": "Standard" + } + }, + { + "id": "/subscriptions/abcdef123-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/Default-ActivityLogAlerts/providers/Microsoft.KeyVault/vaults/testvault", + "name": "testvault", + "type": "Microsoft.KeyVault/vaults", + "location": "eastus", + "tags": {}, + "sku": { + "family": "A", + "name": "Standard" + } + } +]; + +const getSecrets = [ + { + '/subscriptions/abcdef123-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/Default-ActivityLogAlerts/providers/Microsoft.KeyVault/vaults/testvault': { + data: [ + { + "id": "https://testvault.vault.azure.net/secrets/mysecret", + "attributes": { + "enabled": true, + "expiry": null, + "created": 1572289869, + "updated": 1572290380, + "recoveryLevel": "Recoverable+Purgeable" + }, + "tags": {} + } + ] + } + },{ + '/subscriptions/abcdef123-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/Default-ActivityLogAlerts/providers/Microsoft.KeyVault/vaults/testvault': { + data: [ + { + "id": "https://testvault.vault.azure.net/secrets/mysecret", + "attributes": { + "enabled": true, + "expiry": secretExpiryPass, + "created": 1572289869, + "updated": 1572290380, + "recoveryLevel": "Recoverable+Purgeable" + }, + "tags": {} + } + ] + } + }, + { + '/subscriptions/abcdef123-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/Default-ActivityLogAlerts/providers/Microsoft.KeyVault/vaults/testvault': { + data: [ + { + "id": "https://testvault.vault.azure.net/secrets/mysecret", + "attributes": { + "enabled": true, + "expiry": secretExpiryFail, + "created": 1572289869, + "updated": 1572290380, + "recoveryLevel": "Recoverable+Purgeable" + }, + "tags": {} + } + ] + } + }, + { + '/subscriptions/abcdef123-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/Default-ActivityLogAlerts/providers/Microsoft.KeyVault/vaults/testvault': { + data: [ + { + "id": "https://testvault.vault.azure.net/secrets/mysecret", + "attributes": { + "enabled": true, + "expiry": secretExpired, + "created": 1572289869, + "updated": 1572290380, + "recoveryLevel": "Recoverable+Purgeable" + }, + "tags": {} + } + ] + } + }, + { + '/subscriptions/abcdef123-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/Default-ActivityLogAlerts/providers/Microsoft.KeyVault/vaults/testvault': { + data: [ + { + "id": "https://testvault.vault.azure.net/secrets/mysecret", + "attributes": { + "enabled": false, + "expiry": secretExpired, + "created": 1572289869, + "updated": 1572290380, + "recoveryLevel": "Recoverable+Purgeable" + }, + "tags": {} + } + ] + } + } +]; + +const createCache = (err, list, get) => { + return { + vaults: { + list: { + 'eastus': { + err: err, + data: list + } + }, + getSecrets: { + 'eastus': get + } + } + } +}; + +describe('keyVaultSecretExpiry', function() { + describe('run', function() { + it('should give passing result if no secrets 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 Key Vaults found'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + auth.run(createCache(null, [], {}), {}, callback); + }); + + it('should give passing result if secret expiration is not enabled', 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('Secret expiration is not enabled'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + auth.run(createCache(null, [listKeyVaults[0]], getSecrets[0]), {}, callback); + }); + + it('should give passing result if secret expiry is not yet reached', 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('Secret expires'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + auth.run(createCache(null, [listKeyVaults[0]], getSecrets[1]), {}, callback); + }); + + it('should give failing result if secret has expired', 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('Secret expired'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + auth.run(createCache(null, [listKeyVaults[0]], getSecrets[3]), {}, callback); + }); + + it('should give failing result if secret expires within failure expiry date', 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('Secret expires'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + auth.run(createCache(null, [listKeyVaults[0]], getSecrets[2]), { key_vault_secret_expiry_fail: '40' }, callback); + }); + + it('should give passing result if key is disabled', 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('Secret is not enabled'); + expect(results[0].region).to.equal('eastus'); + done() + }; + + auth.run(createCache(null, [listKeyVaults[0]], getSecrets[4]), {}, callback); + }); + }) +}); From 40f3bda4f81436080dc3ec0945d0e99fe89aa61f Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 5 Nov 2024 07:04:42 -0700 Subject: [PATCH 3/3] single file fix --- plugins/azure/keyvaults/keyVaultSecretExpiry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/azure/keyvaults/keyVaultSecretExpiry.js b/plugins/azure/keyvaults/keyVaultSecretExpiry.js index 2fe0f307a0..9b7f99dedf 100644 --- a/plugins/azure/keyvaults/keyVaultSecretExpiry.js +++ b/plugins/azure/keyvaults/keyVaultSecretExpiry.js @@ -2,7 +2,7 @@ var async = require('async'); var helpers = require('../../../helpers/azure'); module.exports = { - title: 'Key Vault Secret Expiry', + title: 'Key Vault Secret Expiry RBAC', category: 'Key Vaults', domain: 'Application Integration', severity: 'High',