diff --git a/exports.js b/exports.js index 889ab0a1ab..f5d69b8078 100644 --- a/exports.js +++ b/exports.js @@ -741,6 +741,7 @@ module.exports = { 'mlWorkspaceHBI' : require(__dirname + '/plugins/azure/machinelearning/mlWorkspaceHBI.js'), 'workspacePublicAccessDisabled' : require(__dirname + '/plugins/azure/machinelearning/workspacePublicAccessDisabled.js'), + 'mlWorkspaceCMKEncrypted' : require(__dirname + '/plugins/azure/machinelearning/mlWorkspaceCMKEncrypted.js'), 'workspaceLoggingEnabled' : require(__dirname + '/plugins/azure/machinelearning/workspaceLoggingEnabled.js'), 'mlWorkspaceHasTags' : require(__dirname + '/plugins/azure/machinelearning/mlWorkspaceHasTags.js'), diff --git a/plugins/azure/machinelearning/mlWorkspaceCMKEncrypted.js b/plugins/azure/machinelearning/mlWorkspaceCMKEncrypted.js new file mode 100644 index 0000000000..efc8dd25bd --- /dev/null +++ b/plugins/azure/machinelearning/mlWorkspaceCMKEncrypted.js @@ -0,0 +1,53 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Machine Learning Workspace CMK Encrypted', + category: 'MySQL Server', + domain: 'Databases', + severity: 'High', + description: 'Ensures that Machine Learning Workspaces are encrypted using CMK.', + more_info: 'Azure Machine Learning allows you to encrypt workspaces using customer-managed keys (CMK) instead of using platform-managed keys, which are enabled by default. Using CMK encryption offers enhanced security and compliance, allowing centralized management and control of encryption keys through Azure Key Vault.', + recommended_action: 'Ensure that Machine Learning Workspaces are encrypted using CMK.', + link: 'https://learn.microsoft.com/en-us/azure/machine-learning/concept-customer-managed-keys', + apis: ['machineLearning:listWorkspaces'], + realtime_triggers: ['microsoft:machinelearningservices:workspaces:write', 'microsoft:machinelearningservices:workspaces:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.machineLearning, function(location, rcb) { + var machineLearningWorkspaces = helpers.addSource(cache, source, + ['machineLearning', 'listWorkspaces', location]); + + if (!machineLearningWorkspaces) return rcb(); + + if (machineLearningWorkspaces.err || !machineLearningWorkspaces.data) { + helpers.addResult(results, 3, + 'Unable to query for Machine Learning workspaces: ' + helpers.addError(machineLearningWorkspaces), location); + return rcb(); + } + + if (!machineLearningWorkspaces.data.length) { + helpers.addResult(results, 0, 'No existing Machine Learning workspaces found', location); + return rcb(); + } + + for (let workspace of machineLearningWorkspaces.data) { + if (!workspace.id) continue; + + if (workspace.encryption && workspace.encryption.keyVaultProperties && workspace.encryption.keyVaultProperties.keyIdentifier) { + helpers.addResult(results, 0, 'Machine Learning workspace is encrypted using CMK', location, workspace.id); + } else { + helpers.addResult(results, 2, 'Machine Learning workspace is not encrypted using CMK', location, workspace.id); + } + } + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/machinelearning/mlWorkspaceCMKEncrypted.spec.js b/plugins/azure/machinelearning/mlWorkspaceCMKEncrypted.spec.js new file mode 100644 index 0000000000..5427b8f472 --- /dev/null +++ b/plugins/azure/machinelearning/mlWorkspaceCMKEncrypted.spec.js @@ -0,0 +1,97 @@ +var expect = require('chai').expect; +var mlWorkspaceCMKEncrypted = require('./mlWorkspaceCMKEncrypted'); + +const workspaces = [ + { + "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/workspaces/test1", + "name": "test", + "type": "Microsoft.MachineLearningServices/workspaces", + "encryption": { + "keyVaultProperties": { + "keyIdentifier": "https://dummy.vault.azure.net/keys/test2/9e34232342342343242343", + "identityClientId": null, + "keyVaultArmId": "/subscriptions/12345667/resourceGroups/test1223/providers/Microsoft.KeyVault/vaults/dummy" + }, + } + + + }, + { + "id": "/subscriptions/12345667/resourceGroups/test/providers/Microsoft.MachineLearningServices/workspaces/test1", + "name": "test", + "type": "Microsoft.MachineLearningServices/workspaces", + + }, +]; + +const createCache = (workspaces) => { + return { + machineLearning: { + listWorkspaces: { + 'eastus': { + data: workspaces + } + } + } + }; +}; + +const createErrorCache = () => { + return { + machineLearning: { + listWorkspaces: { + 'eastus': {} + } + } + }; +}; + +describe('mlWorkspaceCMKEncrypted', function() { + describe('run', function() { + it('should give passing result if no Machine Learning workspace found', function(done) { + const cache = createCache([]); + mlWorkspaceCMKEncrypted.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No existing Machine Learning workspaces found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if unable to query for Machine Learning workspaces', function(done) { + const cache = createErrorCache(); + mlWorkspaceCMKEncrypted.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 for Machine Learning workspaces: '); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + + it('should give passing result if Machine Learning workspace is not encrypted using CMK', function(done) { + const cache = createCache([workspaces[0]]); + mlWorkspaceCMKEncrypted.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 is encrypted using CMK'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Machine Learning workspace is not CMK encrypted', function(done) { + const cache = createCache([workspaces[1]]); + mlWorkspaceCMKEncrypted.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 is not encrypted using CMK'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + }); +}); \ No newline at end of file