diff --git a/collectors/aws/ecs/listContainerInstances.js b/collectors/aws/ecs/listContainerInstances.js index c7b9eb175e..987ca65d2b 100644 --- a/collectors/aws/ecs/listContainerInstances.js +++ b/collectors/aws/ecs/listContainerInstances.js @@ -16,13 +16,13 @@ module.exports = function(AWSConfig, collection, retries, callback) { helpers.makeCustomCollectorCall(ecs, 'listContainerInstances', params, retries, null, null, null, function(err, data) { if (err) { collection.ecs.listContainerInstances[AWSConfig.region][cluster].err = err; - } else if (data && data.containerInstanceArns) { - collection.ecs.listContainerInstances[AWSConfig.region][cluster].data = data.containerInstanceArns; } + collection.ecs.listContainerInstances[AWSConfig.region][cluster].data = data.containerInstanceArns; + cb(); }); }, function(){ callback(); }); -}; +}; \ No newline at end of file diff --git a/collectors/aws/ecs/listServices.js b/collectors/aws/ecs/listServices.js index 4898290ad1..916ab9002d 100644 --- a/collectors/aws/ecs/listServices.js +++ b/collectors/aws/ecs/listServices.js @@ -19,11 +19,12 @@ module.exports = function(AWSConfig, collection, retries, callback) { if (err) { collection.ecs.listServices[AWSConfig.region][cluster].err = err; } - if (data && data.serviceArns) collection.ecs.listServices[AWSConfig.region][cluster].data = data.serviceArns; + + collection.ecs.listServices[AWSConfig.region][cluster].data = data.serviceArns; cb(); }); }, function(){ callback(); }); -}; +}; \ No newline at end of file diff --git a/collectors/azure/collector.js b/collectors/azure/collector.js index fabd6f2c20..6ad2040b53 100644 --- a/collectors/azure/collector.js +++ b/collectors/azure/collector.js @@ -65,8 +65,7 @@ let collect = function(AzureConfig, settings, callback) { helpers.call({ url: localUrl, post: obj.post, - token: obj.graph ? loginData.graphToken : (obj.vault ? loginData.vaultToken : loginData.token), - govcloud : AzureConfig.Govcloud + token: obj.graph ? loginData.graphToken : (obj.vault ? loginData.vaultToken : loginData.token) }, function(err, data) { if (err) return cb(err); @@ -144,8 +143,7 @@ let collect = function(AzureConfig, settings, callback) { function(cb) { function processTopCall(collectionObj, service, subCallObj, subCallCb) { processCall(subCallObj, function(processCallErr, processCallData) { - if (AzureConfig.Govcloud) helpers.addGovLocations(subCallObj, service, collectionObj, processCallErr, processCallData , skip_locations); - else helpers.addLocations(subCallObj, service, collectionObj, processCallErr, processCallData , skip_locations); + helpers.addLocations(subCallObj, service, collectionObj, processCallErr, processCallData , skip_locations); subCallCb(); }); } diff --git a/config_example.js b/config_example.js index e943ad61d9..2ff65bb828 100644 --- a/config_example.js +++ b/config_example.js @@ -33,10 +33,7 @@ module.exports = { // application_id: process.env.AZURE_APPLICATION_ID || '', // key_value: process.env.AZURE_KEY_VALUE || '', // directory_id: process.env.AZURE_DIRECTORY_ID || '', - // subscription_id: process.env.AZURE_SUBSCRIPTION_ID || '', - // storage_connection: process.env.AZURE_STORAGE_CONNECTION || '', - // blob_container: process.env.AZURE_BLOB_CONTAINER || '', - // govcloud: process.env.AZURE_GOV_CLOUD || '' + // subscription_id: process.env.AZURE_SUBSCRIPTION_ID || '' }, azure_remediate: { // OPTION 1: If using a credential JSON file, enter the path below diff --git a/engine.js b/engine.js index e10dd5c229..006d96c474 100644 --- a/engine.js +++ b/engine.js @@ -13,36 +13,6 @@ function runAuth(settings, remediateConfig, callback) { }); } else callback(); } - -async function uploadResultsToBlob(resultsObject, storageConnection, blobContainerName ) { - var azureStorage = require('@azure/storage-blob'); - - try { - const blobServiceClient = azureStorage.BlobServiceClient.fromConnectionString(storageConnection); - const containerClient = blobServiceClient.getContainerClient(blobContainerName); - - // Check if the container exists, if not, create it - const exists = await containerClient.exists(); - if (!exists) { - await containerClient.create(); - console.log(`Container ${blobContainerName} created successfully.`); - } - - const blobName = `results-${Date.now()}.json`; - const blockBlobClient = containerClient.getBlockBlobClient(blobName); - - const data = JSON.stringify(resultsObject, null, 2); - const uploadBlobResponse = await blockBlobClient.upload(data, data.length); - console.log(`Blob ${blobName} uploaded successfully. Request ID: ${uploadBlobResponse.requestId}`); - } catch (error) { - if (error.message && error.message == 'Invalid DefaultEndpointsProtocol') { - console.log(`Invalid Storage Account connection string ${error.message}`); - } else { - console.log(`Failed to upload results to blob: ${error.message}`); - } - } -} - /** * The main function to execute CloudSploit scans. * @param cloudConfig The configuration for the cloud provider. @@ -178,19 +148,16 @@ var engine = function(cloudConfig, settings) { console.log('INFO: Analysis complete. Scan report to follow...'); var maximumStatus = 0; - var resultsObject = {}; // Initialize resultsObject for azure gov cloud - + function executePlugins(cloudRemediateConfig) { async.mapValuesLimit(plugins, 10, function(plugin, key, pluginDone) { if (skippedPlugins.indexOf(key) > -1) return pluginDone(null, 0); + var postRun = function(err, results) { if (err) return console.log(`ERROR: ${err}`); if (!results || !results.length) { console.log(`Plugin ${plugin.title} returned no results. There may be a problem with this plugin.`); } else { - if (!resultsObject[plugin.title]) { - resultsObject[plugin.title] = []; - } for (var r in results) { // If we have suppressed this result, then don't process it // so that it doesn't affect the return code. @@ -198,8 +165,6 @@ var engine = function(cloudConfig, settings) { continue; } - resultsObject[plugin.title].push(results[r]); - var complianceMsg = []; if (settings.compliance && settings.compliance.length) { settings.compliance.forEach(function(c) { @@ -259,8 +224,6 @@ var engine = function(cloudConfig, settings) { } }, function(err) { if (err) return console.log(err); - - if (cloudConfig.StorageConnection && cloudConfig.BlobContainer) uploadResultsToBlob(resultsObject, cloudConfig.StorageConnection, cloudConfig.BlobContainer); // console.log(JSON.stringify(collection, null, 2)); outputHandler.close(); if (settings.exit_code) { diff --git a/exports.js b/exports.js index ed0e07a616..9ca097916d 100644 --- a/exports.js +++ b/exports.js @@ -25,14 +25,11 @@ module.exports = { 'customDomainTlsVersion' : require(__dirname + '/plugins/aws/apigateway/customDomainTlsVersion.js'), 'apigatewayDefaultEndpointDisabled' : require(__dirname + '/plugins/aws/apigateway/apigatewayDefaultEndpointDisabled.js'), 'apigatewayAuthorization' : require(__dirname + '/plugins/aws/apigateway/apigatewayAuthorization.js'), - 'apigatewayV2Authorization' : require(__dirname + '/plugins/aws/apigateway/apigatewayV2Authorization.js'), - 'apigatewayV2AccessLogging' : require(__dirname + '/plugins/aws/apigateway/apigatewayV2AccessLogging.js'), - 'apigatewayRequestValidation' : require(__dirname + '/plugins/aws/apigateway/apigatewayRequestValidation.js'), 'restrictExternalTraffic' : require(__dirname + '/plugins/aws/appmesh/restrictExternalTraffic.js'), 'appmeshTLSRequired' : require(__dirname + '/plugins/aws/appmesh/appmeshTLSRequired.js'), 'appmeshVGHealthChecks' : require(__dirname + '/plugins/aws/appmesh/appmeshVGHealthChecks.js'), - + 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), 'asgActiveNotifications' : require(__dirname + '/plugins/aws/autoscaling/asgActiveNotifications.js'), @@ -242,7 +239,6 @@ module.exports = { 'ebsVolumeHasTags' : require(__dirname + '/plugins/aws/ec2/ebsVolumeHasTags.js'), 'openAllPortsProtocolsEgress' : require(__dirname + '/plugins/aws/ec2/openAllPortsProtocolsEgress.js'), 'defaultSecurityGroupInUse' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroupInUse.js'), - 'ec2NetworkExposure' : require(__dirname + '/plugins/aws/ec2/ec2NetworkExposure.js'), 'efsCmkEncrypted' : require(__dirname + '/plugins/aws/efs/efsCmkEncrypted.js'), 'efsEncryptionEnabled' : require(__dirname + '/plugins/aws/efs/efsEncryptionEnabled.js'), @@ -324,7 +320,7 @@ module.exports = { 'opensearchPublicEndpoint' : require(__dirname + '/plugins/aws/opensearch/opensearchPublicEndpoint.js'), 'opensearchRequireIAMAuth' : require(__dirname + '/plugins/aws/opensearch/opensearchRequireIAMAuth.js'), 'opensearchTlsVersion' : require(__dirname + '/plugins/aws/opensearch/opensearchTlsVersion.js'), - 'opensearchUpgradeAvailable' : require(__dirname + '/plugins/aws/opensearch/opensearchUpgradeAvailable.js'), + 'opensearchUpgradeAvailable' : require(__dirname + '/plugins/aws/opensearch/opensearchUpgradeAvailable.js'), 'opensearchVersion' : require(__dirname + '/plugins/aws/opensearch/opensearchVersion.js'), 'opensearchZoneAwarenessEnabled': require(__dirname + '/plugins/aws/opensearch/opensearchZoneAwarenessEnabled.js'), @@ -498,8 +494,6 @@ module.exports = { 'ssmSessionDuration' : require(__dirname + '/plugins/aws/ssm/ssmSessionDuration'), 'ledgerEncrypted' : require(__dirname + '/plugins/aws/qldb/ledgerEncrypted'), - 'ledgerHasTags' : require(__dirname + '/plugins/aws/qldb/ledgerHasTags'), - 'ledgerDeletionProtection' : require(__dirname + '/plugins/aws/qldb/ledgerDeletionProtection'), 'lambdaAdminPrivileges' : require(__dirname + '/plugins/aws/lambda/lambdaAdminPrivileges.js'), 'envVarsClientSideEncryption' : require(__dirname + '/plugins/aws/lambda/envVarsClientSideEncryption.js'), @@ -597,13 +591,11 @@ module.exports = { 'organizationInvite' : require(__dirname + '/plugins/aws/organizations/organizationInvite.js'), 'guardDutyEnabled' : require(__dirname + '/plugins/aws/guardduty/guarddutyEnabled.js'), - 'eksProtectionEnabled' : require(__dirname + '/plugins/aws/guardduty/eksProtectionEnabled.js'), 'guardDutyMaster' : require(__dirname + '/plugins/aws/guardduty/guarddutyMaster.js'), 'noActiveFindings' : require(__dirname + '/plugins/aws/guardduty/noActiveFindings'), 's3ProtectionEnabled' : require(__dirname + '/plugins/aws/guardduty/s3ProtectionEnabled.js'), 'rdsProtectionEnabled' : require(__dirname + '/plugins/aws/guardduty/rdsProtectionEnabled.js'), 'exportedFindingsEncrypted' : require(__dirname + '/plugins/aws/guardduty/exportedFindingsEncrypted.js'), - 'lambdaProtectionEnabled' : require(__dirname + '/plugins/aws/guardduty/lambdaProtectionEnabled.js'), 'workspacesVolumeEncryption' : require(__dirname + '/plugins/aws/workspaces/workspacesVolumeEncryption.js'), 'workSpacesHealthyInstances' : require(__dirname + '/plugins/aws/workspaces/workSpacesHealthyInstances.js'), @@ -616,8 +608,6 @@ module.exports = { 'codebuildValidSourceProviders' : require(__dirname + '/plugins/aws/codebuild/codebuildValidSourceProviders.js'), 'projectArtifactsEncrypted' : require(__dirname + '/plugins/aws/codebuild/projectArtifactsEncrypted.js'), - 'buildProjectEnvPriviligedMode' : require(__dirname + '/plugins/aws/codebuild/buildProjectEnvPriviligedMode.js'), - 'codebuildProjectLoggingEnabled': require(__dirname + '/plugins/aws/codebuild/codebuildProjectLoggingEnabled.js'), 'codestarValidRepoProviders' : require(__dirname + '/plugins/aws/codestar/codestarValidRepoProviders.js'), 'codestarHasTags' : require(__dirname + '/plugins/aws/codestar/codestarHasTags.js'), @@ -625,7 +615,6 @@ module.exports = { 'pipelineArtifactsEncrypted' : require(__dirname + '/plugins/aws/codepipeline/pipelineArtifactsEncrypted.js'), 'dataStoreEncrypted' : require(__dirname + '/plugins/aws/healthlake/dataStoreEncrypted.js'), - 'dataStoreHasTags' : require(__dirname + '/plugins/aws/healthlake/dataStoreHasTags.js'), 'codeartifactDomainEncrypted' : require(__dirname + '/plugins/aws/codeartifact/codeartifactDomainEncrypted.js'), @@ -641,14 +630,11 @@ module.exports = { 'databrewJobOutputEncrypted' : require(__dirname + '/plugins/aws/gluedatabrew/databrewJobOutputEncrypted.js'), 'networkMemberDataEncrypted' : require(__dirname + '/plugins/aws/managedblockchain/networkMemberDataEncrypted.js'), - 'networkMemberCloudwatchLogs' : require(__dirname + '/plugins/aws/managedblockchain/networkMemberCloudwatchLogs.js'), 'docdbClusterEncrypted' : require(__dirname + '/plugins/aws/documentDB/docdbClusterEncrypted.js'), 'docDbHasTags' : require(__dirname + '/plugins/aws/documentDB/docDbHasTags.js'), 'docdbDeletionProtectionEnabled': require(__dirname + '/plugins/aws/documentDB/docdbDeletionProtectionEnabled.js'), 'docdbClusterBackupRetention' : require(__dirname + '/plugins/aws/documentDB/docdbClusterBackupRetention.js'), - 'docdbCertificateRotated' : require(__dirname + '/plugins/aws/documentDB/docdbCertificateRotated.js'), - 'docdbClusterProfilerEnabled' : require(__dirname + '/plugins/aws/documentDB/docdbClusterProfilerEnabled.js'), 'instanceMediaStreamsEncrypted' : require(__dirname + '/plugins/aws/connect/instanceMediaStreamsEncrypted.js'), 'instanceTranscriptsEncrypted' : require(__dirname + '/plugins/aws/connect/instanceTranscriptsEncrypted.js'), @@ -702,8 +688,7 @@ module.exports = { 'opensearchCollectionCmkEncrypted': require(__dirname + '/plugins/aws/openSearchServerless/opensearchCollectionCmkEncrypted.js'), 'opensearchCollectionPublicAccess': require(__dirname + '/plugins/aws/openSearchServerless/opensearchCollectionPublicAccess.js'), - 'securityHubEnabled' : require(__dirname + '/plugins/aws/securityhub/securityHubEnabled.js'), - 'securityHubActiveFindings' : require(__dirname + '/plugins/aws/securityhub/securityHubActiveFindings.js'), + 'securityHubEnabled' : require(__dirname + '/plugins/aws/securityhub/securityHubEnabled.js') }, azure : { 'fileServiceEncryption' : require(__dirname + '/plugins/azure/storageaccounts/fileServiceEncryption.js'), @@ -720,7 +705,7 @@ module.exports = { 'storageAccountHasTags' : require(__dirname + '/plugins/azure/storageaccounts/storageAccountHasTags.js'), 'storageAccountPrivateEndpoint' : require(__dirname + '/plugins/azure/storageaccounts/storageAccountPrivateEndpoint.js'), 'infrastructureEncryption' : require(__dirname + '/plugins/azure/storageaccounts/infrastructureEncryption.js'), - 'queueServiceLoggingEnabled' : require(__dirname + '/plugins/azure/storageaccounts/queueServiceLoggingEnabled.js'), + 'queueServiceLoggingEnabled' : require(__dirname + '/plugins/azure/storageaccounts/queueServiceLoggingEnabled.js'), 'tableServiceLoggingEnabled' : require(__dirname + '/plugins/azure/storageaccounts/tableServiceLoggingEnabled.js'), 'blobServiceLoggingEnabled' : require(__dirname + '/plugins/azure/storageaccounts/blobServiceLoggingEnabled.js'), @@ -730,23 +715,18 @@ module.exports = { 'fileServiceAllAccessAcl' : require(__dirname + '/plugins/azure/fileservice/fileServiceAllAccessAcl.js'), 'tableServiceAllAccessAcl' : require(__dirname + '/plugins/azure/tableservice/tableServiceAllAccessAcl.js'), - 'queueServiceAllAccessAcl' : require(__dirname + '/plugins/azure/queueservice/queueServiceAllAccessAcl.js'), - + 'queueServiceAllAccessAcl' : require(__dirname + '/plugins/azure/queueservice/queueServiceAllAccessAcl.js'), + 'externalNetworkAccess' : require(__dirname + '/plugins/azure/containerapps/externalNetworkAccess.js'), 'containerAppManagedIdentity' : require(__dirname + '/plugins/azure/containerapps/containerAppManagedIdentity.js'), 'containerAppAuthEnabled' : require(__dirname + '/plugins/azure/containerapps/containerAppAuthEnabled.js'), 'containerAppVolumeMount' : require(__dirname + '/plugins/azure/containerapps/containerAppVolumeMount.js'), 'containerAppHttpsOnly' : require(__dirname + '/plugins/azure/containerapps/containerAppHttpsOnly.js'), 'containerAppHasTags' : require(__dirname + '/plugins/azure/containerapps/containerAppHasTags.js'), - 'containerAppIPRestriction' : require(__dirname + '/plugins/azure/containerapps/containerAppIPRestriction.js'), - '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'), - 'mlRegistryHasTags' : require(__dirname + '/plugins/azure/machinelearning/mlRegistryHasTags.js'), 'mlWorkspaceHasTags' : require(__dirname + '/plugins/azure/machinelearning/mlWorkspaceHasTags.js'), - 'mlRegistryPublicAccess' : require(__dirname + '/plugins/azure/machinelearning/mlRegistryPublicAccess.js'), 'minimumTlsVersion' : require(__dirname + '/plugins/azure/redisCache/minimumTlsVersion.js'), @@ -815,7 +795,6 @@ module.exports = { 'vmDiskCMKRotation' : require(__dirname + '/plugins/azure/virtualmachines/vmDiskCMKRotation.js'), 'vmDiskPublicAccess' : require(__dirname + '/plugins/azure/virtualmachines/vmDiskPublicAccess.js'), 'computeGalleryRbacSharing' : require(__dirname + '/plugins/azure/virtualmachines/computeGalleryRbacSharing.js'), - 'vmNetworkExposure' : require(__dirname + '/plugins/azure/virtualmachines/vmNetworkExposure.js'), 'bastionHostExists' : require(__dirname + '/plugins/azure/bastion/bastionHostExists.js'), 'bastionHostDiagnosticLogs' : require(__dirname + '/plugins/azure/bastion/bastionHostDiagnosticLogs.js'), @@ -826,7 +805,7 @@ module.exports = { 'monitorLogsEnabled' : require(__dirname + '/plugins/azure/monitor/monitorLogsEnabled.js'), 'diagnosticsCapturedCategories' : require(__dirname + '/plugins/azure/monitor/diagnosticsCapturedCategories.js'), 'diagnosticsSettingsEnabled' : require(__dirname + '/plugins/azure/monitor/diagnosticsSettingsEnabled.js'), - 'resourceAppropriateSKU' : require(__dirname + '/plugins/azure/monitor/monitorResourceSku.js'), + 'resourceAppropriateSKU' : require(__dirname + '/plugins/azure/monitor/monitorResourceSku.js'), 'securityPolicyAlertsEnabled' : require(__dirname + '/plugins/azure/logalerts/securityPolicyAlertsEnabled.js'), 'nsgLoggingEnabled' : require(__dirname + '/plugins/azure/logalerts/nsgLoggingEnabled.js'), @@ -841,33 +820,41 @@ module.exports = { 'loadBalancerLoggingEnabled' : require(__dirname + '/plugins/azure/logalerts/loadBalancerLoggingEnabled.js'), 'virtualMachineLogging' : require(__dirname + '/plugins/azure/logalerts/virtualMachineLogging.js'), 'flexibleServerLoggingEnabled' : require(__dirname + '/plugins/azure/logalerts/flexibleServerLoggingEnabled.js'), - 'mysqlFlexibleServerLoggingEnabled': require(__dirname + '/plugins/azure/logalerts/mysqlFlexibleServerLoggingEnabled.js'), 'postgreSqlDBLoggingEnabled' : require(__dirname + '/plugins/azure/logalerts/postgreSqlDBLoggingEnabled.js'), 'sqlServerDatabaseRenameAlert' : require(__dirname + '/plugins/azure/logalerts/sqlServerDatabaseRenameAlert.js'), 'virtualMachinesPowerOffAlert' : require(__dirname + '/plugins/azure/logalerts/virtualMachinesPowerOffAlert.js'), 'virtualMachinesDeallocateAlert': require(__dirname + '/plugins/azure/logalerts/virtualMachinesDeallocateAlert.js'), 'publicIpAddressLoggingEnabled' : require(__dirname+ '/plugins/azure/logalerts/publicIpAddressLoggingEnabled.js'), + 'monitorEndpointProtection' : require(__dirname + '/plugins/azure/securitycenter/monitorEndpointProtection.js'), 'monitorBlobEncryption' : require(__dirname + '/plugins/azure/securitycenter/monitorBlobEncryption.js'), + 'monitorSystemUpdates' : require(__dirname + '/plugins/azure/securitycenter/monitorSystemUpdates.js'), + 'monitorJitNetworkAccess' : require(__dirname + '/plugins/azure/securitycenter/monitorJitNetworkAccess.js'), 'monitorVMVulnerability' : require(__dirname + '/plugins/azure/securitycenter/monitorVMVulnerability.js'), 'monitorSQLEncryption' : require(__dirname + '/plugins/azure/securitycenter/monitorSqlEncryption.js'), 'monitorSQLAuditing' : require(__dirname + '/plugins/azure/securitycenter/monitorSqlAuditing.js'), 'monitorDiskEncryption' : require(__dirname + '/plugins/azure/securitycenter/monitorDiskEncryption.js'), + 'appWhitelistingEnabled' : require(__dirname + '/plugins/azure/securitycenter/appWhitelistingEnabled.js'), + 'securityConfigMonitoring' : require(__dirname + '/plugins/azure/securitycenter/securityConfigMonitoring.js'), + 'autoProvisioningEnabled' : require(__dirname + '/plugins/azure/securitycenter/autoProvisioningEnabled.js'), 'adminSecurityAlertsEnabled' : require(__dirname + '/plugins/azure/securitycenter/adminSecurityAlertsEnabled.js'), + 'securityContactsEnabled' : require(__dirname + '/plugins/azure/securitycenter/securityContactsEnabled.js'), 'monitorNsgEnabled' : require(__dirname + '/plugins/azure/securitycenter/monitorNsgEnabled.js'), + 'highSeverityAlertsEnabled' : require(__dirname + '/plugins/azure/securitycenter/highSeverityAlertsEnabled.js'), + 'standardPricingEnabled' : require(__dirname + '/plugins/azure/securitycenter/standardPricingEnabled.js'), + 'monitorExternalAccounts' : require(__dirname + '/plugins/azure/securitycenter/monitorExternalAccounts.js'), + 'monitorIpForwarding' : require(__dirname + '/plugins/azure/securitycenter/monitorIpForwarding.js'), + 'monitorNextGenerationFirewall' : require(__dirname + '/plugins/azure/securitycenter/monitorNextGenerationFirewall.js'), + 'monitorSubscriptionOwners' : require(__dirname + '/plugins/azure/securitycenter/monitorSubscriptionOwners.js'), + 'securityContactAdditionalEmail': require(__dirname + '/plugins/azure/securitycenter/securityContactAdditionalEmail.js'), + 'securityContactRoleSetToOwner' : require(__dirname + '/plugins/azure/securitycenter/securityContactRoleSetToOwner.js'), 'resourceAllowedLocations' : require(__dirname + '/plugins/azure/policyservice/resourceAllowedLocations.js'), 'resourceLocationMatch' : require(__dirname + '/plugins/azure/policyservice/resourceLocationMatch.js'), - 'mysqlFlexibleServerHasTags' : require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServerHasTags.js'), 'enforceMySQLSSLConnection' : require(__dirname + '/plugins/azure/mysqlserver/enforceMySQLSSLConnection.js'), 'mysqlFlexibleServersMinTls' : require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServersMinTls.js'), - 'mysqlFlexibleServerVersion' : require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServerVersion.js'), 'mysqlServerHasTags' : require(__dirname + '/plugins/azure/mysqlserver/mysqlServerHasTags.js'), - 'mysqlFlexibleServerCMKEncrypted': require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServerCMKEncrypted.js'), - 'mysqlFlexibleServerPublicAccess': require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServerPublicAccess.js'), - 'mysqlFlexibleServerDignosticLogs': require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServerDignosticLogs.js'), - 'mysqlFlexibleServerIdentity' : require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServerIdentity.js'), 'logRetentionDays' : require(__dirname + '/plugins/azure/postgresqlserver/logRetentionDays.js'), 'connectionThrottlingEnabled' : require(__dirname + '/plugins/azure/postgresqlserver/connectionThrottlingEnabled.js'), @@ -964,7 +951,7 @@ module.exports = { 'sqlServerRecurringScans' : require(__dirname + '/plugins/azure/sqlserver/sqlServerRecurringScans.js'), 'sqlServerSendScanReports' : require(__dirname + '/plugins/azure/sqlserver/sqlServerSendScanReports.js'), 'sqlServerHasTags' : require(__dirname + '/plugins/azure/sqlserver/sqlServerHasTags.js'), - 'restrictOutboundNetworking' : require(__dirname + '/plugins/azure/sqlserver/restrictOutboundNetworking.js'), + 'restrictOutboundNetworking' : require(__dirname + '/plugins/azure/sqlserver/restrictOutboundNetworking.js'), 'auditOperationsEnabled' : require(__dirname + '/plugins/azure/sqlserver/auditOperationsEnabled.js'), 'serverConnectionPolicy' : require(__dirname + '/plugins/azure/sqlserver/serverConnectionPolicy.js'), 'auditStorageAuthType' : require(__dirname + '/plugins/azure/sqlserver/auditStorageAuthType.js'), @@ -1046,7 +1033,7 @@ module.exports = { 'dbLedgerEnabled' : require(__dirname + '/plugins/azure/sqldatabases/dbLedgerEnabled.js'), 'dbEnableSecureEnclaves' : require(__dirname + '/plugins/azure/sqldatabases/dbEnableSecureEnclaves.js'), 'dbDataDiscoveryClassification' : require(__dirname + '/plugins/azure/sqldatabases/dbDataDiscoveryClassification.js'), - + 'lbHttpsOnly' : require(__dirname + '/plugins/azure/loadbalancer/lbHttpsOnly.js'), 'lbNoInstances' : require(__dirname + '/plugins/azure/loadbalancer/lbNoInstances.js'), 'lbHasTags' : require(__dirname + '/plugins/azure/loadbalancer/lbHasTags.js'), @@ -1078,7 +1065,7 @@ module.exports = { 'cosmosdbHasTags' : require(__dirname + '/plugins/azure/cosmosdb/cosmosdbHasTags.js'), 'cosmosdbManagedIdentity' : require(__dirname + '/plugins/azure/cosmosdb/cosmosdbManagedIdentity.js'), 'cosmosdbLocalAuth' : require(__dirname + '/plugins/azure/cosmosdb/cosmosdbLocalAuth.js'), - + 'checkAdvisorRecommendations' : require(__dirname + '/plugins/azure/advisor/checkAdvisorRecommendations.js'), 'enableDefenderForStorage' : require(__dirname + '/plugins/azure/defender/enableDefenderForStorage.js'), @@ -1095,27 +1082,12 @@ module.exports = { 'enableDefenderForAPIs' : require(__dirname + '/plugins/azure/defender/enableDefenderForAPIs.js'), 'enableDefenderForCosmosDB' : require(__dirname + '/plugins/azure/defender/enableDefenderForCosmosDB.js'), 'enableDefenderForSqlServersVMs': require(__dirname + '/plugins/azure/defender/enableDefenderForSqlServersVMs.js'), - 'highSeverityAlertsEnabled' : require(__dirname + '/plugins/azure/defender/highSeverityAlertsEnabled.js'), - 'standardPricingEnabled' : require(__dirname + '/plugins/azure/defender/standardPricingEnabled.js'), - 'monitorExternalAccounts' : require(__dirname + '/plugins/azure/defender/monitorExternalAccounts.js'), - 'monitorIpForwarding' : require(__dirname + '/plugins/azure/defender/monitorIpForwarding.js'), - 'monitorNextGenerationFirewall' : require(__dirname + '/plugins/azure/defender/monitorNextGenerationFirewall.js'), - 'monitorSubscriptionOwners' : require(__dirname + '/plugins/azure/defender/monitorSubscriptionOwners.js'), - 'securityContactAdditionalEmail': require(__dirname + '/plugins/azure/defender/securityContactAdditionalEmail.js'), - 'securityContactRoleSetToOwner' : require(__dirname + '/plugins/azure/defender/securityContactRoleSetToOwner.js'), - 'appWhitelistingEnabled' : require(__dirname + '/plugins/azure/defender/appWhitelistingEnabled.js'), - 'securityConfigMonitoring' : require(__dirname + '/plugins/azure/defender/securityConfigMonitoring.js'), - 'autoProvisioningEnabled' : require(__dirname + '/plugins/azure/defender/autoProvisioningEnabled.js'), - 'monitorSystemUpdates' : require(__dirname + '/plugins/azure/defender/monitorSystemUpdates.js'), - 'monitorEndpointProtection' : require(__dirname + '/plugins/azure/defender/monitorEndpointProtection.js'), - 'monitorJitNetworkAccess' : require(__dirname + '/plugins/azure/defender/monitorJitNetworkAccess.js'), - 'securityContactsEnabled' : require(__dirname + '/plugins/azure/defender/securityContactsEnabled.js'), 'agWafEnabled' : require(__dirname + '/plugins/azure/applicationGateway/agWafEnabled'), 'applicationGatewayHasTags' : require(__dirname + '/plugins/azure/applicationGateway/applicationGatewayHasTags.js'), 'agSecurityLoggingEnabled' : require(__dirname + '/plugins/azure/applicationGateway/agSecurityLoggingEnabled.js'), 'agSslPolicy' : require(__dirname + '/plugins/azure/applicationGateway/agSslPolicy'), - 'agPreventionModeEnabled' : require(__dirname + '/plugins/azure/applicationGateway/agPreventionModeEnabled.js'), + 'agPreventionModeEnabled' : require(__dirname + '/plugins/azure/applicationGateway/agPreventionModeEnabled.js'), 'agRequestBodyInspection' : require(__dirname + '/plugins/azure/applicationGateway/agRequestBodyInspection'), 'agRequestBodySize' : require(__dirname + '/plugins/azure/applicationGateway/agRequestBodySize.js'), 'agHttpsListenerOnly' : require(__dirname + '/plugins/azure/applicationGateway/agHttpsListenerOnly.js'), @@ -1137,7 +1109,6 @@ module.exports = { 'eventHubMinimumTLSversion' : require(__dirname + '/plugins/azure/eventhub/eventHubMinimumTLSversion.js'), 'eventHubNamespaceHasTags' : require(__dirname + '/plugins/azure/eventhub/eventHubNamespaceHasTags.js'), - 'eventHubNamespaceAutoInflate' : require(__dirname + '/plugins/azure/eventhub/eventHubNamespaceAutoInflate.js'), 'eventHubLocalAuthDisabled' : require(__dirname + '/plugins/azure/eventhub/eventHubLocalAuthDisabled.js'), 'eventHubPublicAccess' : require(__dirname + '/plugins/azure/eventhub/eventHubPublicAccess.js'), 'eventHubNamespaceCmkEncrypted' : require(__dirname + '/plugins/azure/eventhub/eventHubNamespaceCmkEncrypted.js'), @@ -1165,7 +1136,7 @@ module.exports = { 'namespaceLoggingEnabled' : require(__dirname + '/plugins/azure/servicebus/namespaceLoggingEnabled.js'), 'namespacePublicAccess' : require(__dirname + '/plugins/azure/servicebus/namespacePublicAccess.js'), 'namespaceInfraEncryption' : require(__dirname + '/plugins/azure/servicebus/namespaceInfraEncryption.js'), - + 'amsStorageAccountIdentity' : require(__dirname + '/plugins/azure/mediaServices/amsStorageAccountIdentity.js'), 'amsDiagnosticLogsEnabled' : require(__dirname + '/plugins/azure/mediaServices/amsDiagnosticLogsEnabled.js'), 'amsPublicAccessDisabled' : require(__dirname + '/plugins/azure/mediaServices/amsPublicAccessDisabled.js'), @@ -1190,7 +1161,7 @@ module.exports = { 'healthMonitoringExtensionHttps': require(__dirname + '/plugins/azure/virtualmachinescaleset/healthMonitoringExtensionHttps.js'), 'vmssBootDiagnosticsEnabled' : require(__dirname + '/plugins/azure/virtualmachinescaleset/vmssBootDiagnosticsEnabled'), 'vmssWindowsAntiMalwareExt' : require(__dirname + '/plugins/azure/virtualmachinescaleset/vmssWindowsAntiMalwareExt'), - + 'appConfigManagedIdentity' : require(__dirname + '/plugins/azure/appConfigurations/appConfigManagedIdentity.js'), 'appConfigurationDiagnosticLogs': require(__dirname + '/plugins/azure/appConfigurations/appConfigurationDiagnosticLogs.js'), 'appConfigurationPublicAccess' : require(__dirname + '/plugins/azure/appConfigurations/appConfigurationPublicAccess.js'), @@ -1200,7 +1171,7 @@ module.exports = { 'automationAcctDiagnosticLogs' : require(__dirname + '/plugins/azure/automationAccounts/automationAcctDiagnosticLogs.js'), 'automationAcctManagedIdentity' : require(__dirname + '/plugins/azure/automationAccounts/automationAcctManagedIdentity.js'), - 'automationAcctApprovedCerts' : require(__dirname + '/plugins/azure/automationAccounts/automationAcctApprovedCerts.js'), + 'automationAcctApprovedCerts' : require(__dirname + '/plugins/azure/automationAccounts/automationAcctApprovedCerts.js'), 'automationAcctEncryptedVars' : require(__dirname + '/plugins/azure/automationAccounts/automationAcctEncryptedVars.js'), 'automationAcctPublicAccess' : require(__dirname + '/plugins/azure/automationAccounts/automationAcctPublicAccess.js'), 'automationAcctExpiredWebhooks' : require(__dirname + '/plugins/azure/automationAccounts/automationAcctExpiredWebhooks.js'), @@ -1210,10 +1181,6 @@ module.exports = { 'batchAccountCmkEncrypted' : require(__dirname + '/plugins/azure/batchAccounts/batchAccountCmkEncrypted.js'), 'batchAccountDiagnosticLogs' : require(__dirname + '/plugins/azure/batchAccounts/batchAccountDiagnosticLogs.js'), - 'batchAccountsAADEnabled' : require(__dirname + '/plugins/azure/batchAccounts/batchAccountsAADEnabled.js'), - 'batchAccountsHasTags' : require(__dirname + '/plugins/azure/batchAccounts/batchAccountsHasTags.js'), - 'batchAccountsPublicAccess' : require(__dirname + '/plugins/azure/batchAccounts/batchAccountsPublicAccess.js'), - 'batchAccountsManagedIdentity' : require(__dirname + '/plugins/azure/batchAccounts/batchAccountsManagedIdentity.js'), 'accountCMKEncrypted' : require(__dirname + '/plugins/azure/openai/accountCMKEncrypted.js'), 'accountManagedIdentity' : require(__dirname + '/plugins/azure/openai/accountManagedIdentity.js'), @@ -1228,16 +1195,6 @@ module.exports = { 'workspaceManagedDiskCmk' : require(__dirname + '/plugins/azure/databricks/workspaceManagedDiskCmk.js'), 'workspaceHasTags' : require(__dirname + '/plugins/azure/databricks/workspaceHasTags.js'), - 'workspaceManagedIdentity' : require(__dirname + '/plugins/azure/synapse/workspaceManagedIdentity.js'), - 'synapseWorkspaceAdAuthEnabled' : require(__dirname + '/plugins/azure/synapse/synapseWorkspaceAdAuthEnabled.js'), - 'synapseWorkspacPrivateEndpoint': require(__dirname + '/plugins/azure/synapse/synapseWorkspacPrivateEndpoint.js'), - 'synapseWorkspaceHasTags' : require(__dirname + '/plugins/azure/synapse/synapseWorkspaceHasTags.js'), - 'workspaceDiagnosticLogsEnabled': require(__dirname + '/plugins/azure/synapse/workspaceDiagnosticLogsEnabled.js'), - 'workspaceDoubleEncryption' : require(__dirname + '/plugins/azure/synapse/workspaceDoubleEncryption.js'), - - 'apiInstanceManagedIdentity' : require(__dirname + '/plugins/azure/apiManagement/apiInstanceManagedIdentity.js'), - 'apiInstanceHasTags' : require(__dirname + '/plugins/azure/apiManagement/apiInstanceHasTags.js'), - }, github: { 'publicKeysRotated' : require(__dirname + '/plugins/github/users/publicKeysRotated.js'), @@ -1453,7 +1410,6 @@ module.exports = { 'confidentialComputingEnabled' : require(__dirname + '/plugins/google/compute/confidentialComputingEnabled.js'), 'imagesCMKEncrypted' : require(__dirname + '/plugins/google/compute/imagesCMKEncrypted.js'), 'snapshotEncryption' : require(__dirname + '/plugins/google/compute/snapshotEncryption.js'), - 'instanceNetworkExposure' : require(__dirname + '/plugins/google/compute/instanceNetworkExposure.js'), 'keyRotation' : require(__dirname + '/plugins/google/cryptographickeys/keyRotation.js'), 'keyProtectionLevel' : require(__dirname + '/plugins/google/cryptographickeys/keyProtectionLevel.js'), @@ -1596,10 +1552,8 @@ module.exports = { 'instanceNodeCount' : require(__dirname + '/plugins/google/spanner/instanceNodeCount.js'), 'httpTriggerRequireHttps' : require(__dirname + '/plugins/google/cloudfunctions/httpTriggerRequireHttps.js'), - 'functionDefaultServiceAccount' : require(__dirname + '/plugins/google/cloudfunctions/functionDefaultServiceAccount.js'), 'ingressAllTrafficDisabled' : require(__dirname + '/plugins/google/cloudfunctions/ingressAllTrafficDisabled.js'), 'cloudFunctionLabelsAdded' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionLabelsAdded.js'), - 'cloudFunctionOldRuntime' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionOldRuntime.js'), 'functionAllUsersPolicy' : require(__dirname + '/plugins/google/cloudfunctions/functionAllUsersPolicy.js'), 'serverlessVPCAccess' : require(__dirname + '/plugins/google/cloudfunctions/serverlessVPCAccess.js'), diff --git a/helpers/asl/asl-1.js b/helpers/asl/asl-1.js index 06bfed0541..55a68c39cc 100644 --- a/helpers/asl/asl-1.js +++ b/helpers/asl/asl-1.js @@ -191,30 +191,6 @@ var validate = function(condition, conditionResult, inputResultsArr, message, pr message.push(`${condition.parsed} is not the right property type for this operation`); return 2; } - } else if (condition.op == 'NOTCONTAINS') { - var conditionStringified = JSON.stringify(condition.parsed); - if (condition.value && condition.value.includes(':')) { - - var conditionKey = condition.value.split(/:(?!.*:)/)[0]; - var conditionValue = condition.value.split(/:(?!.*:)/)[1]; - - if (conditionStringified.includes(conditionKey) && !conditionStringified.includes(conditionValue)){ - message.push(`${property}: ${condition.value} not found in ${conditionStringified}`); - return 0; - } else { - message.push(`${condition.value} found in ${conditionStringified}`); - return 2; - } - } else if (conditionStringified && !conditionStringified.includes(condition.value)) { - message.push(`${property}: ${condition.value} not found in ${conditionStringified}`); - return 0; - } else if (conditionStringified && conditionStringified.length){ - message.push(`${condition.value} found in ${conditionStringified}`); - return 2; - } else { - message.push(`${condition.parsed} is not the right property type for this operation`); - return 2; - } } else { // Recurse into the same function var subProcessed = []; @@ -304,17 +280,6 @@ var validate = function(condition, conditionResult, inputResultsArr, message, pr message.push(`${condition.parsed} is not the right property type for this operation`); return 2; } - } else if (condition.op == 'NOTCONTAINS') { - if (condition.parsed && condition.parsed.length && !condition.parsed.includes(condition.value)) { - message.push(`${property}: ${condition.value} not found in ${condition.parsed}`); - return 0; - } else if (condition.parsed && condition.parsed.length){ - message.push(`${condition.value} found in ${condition.parsed}`); - return 2; - } else { - message.push(`${condition.parsed} is not the right property type for this operation`); - return 2; - } } return conditionResult; } @@ -366,7 +331,7 @@ var runValidation = function(obj, condition, inputResultsArr, nestedResultArr) { propertyArr.shift(); property = propertyArr.join('.'); condition.property = property; - if (condition.op !== 'CONTAINS' || condition.op !== 'NOTCONTAINS') { + if (condition.op !== 'CONTAINS') { condition.parsed.forEach(parsed => { if (property.includes('[*]')) { runValidation(parsed, condition, inputResultsArr, nestedResultArr); @@ -602,4 +567,4 @@ var asl = function(source, input, resourceMap, cloud, accountId, callback) { callback(null, results, data); }; -module.exports = asl; +module.exports = asl; \ No newline at end of file diff --git a/helpers/aws/api.js b/helpers/aws/api.js index a0227ae405..b28dff1cbd 100644 --- a/helpers/aws/api.js +++ b/helpers/aws/api.js @@ -594,12 +594,6 @@ var calls = { paginate: 'NextToken' } }, - ApiGatewayV2: { - getApis: { - property: 'Items', - paginate: 'NextToken' - }, - }, AppConfig: { listApplications: { property: 'Items', @@ -1559,27 +1553,6 @@ var calls = { describeHub: { property:'', paginate: 'NextToken' - }, - getFindings: { - property: 'Findings', - paginate: 'NextToken', - params: { - MaxResults: 100, - Filters: { - RecordState: [ - { - Comparison: 'EQUALS', - Value: 'ACTIVE' - } - ], - WorkflowStatus: [ - { - Comparison: 'EQUALS', - Value: 'NEW' - } - ] - } - } } }, SageMaker: { @@ -1869,26 +1842,6 @@ var postcalls = [ reliesOnCall: 'getRestApis', filterKey: 'restApiId', filterValue: 'id' - }, - getRequestValidators: { - reliesOnService: 'apigateway', - reliesOnCall: 'getRestApis', - filterKey: 'restApiId', - filterValue: 'id' - } - }, - ApiGatewayV2: { - getStages: { - reliesOnService: 'apigatewayv2', - reliesOnCall: 'getApis', - filterKey: 'ApiId', - filterValue: 'ApiId' - }, - getAuthorizers: { - reliesOnService: 'apigatewayv2', - reliesOnCall: 'getApis', - filterKey: 'ApiId', - filterValue: 'ApiId' } }, AppConfig: { @@ -2127,6 +2080,7 @@ var postcalls = [ filterKey: 'id', filterValue: 'projectId' }, + sendIntegration: serviceMap['CodeStar'] }, CustomerProfiles: { getDomain: { @@ -2192,7 +2146,7 @@ var postcalls = [ reliesOnService: 'docdb', reliesOnCall: 'describeDBClusters', filterKey: 'ResourceName', - filterValue: 'DBClusterArn' + filterValue: 'DBClusterArn' }, sendIntegration: serviceMap['DocumentDB'] }, @@ -3032,7 +2986,8 @@ var postcalls = [ getRole: { reliesOnService: 'iam', reliesOnCall: 'listRoles', - override: true + filterKey: 'RoleName', + filterValue: 'RoleName' }, getUser: { reliesOnService: 'iam', diff --git a/helpers/aws/api_multipart.js b/helpers/aws/api_multipart.js index 4590b8ffb0..9389518a67 100644 --- a/helpers/aws/api_multipart.js +++ b/helpers/aws/api_multipart.js @@ -1044,27 +1044,6 @@ var calls = [ describeHub: { property: '', paginate: 'NextToken' - }, - getFindings: { - property: 'Findings', - paginate: 'NextToken', - params: { - MaxResults: 100, - Filters: { - RecordState: [ - { - Comparison: 'EQUALS', - Value: 'ACTIVE' - } - ], - WorkflowStatus: [ - { - Comparison: 'EQUALS', - Value: 'NEW' - } - ] - } - } } }, Transfer: { @@ -1220,12 +1199,6 @@ var postcalls = [ reliesOnCall: 'getRestApis', filterKey: 'restApiId', filterValue: 'id' - }, - getRequestValidators: { - reliesOnService: 'apigateway', - reliesOnCall: 'getRestApis', - filterKey: 'restApiId', - filterValue: 'id' } }, AppConfig: { @@ -1442,7 +1415,7 @@ var postcalls = [ reliesOnService: 'docdb', reliesOnCall: 'describeDBClusters', filterKey: 'ResourceName', - filterValue: 'DBClusterArn' + filterValue: 'DBClusterArn' }, }, DynamoDB: { @@ -2341,7 +2314,8 @@ var postcalls = [ getRole: { reliesOnService: 'iam', reliesOnCall: 'listRoles', - override: true, + filterKey: 'RoleName', + filterValue: 'RoleName', rateLimit: 500 }, listRolePolicies: { diff --git a/helpers/aws/functions.js b/helpers/aws/functions.js index 3b66ac6acf..b681ed5743 100644 --- a/helpers/aws/functions.js +++ b/helpers/aws/functions.js @@ -174,18 +174,15 @@ function findOpenPorts(groups, ports, service, region, results, cache, config, c } } } - + return; } -function checkNetworkInterface(groupId, groupName, resultsString, region, results, resource, cache, bool = false) { +function checkNetworkInterface(groupId, groupName, resultsString, region, results, resource, cache) { const describeNetworkInterfaces = helpers.addSource(cache, {}, ['ec2', 'describeNetworkInterfaces', region]); if (!describeNetworkInterfaces || describeNetworkInterfaces.err || !describeNetworkInterfaces.data) { - if (bool) { - return false; - } helpers.addResult(results, 3, 'Unable to query for network interfaces: ' + helpers.addError(describeNetworkInterfaces), region); return; @@ -201,28 +198,20 @@ function checkNetworkInterface(groupId, groupName, resultsString, region, result } } } - if (bool && !networksWithSecurityGroup.length) { - return groupId; - } - let exposedENI; if (hasOpenSecurityGroup) { let hasPublicIp = false; for (var eni of networksWithSecurityGroup) { if (eni.Association && eni.Association.PublicIp) { hasPublicIp = true; - exposedENI = `sg ${groupId} > eni ${eni.NetworkInterfaceId}`; break; } } if (hasPublicIp) { - if (bool) return exposedENI; addResult(results, 2, `Security Group ${groupId}(${groupName}) is associated with an ENI that is publicly exposed`, region, resource); } else { - if (bool) return false; addResult(results, 0, `Security Group ${groupId} (${groupName}) is only exposed internally`, region, resource); } } else { - if (bool) return false; addResult(results, 2, resultsString, region, resource); } } @@ -302,7 +291,7 @@ function userGlobalAccess(statement, restrictedPermissions) { statement.Action && restrictedPermissions.some(permission=> statement.Action.includes(permission))) { return true; } - + return false; } @@ -335,7 +324,7 @@ function crossAccountPrincipal(principal, accountId, fetchPrincipals, settings={ } function hasFederatedUserRole(policyDocument) { - // true iff every statement refers to federated user access + // true iff every statement refers to federated user access for (let statement of policyDocument) { if (statement.Action && !statement.Action.includes('sts:AssumeRoleWithSAML') && @@ -350,13 +339,13 @@ function extractStatementPrincipals(statement) { let response = []; if (statement.Principal) { let principal = statement.Principal; - + if (typeof principal === 'string') { return [principal]; } if (!principal.AWS) return response; - + var awsPrincipals = principal.AWS; if (!Array.isArray(awsPrincipals)) { awsPrincipals = [awsPrincipals]; @@ -527,7 +516,7 @@ function getS3BucketLocation(cache, region, bucketName) { if (getBucketLocation.data.LocationConstraint && regions.all.includes(getBucketLocation.data.LocationConstraint)) return getBucketLocation.data.LocationConstraint; else if (getBucketLocation.data.LocationConstraint && - !regions.all.includes(getBucketLocation.data.LocationConstraint)) return 'global'; + !regions.all.includes(getBucketLocation.data.LocationConstraint)) return 'global'; else return 'us-east-1'; } @@ -904,7 +893,7 @@ function getOrganizationAccounts(listAccounts, accountId) { if (listAccounts.data && listAccounts.data.length){ listAccounts.data.forEach(account => { if (account.Id && account.Id !== accountId) orgAccountIds.push(account.Id); - }); + }); } return orgAccountIds; @@ -914,7 +903,7 @@ function getUsedSecurityGroups(cache, results, region) { let result = []; const describeNetworkInterfaces = helpers.addSource(cache, {}, ['ec2', 'describeNetworkInterfaces', region]); - + if (!describeNetworkInterfaces || describeNetworkInterfaces.err || !describeNetworkInterfaces.data) { helpers.addResult(results, 3, 'Unable to query for network interfaces: ' + helpers.addError(describeNetworkInterfaces), region); @@ -923,7 +912,7 @@ function getUsedSecurityGroups(cache, results, region) { const listFunctions = helpers.addSource(cache, {}, ['lambda', 'listFunctions', region]); - + if (!listFunctions || listFunctions.err || !listFunctions.data) { helpers.addResult(results, 3, 'Unable to list lambda functions: ' + helpers.addError(listFunctions), region); @@ -978,7 +967,7 @@ function getSubnetRTMap(subnets, routeTables) { }); } if (routeTable.VpcId && routeTable.RouteTableId && routeTable.Associations && - routeTable.Associations.find(association => association.Main) && !vpcRTMap[routeTable.VpcId]) vpcRTMap[routeTable.VpcId] = routeTable.RouteTableId; + routeTable.Associations.find(association => association.Main) && !vpcRTMap[routeTable.VpcId]) vpcRTMap[routeTable.VpcId] = routeTable.RouteTableId; }); subnets.forEach(subnet => { @@ -1034,7 +1023,6 @@ var debugApiCalls = function(call, service, debugMode, finished) { }; var logError = function(service, call, region, err, errorsLocal, apiCallErrorsLocal, apiCallTypeErrorsLocal, totalApiCallErrorsLocal, errorSummaryLocal, errorTypeSummaryLocal, debugMode) { - if (debugMode) console.log(`[INFO] ${service}:${call} returned error: ${err.message}`); totalApiCallErrorsLocal++; if (!errorSummaryLocal[service]) errorSummaryLocal[service] = {}; @@ -1124,7 +1112,7 @@ function processFieldSelectors(fieldSelectors,buckets ,startsWithBuckets,notEnds var checkTags = function(cache, resourceName, resourceList, region, results, settings={}) { const allResources = helpers.addSource(cache, {}, ['resourcegroupstaggingapi', 'getResources', region]); - + if (!allResources || allResources.err || !allResources.data) { helpers.addResult(results, 3, 'Unable to query all resources from group tagging api:' + helpers.addError(allResources), region); @@ -1140,7 +1128,7 @@ var checkTags = function(cache, resourceName, resourceList, region, results, set }); resourceList.map(arn => { - if (filteredResourceARN.includes(arn)) { + if (filteredResourceARN.includes(arn)) { helpers.addResult(results, 0, `${resourceName} has tags`, region, arn); } else { helpers.addResult(results, 2, `${resourceName} does not have any tags`, region, arn); @@ -1148,134 +1136,6 @@ var checkTags = function(cache, resourceName, resourceList, region, results, set }); }; -function checkSecurityGroup(securityGroup, cache, region) { - let allowsAllTraffic; - for (var p in securityGroup.IpPermissions) { - var permission = securityGroup.IpPermissions[p]; - - for (var k in permission.IpRanges) { - var range = permission.IpRanges[k]; - - if (range.CidrIp === '0.0.0.0/0') { - allowsAllTraffic = true; - } - } - - for (var l in permission.Ipv6Ranges) { - var rangeV6 = permission.Ipv6Ranges[l]; - - if (rangeV6.CidrIpv6 === '::/0') { - allowsAllTraffic = true; - } - } - } - - if (allowsAllTraffic) { - return checkNetworkInterface(securityGroup.GroupId, securityGroup.GroupName, '', region, null, securityGroup, cache, true); - } - return false; -} - -var checkNetworkExposure = function(cache, source, subnetId, securityGroups, region, results) { - - var internetExposed = ''; - - // Scenario 1: check if resource is in a private subnet - let subnetRouteTableMap, privateSubnets; - var describeSubnets = helpers.addSource(cache, source, - ['ec2', 'describeSubnets', region]); - var describeRouteTables = helpers.addSource(cache, {}, - ['ec2', 'describeRouteTables', region]); - - if (!describeRouteTables || describeRouteTables.err || !describeRouteTables.data) { - helpers.addResult(results, 3, - 'Unable to query for route tables: ' + helpers.addError(describeRouteTables), region); - } else if (!describeSubnets || describeSubnets.err || !describeSubnets.data) { - helpers.addResult(results, 3, - 'Unable to query for subnets: ' + helpers.addError(describeSubnets), region); - } else if (describeSubnets.data.length && subnetId) { - subnetRouteTableMap = getSubnetRTMap(describeSubnets.data, describeRouteTables.data); - privateSubnets = getPrivateSubnets(subnetRouteTableMap, describeSubnets.data, describeRouteTables.data); - if (privateSubnets && privateSubnets.length && privateSubnets.find(subnet => subnet === subnetId)) { - return ''; - } - // If the subnet is not private we will check if security groups and Network ACLs allow internal traffic - } - - // Scenario 2: check if security group allows all traffic - var describeSecurityGroups = helpers.addSource(cache, source, - ['ec2', 'describeSecurityGroups', region]); - - - if (!describeSecurityGroups || describeSecurityGroups.err || !describeSecurityGroups.data) { - helpers.addResult(results, 3, - 'Unable to query for security groups: ' + helpers.addError(describeSecurityGroups), region); - } else if (describeSecurityGroups.data.length && securityGroups && securityGroups.length) { - let instanceSGs = describeSecurityGroups.data.filter(sg => securityGroups.find(isg => isg.GroupId === sg.GroupId)); - for (var group of instanceSGs) { - let exposedSG = checkSecurityGroup(group, cache, region); - if (!exposedSG) { - return ''; - } else { - internetExposed += exposedSG; - } - } - } - - - - // Scenario 3: check if Network ACLs associated with the resource allow all traffic - var describeNetworkAcls = helpers.addSource(cache, source, - ['ec2', 'describeNetworkAcls', region]); - - if (!describeNetworkAcls || describeNetworkAcls.err || !describeNetworkAcls.data) { - helpers.addResult(results, 3, - `Unable to query for Network ACLs: ${helpers.addError(describeNetworkAcls)}`, region); - } else if (describeNetworkAcls.data.length && subnetId) { - let instanceACL = describeNetworkAcls.data.find(acl => acl.Associations.find(assoc => assoc.SubnetId === subnetId)); - if (instanceACL && instanceACL.Entries && instanceACL.Entries.length) { - - const allowRules = instanceACL.Entries.filter(entry => - entry.Egress === false && - entry.RuleAction === 'allow' && - (entry.CidrBlock === '0.0.0.0/0' || entry.Ipv6CidrBlock === '::/0') - ); - - - // Checking if there's a deny rule with lower rule number - let exposed = allowRules.some(allowRule => { - // Check if there's a deny with a lower rule number - return !instanceACL.Entries.some(denyRule => { - return ( - denyRule.Egress === false && - denyRule.RuleAction === 'deny' && - ( - (allowRule.CidrBlock && denyRule.CidrBlock === allowRule.CidrBlock) || - (allowRule.Ipv6CidrBlock && denyRule.Ipv6CidrBlock === allowRule.Ipv6CidrBlock) - ) && - denyRule.Protocol === allowRule.Protocol && - ( - denyRule.PortRange ? - (allowRule.PortRange && - denyRule.PortRange.From === allowRule.PortRange.From && - denyRule.PortRange.To === allowRule.PortRange.To) : true - ) && - denyRule.RuleNumber < allowRule.RuleNumber - ); - }); - }); - if (exposed) { - internetExposed += `> nacl ${instanceACL.NetworkAclId}`; - } else { - internetExposed = ''; - } - } - - } - - return internetExposed; -}; - module.exports = { addResult: addResult, findOpenPorts: findOpenPorts, @@ -1313,6 +1173,4 @@ module.exports = { checkConditions: checkConditions, processFieldSelectors: processFieldSelectors, checkNetworkInterface: checkNetworkInterface, - checkNetworkExposure: checkNetworkExposure, -}; - +}; \ No newline at end of file diff --git a/helpers/aws/regions.js b/helpers/aws/regions.js index a1590babab..075f1d8a3e 100644 --- a/helpers/aws/regions.js +++ b/helpers/aws/regions.js @@ -42,7 +42,6 @@ module.exports = { accessanalyzer: [...regions, ...newRegionsUpdate], acm: [...regions, ...newRegionsUpdate], apigateway: [...regions, ...newRegionsUpdate], - apigatewayv2: [...regions, ...newRegionsUpdate], athena:[...regions, ...newRegionsUpdate], bedrock: ['us-east-1', 'us-west-2', 'ap-south-1','ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'eu-central-1', 'eu-west-1', 'eu-west-3'], cloudfront: ['us-east-1'], // CloudFront uses the default global region diff --git a/helpers/aws/regions_china.js b/helpers/aws/regions_china.js index 5a7e38a712..b3d5984374 100644 --- a/helpers/aws/regions_china.js +++ b/helpers/aws/regions_china.js @@ -13,7 +13,6 @@ module.exports = { cognitoidentityserviceprovider: regions, acm: [], apigateway: regions, - apigatewayv2: regions, athena: [], bedrock:[], cloudfront: [], diff --git a/helpers/aws/regions_gov.js b/helpers/aws/regions_gov.js index df2b454615..856fb5109b 100644 --- a/helpers/aws/regions_gov.js +++ b/helpers/aws/regions_gov.js @@ -12,7 +12,6 @@ module.exports = { accessanalyzer: regions, acm: regions, apigateway: regions, - apigatewayv2: regions, athena: regions, backup: regions, bedrock: ['us-gov-west-1'], diff --git a/helpers/azure/api.js b/helpers/azure/api.js index 8b77b76ff1..150b7491a2 100644 --- a/helpers/azure/api.js +++ b/helpers/azure/api.js @@ -380,11 +380,7 @@ var calls = { machineLearning: { listWorkspaces: { url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.MachineLearningServices/workspaces?api-version=2024-04-01' - }, - listRegistries: { - url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.MachineLearningServices/registries?api-version=2024-04-01' - }, - + } }, loadBalancers: { listAll: { @@ -534,11 +530,6 @@ var calls = { url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Databricks/workspaces?api-version=2023-02-01' } }, - apiManagementService: { - list: { - url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.ApiManagement/service?api-version=2022-08-01' - } - }, // For CIEM aad: { listRoleAssignments: { @@ -573,11 +564,6 @@ var calls = { } }, - synapse: { - listWorkspaces: { - url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Synapse/workspaces?api-version=2021-06-01' - } - } }; @@ -1275,16 +1261,6 @@ var tertiarycalls = { reliesOnPath: 'batchAccounts.list', properties: ['id'], url: 'https://management.azure.com/{id}/providers/microsoft.insights/diagnosticSettings?api-version=2021-05-01-preview' - }, - listByMysqlFlexibleServer: { - reliesOnPath: 'servers.listMysqlFlexibleServer', - properties: ['id'], - url: 'https://management.azure.com/{id}/providers/microsoft.insights/diagnosticSettings?api-version=2021-05-01-preview' - }, - listByWorkspaces: { - reliesOnPath: 'synapse.listWorkspaces', - properties: ['id'], - url: 'https://management.azure.com/{id}/providers/microsoft.insights/diagnosticSettings?api-version=2021-05-01-preview' } }, backupShortTermRetentionPolicies: { diff --git a/helpers/azure/auth.js b/helpers/azure/auth.js index fb491d8dc5..21aa2d44e7 100644 --- a/helpers/azure/auth.js +++ b/helpers/azure/auth.js @@ -1,6 +1,5 @@ var request = require('request'); var locations = require(__dirname + '/locations.js'); -var locations_gov = require(__dirname + '/locations_gov.js'); var dontReplace = { 'type': { @@ -59,39 +58,22 @@ module.exports = { }); } - if (azureConfig.Govcloud) { - performLogin({ environment: msRestAzure.AzureEnvironment.AzureUSGovernment }, function(err, credentials) { - if (err) return callback(err); - performLogin({ tokenAudience: 'https://graph.microsoft.us', environment: msRestAzure.AzureEnvironment.AzureUSGovernment }, function(graphErr, graphCredentials) { - if (graphErr) return callback(graphErr); - performLogin({ tokenAudience: 'https://vault.azure.us', environment: msRestAzure.AzureEnvironment.AzureUSGovernment }, function(vaultErr, vaultCredentials) { - if (vaultErr) console.log('No vault'); - callback(null, { - environment: credentials.environment, - token: credentials.tokenCache._entries[0].accessToken, - graphToken: graphCredentials ? graphCredentials.tokenCache._entries[0].accessToken : null, - vaultToken: vaultCredentials ? vaultCredentials.tokenCache._entries[0].accessToken : null - }); + // First, login without audience + performLogin(null, function(err, credentials) { + if (err) return callback(err); + performLogin({ tokenAudience: 'https://graph.microsoft.com' }, function(graphErr, graphCredentials) { + if (graphErr) return callback(graphErr); + performLogin({ tokenAudience: 'https://vault.azure.net' }, function(vaultErr, vaultCredentials) { + if (vaultErr) return callback(vaultErr); + callback(null, { + environment: credentials.environment, + token: credentials.tokenCache._entries[0].accessToken, + graphToken: graphCredentials.tokenCache._entries[0].accessToken, + vaultToken: vaultCredentials.tokenCache._entries[0].accessToken }); }); }); - } else { - performLogin(null, function(err, credentials) { - if (err) return callback(err); - performLogin({ tokenAudience: 'https://graph.microsoft.com' }, function(graphErr, graphCredentials) { - if (graphErr) return callback(graphErr); - performLogin({ tokenAudience: 'https://vault.azure.net' }, function(vaultErr, vaultCredentials) { - if (vaultErr) return callback(vaultErr); - callback(null, { - environment: credentials.environment, - token: credentials.tokenCache._entries[0].accessToken, - graphToken: graphCredentials.tokenCache._entries[0].accessToken, - vaultToken: vaultCredentials.tokenCache._entries[0].accessToken - }); - }); - }); - }); - } + }); }, call: function(params, callback) { @@ -104,9 +86,6 @@ module.exports = { headers['Content-Type'] = 'application/json;charset=UTF-8'; } - if (params.govcloud) params.url = params.url.replace('management.azure.com', 'management.usgovcloudapi.net'); - - request({ method: params.method ? params.method : params.post ? 'POST' : 'GET', uri: params.url, @@ -176,7 +155,7 @@ module.exports = { }); }, - addLocations: function(obj, service, collection, err, data, skip_locations) { + addLocations: function(obj, service, collection, err, data , skip_locations) { if (!service || !locations[service]) return; locations[service].forEach(function(location) { if (skip_locations.includes(location)) return; @@ -199,29 +178,6 @@ module.exports = { } }); }, - addGovLocations: function(obj, service, collection, err, data , skip_locations) { - if (!service || !locations_gov[service]) return; - locations_gov[service].forEach(function(location) { - if (skip_locations.includes(location)) return; - collection[location.toLowerCase()] = {}; - if (err) { - collection[location.toLowerCase()].err = err; - } else if (data) { - if (data.value && Array.isArray(data.value)) { - collection[location.toLowerCase()].data = data.value.filter(function(dv) { - if (dv.location && - dv.location.toLowerCase().replace(/ /g, '') == location.toLowerCase()) { - return true; - } else if (location.toLowerCase() == 'global' && (!dv.location || obj.ignoreLocation)) { - return true; - } - return false; - }); - reduceProperties(service, collection[location.toLowerCase()].data); - } - } - }); - }, reduceProperties: reduceProperties }; \ No newline at end of file diff --git a/helpers/azure/functions.js b/helpers/azure/functions.js index 3fdc0a5652..3b8261074f 100644 --- a/helpers/azure/functions.js +++ b/helpers/azure/functions.js @@ -392,16 +392,16 @@ function checkFlexibleServerConfigs(servers, cache, source, location, results, s } function checkMicrosoftDefender(pricings, serviceName, serviceDisplayName, results, location ) { - + let pricingData = pricings.data.find((pricing) => pricing.name.toLowerCase() === serviceName); if (pricingData) { if (pricingData.pricingTier.toLowerCase() === 'standard') { - addResult(results, 0, `Azure Defender is enabled for ${serviceDisplayName}`, location, pricingData.id); + addResult(results, 0, `Azure Defender is enabled for ${serviceDisplayName}`, location, pricingData.id); } else { - addResult(results, 2, `Azure Defender is not enabled for ${serviceDisplayName}`, location, pricingData.id); + addResult(results, 2, `Azure Defender is not enabled for ${serviceDisplayName}`, location, pricingData.id); } } else { - addResult(results, 2, `Azure Defender is not enabled for ${serviceDisplayName}`, location); + addResult(results, 2, `Azure Defender is not enabled for ${serviceDisplayName}`, location); } } @@ -612,12 +612,12 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache, sourceAddressArr.push(settings.input[inputKey]); sourceAddressArr.splice(sourceAddressArr.indexOf(publicString), 1); - // this if the input specified already exists + // this if the input specified already exists } else if (settings.input && settings.input[inputKey] && sourceAddressArr.indexOf(settings.input[inputKey]) > -1) { ipType === 'ipv4' ? localIpExists = true : localIpV6Exists = true; sourceAddressArr.splice(sourceAddressArr.indexOf(publicString), 1); - // this is if there is no input and the failing port is in an array (destinationPortRanges). Will remove the port from the array + // this is if there is no input and the failing port is in an array (destinationPortRanges). Will remove the port from the array } else if ((!settings.input || !settings.input[inputKey]) && (failingRulePortIndex[failingPermission.name]) && !spliced) { spliced = true; failingPermission.properties['destinationPortRanges'].splice([failingRulePortIndex[failingPermission.name]], 1); @@ -741,56 +741,6 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache, }); } -function checkSecurityGroup(securityGroups) { - var openPrefix = ['*', '0.0.0.0', '0.0.0.0/0', '', '/0', '::/0', 'internet']; - - const allRules = securityGroups.flatMap(nsg => - [ - ...(nsg.securityRules ? nsg.securityRules.map(rule => ({ - ...rule, - nsgName: nsg.name - })) : []), - ...(nsg.defaultSecurityRules ? nsg.defaultSecurityRules.map(rule => ({ - ...rule, - nsgName: nsg.name - })) : []) - ] - ); - - // sorting by priority - const sortedRules = allRules.sort((a, b) => a.properties.priority - b.properties.priority); - - // The most restrictive rule takes precedence - for (const rule of sortedRules) { - if (rule.properties.direction === "Inbound" && openPrefix.includes(rule.properties.sourceAddressPrefix)) { - if (rule.properties.access === "Deny") { - return {exposed: false}; - } - if (rule.properties.access === "Allow") { - return {exposed: true, nsg: rule.nsgName}; - } - } - } - - return {exposed: true}; -} - -function checkNetworkExposure(cache, source, networkInterfaces, securityGroups, region, results) { - let exposedPath = ''; - - if (securityGroups && securityGroups.length) { - // Scenario 1: check if security group allow all inbound traffic - let exposedSG = checkSecurityGroup(securityGroups); - if (exposedSG && exposedSG.exposed) { - if (exposedSG.nsg) { - exposedPath += `nsg ${exposedSG.nsg}` - } - } - - return exposedPath - } -} - module.exports = { addResult: addResult, findOpenPorts: findOpenPorts, @@ -803,7 +753,6 @@ module.exports = { remediateOpenPorts: remediateOpenPorts, remediateOpenPortsHelper: remediateOpenPortsHelper, checkMicrosoftDefender: checkMicrosoftDefender, - checkFlexibleServerConfigs: checkFlexibleServerConfigs, - checkNetworkExposure: checkNetworkExposure, + checkFlexibleServerConfigs:checkFlexibleServerConfigs }; diff --git a/helpers/azure/locations.js b/helpers/azure/locations.js index c376e3ded7..280fb294ea 100644 --- a/helpers/azure/locations.js +++ b/helpers/azure/locations.js @@ -134,7 +134,5 @@ module.exports = { databricks: locations, containerApps: locations, batchAccounts: locations, - machineLearning: locations, - apiManagementService: locations, - synapse: locations + machineLearning: locations }; diff --git a/helpers/azure/locations_gov.js b/helpers/azure/locations_gov.js index 2f71aa31d7..359ce8450e 100644 --- a/helpers/azure/locations_gov.js +++ b/helpers/azure/locations_gov.js @@ -10,81 +10,5 @@ var locations = [ ]; module.exports = { - all: locations, - resources: locations, - storageAccounts: ['global'], - virtualMachines: locations, - snapshots: locations, - disks: locations, - activityLogAlerts: ['global'], - vaults: locations, - policyAssignments: locations.concat(['global']), - recoveryServiceVaults: locations, - backupPolicies: locations, - backupProtectedItems: locations, - webApps: locations, - appServiceCertificates: locations, - networkSecurityGroups: locations, - servers: locations, - logProfiles: ['global'], - profiles: ['global'], - managementLocks: ['global'], - blobServices: locations, - networkWatchers: locations, - networkInterfaces: locations, - managedClusters: locations, - virtualMachineScaleSets: locations, - autoProvisioningSettings: ['global'], - securityContacts: ['global'], - usages: ['global'], - subscriptions: ['global'], - loadBalancers: locations, - availabilitySets: locations, - virtualNetworks: locations, - virtualNetworkPeerings: locations, - virtualNetworkGateways: locations, - networkGatewayConnections: locations, - natGateways: locations, - users: ['global'], - registries: locations, - redisCaches: locations, - pricings: ['global'], - roleDefinitions: ['global'], - aad: ['global'], - groups: ['global'], - servicePrincipals: ['global'], - autoscaleSettings: locations, - resourceGroups: locations, - policyDefinitions: locations, - diagnosticSettingsOperations: ['global'], - databaseAccounts: locations, - securityCenter: ['global'], - advisor: ['global'], - publicIPAddresses: locations, - privateDnsZones: ['global'], - privateEndpoints: locations, - securityContactv2: ['global'], - images: locations, - vmScaleSet: locations, - applicationGateway: locations, - wafPolicies: locations, - routeTables: locations, - bastionHosts: locations, - applications: ['global'], - eventGrid: locations, - eventHub: locations, - mediaServices: locations, - serviceBus: locations, - classicFrontDoors: ['global'], - afdWafPolicies: ['global'], - appConfigurations: locations, - automationAccounts: locations, - openAI: locations, - logAnalytics: locations, - publicIpAddresses: locations, - computeGalleries: locations, - databricks: locations, - containerApps: locations, - apiManagementService: locations, - synapse: locations + all: locations }; diff --git a/helpers/azure/resources.js b/helpers/azure/resources.js index 7c05970efa..111bd05ee4 100644 --- a/helpers/azure/resources.js +++ b/helpers/azure/resources.js @@ -306,8 +306,5 @@ module.exports = { }, machineLearning: { listWorkspaces: 'id' - }, - apiManagementService: { - list: 'id' } }; diff --git a/helpers/google/functions.js b/helpers/google/functions.js index f8b9367bef..ba1c667897 100644 --- a/helpers/google/functions.js +++ b/helpers/google/functions.js @@ -365,7 +365,7 @@ function checkOrgPolicy(orgPolicies, constraintName, constraintType, shouldBeEna } else { isEnabled = ifNotFound; } - } + } let successMessage = `"${displayName}" constraint is enforced at the organization level.`; let failureMessage = `"${displayName}" constraint is not enforced at the organization level.`; let status, message; @@ -402,54 +402,6 @@ function checkIAMRole(iamPolicy, roles, region, results, project, notFoundMessag } } -function checkFirewallRules(firewallRules) { - firewallRules.sort((a, b) => (a.priority || 1000) - (b.priority || 1000)); - - for (const firewallRule of firewallRules) { - if (firewallRule.direction !== 'INGRESS' || firewallRule.disabled) { - continue; - } - - const networkName = firewallRule.network ? firewallRule.network.split('/').pop() : ''; - - let allSources = firewallRule.sourceRanges && firewallRule.sourceRanges.some(sourceAddressPrefix => - sourceAddressPrefix === '*' || - sourceAddressPrefix === '0.0.0.0/0' || - sourceAddressPrefix === '::/0' || - sourceAddressPrefix.includes('/0') || - sourceAddressPrefix.toLowerCase() === 'internet' || - sourceAddressPrefix.includes('/0') - ); - - if (allSources && firewallRule.allowed && firewallRule.allowed.some(allow => !!allow.IPProtocol)) { - return { exposed: true, networkName: `vpc ${networkName}` }; - } - - if (allSources && firewallRule.denied && firewallRule.denied.some(deny => deny.IPProtocol === 'all')) { - return { exposed: false }; - } - - } - - return {exposed: true}; - - -} - -function checkNetworkExposure(cache, source, networks, firewallRules, region, results) { - let exposedPath = ''; - - if (firewallRules && firewallRules.length) { - // Scenario 1: check if any firewall rule allows all inbound traffic - let isExposed = checkFirewallRules(firewallRules); - if (isExposed.exposed) { - if (isExposed.networkName) { - return isExposed.networkName; - } - } - } - return exposedPath -} module.exports = { addResult: addResult, findOpenPorts: findOpenPorts, @@ -461,6 +413,5 @@ module.exports = { createResourceName: createResourceName, checkOrgPolicy: checkOrgPolicy, checkIAMRole: checkIAMRole, - findOpenAllPortsEgress: findOpenAllPortsEgress, - checkNetworkExposure: checkNetworkExposure + findOpenAllPortsEgress: findOpenAllPortsEgress }; diff --git a/helpers/shared.js b/helpers/shared.js index f230eb3c4f..6c2a4ca572 100644 --- a/helpers/shared.js +++ b/helpers/shared.js @@ -92,14 +92,14 @@ var processIntegration = function(serviceName, settings, collection, calls, post }; var processIntegrationAdditionalData = function(serviceName, localSettings, localCollection, calls, postcalls, localEventCollection, callback){ - if (!localCollection || - !Object.keys(localCollection).length || - !localCollection[serviceName.toLowerCase()] || - !Object.keys(localCollection[serviceName.toLowerCase()]).length) { + if (localCollection == undefined || + (localCollection && + (JSON.stringify(localCollection)==='{}' || + localCollection[serviceName.toLowerCase()] == undefined || + JSON.stringify(localCollection[serviceName.toLowerCase()])==='{}'))) { return callback(null); } - let callsMap = calls[serviceName] ? Object.keys(calls[serviceName]) : null; let foundData=[]; diff --git a/index.js b/index.js index 3835435550..1668488c1c 100644 --- a/index.js +++ b/index.js @@ -163,10 +163,7 @@ if (config.credentials.aws.credential_file && (!settings.cloud || (settings.clou KeyValue: config.credentials.azure.key_value, DirectoryID: config.credentials.azure.directory_id, SubscriptionID: config.credentials.azure.subscription_id, - location: 'East US', - Govcloud: config.credentials.azure.govcloud, - StorageConnection: config.credentials.azure.storage_connection, - BlobContainer: config.credentials.azure.blob_container + location: 'East US' }; } else if (config.credentials.google.credential_file && (!settings.cloud || (settings.cloud == 'google'))) { settings.cloud = 'google'; diff --git a/plugins/alibaba/securitycenter/securityCenterEdition.js b/plugins/alibaba/securitycenter/securityCenterEdition.js index 254d116da1..eaf4804d72 100644 --- a/plugins/alibaba/securitycenter/securityCenterEdition.js +++ b/plugins/alibaba/securitycenter/securityCenterEdition.js @@ -1,5 +1,4 @@ var helpers = require('../../../helpers/alibaba'); -const async = require('async'); module.exports = { title: 'Security Center Edition', @@ -7,7 +6,7 @@ module.exports = { domain: 'Management and Governance', severity: 'Medium', description: 'Ensure that your cloud Security Center edition is Advanced or plus.', - more_info: 'Premium Security Center editions like Advanced or Enterprise Edition provides crucial features liekthreat detection for network and endpoints, ' + + more_info: 'Premium Security Center editions like Advanced or Enterprise Edition provides crucial features liekthreat detection for network and endpoints, ' + 'providing malware detection, webshell detection and anomaly detection in Security Center.', link: 'https://www.alibabacloud.com/help/product/28498.htm', recommended_action: 'Upgrade your Security Center edition to at least Advanced.', @@ -16,7 +15,7 @@ module.exports = { run: function(cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings); + var region = helpers.defaultRegion(settings); // Below map might not be accurate as I checked with Anti-virus and Advanced editions and API is returning // 6 and 5 respectively against the version key. As it will be costly to try all editions to get the acrual @@ -30,32 +29,29 @@ module.exports = { 5: 'Advanced', 6: 'Anti-virus' }; - async.each(regions.tds, function(region, rcb) { - var describeVersionConfig = helpers.addSource(cache, source, - ['tds', 'DescribeVersionConfig', region]); - - if (!describeVersionConfig) { - return rcb(); - } - - if (describeVersionConfig.err || !describeVersionConfig.data) { - helpers.addResult(results, 3, - `Unable to query Security Center version config: ${helpers.addError(describeVersionConfig)}`, - region); - return rcb(); - } - - let securityVersion = describeVersionConfig.data.Version ? describeVersionConfig.data.Version : 1; - - if (securityVersion == 1 || securityVersion == 6) { - helpers.addResult(results, 2, `Security Center edition is ${versionIdNameMap[securityVersion]}`, region); - } else { - helpers.addResult(results, 0, `Security Center edition is ${versionIdNameMap[securityVersion]}`, region); - } - - rcb(); - }, function(){ - callback(null, results, source); - }); + + var describeVersionConfig = helpers.addSource(cache, source, + ['tds', 'DescribeVersionConfig', region]); + + if (!describeVersionConfig) { + return callback(null, results, source); + } + + if (describeVersionConfig.err || !describeVersionConfig.data) { + helpers.addResult(results, 3, + `Unable to query Security Center version config: ${helpers.addError(describeVersionConfig)}`, + region); + return callback(null, results, source); + } + + let securityVersion = describeVersionConfig.data.Version ? describeVersionConfig.data.Version : 1; + + if (securityVersion == 1 || securityVersion == 6) { + helpers.addResult(results, 2, `Security Center edition is ${versionIdNameMap[securityVersion]}`, region); + } else { + helpers.addResult(results, 0, `Security Center edition is ${versionIdNameMap[securityVersion]}`, region); + } + + callback(null, results, source); } -}; +}; \ No newline at end of file diff --git a/plugins/aws/apprunner/serviceEncrypted.js b/plugins/aws/apprunner/serviceEncrypted.js index c90dfc0adb..85f913040d 100644 --- a/plugins/aws/apprunner/serviceEncrypted.js +++ b/plugins/aws/apprunner/serviceEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: ' App Runner service desired Encryption level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['apprunner:CreateService','apprunner:DeleteService'], diff --git a/plugins/aws/auditmanager/auditmanagerDataEncrypted.js b/plugins/aws/auditmanager/auditmanagerDataEncrypted.js index ef9652f57a..67038211d3 100644 --- a/plugins/aws/auditmanager/auditmanagerDataEncrypted.js +++ b/plugins/aws/auditmanager/auditmanagerDataEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Audit Manager Data Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['auditmanager:registerAccount','auditmanager:UpdateSettings','auditmanager:DeregisterAccount'], diff --git a/plugins/aws/auditmanager/auditmanagerDataEncrypted.spec.js b/plugins/aws/auditmanager/auditmanagerDataEncrypted.spec.js index f25b3b70fc..bef446ac37 100644 --- a/plugins/aws/auditmanager/auditmanagerDataEncrypted.spec.js +++ b/plugins/aws/auditmanager/auditmanagerDataEncrypted.spec.js @@ -98,7 +98,7 @@ describe('auditmanagerDataEncrypted', function () { it('should FAIL if Audit Manager data is not encrypted with desired encryption level', function (done) { const cache = createCache(getSettings, listKeys, describeKey[1]); - auditmanagerDataEncrypted.run(cache, {auditmanager_data_encryption_level: 'awscmk'}, (err, results) => { + auditmanagerDataEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/backup/backupVaultEncrypted.js b/plugins/aws/backup/backupVaultEncrypted.js index a8c69131ac..b5b9e6c718 100644 --- a/plugins/aws/backup/backupVaultEncrypted.js +++ b/plugins/aws/backup/backupVaultEncrypted.js @@ -16,7 +16,7 @@ module.exports = { name: 'CodeArtifact Domain Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['backup:CreateBackupVault','backup:DeleteBackupVault'], diff --git a/plugins/aws/cloudwatchlogs/logGroupsEncrypted.js b/plugins/aws/cloudwatchlogs/logGroupsEncrypted.js index e280a45c48..d7341202f9 100644 --- a/plugins/aws/cloudwatchlogs/logGroupsEncrypted.js +++ b/plugins/aws/cloudwatchlogs/logGroupsEncrypted.js @@ -18,7 +18,7 @@ module.exports = { name: 'CloudWatch Log Groups Target Ecryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' }, cloudwatchlog_whitelist: { name: 'Lambda Functions Whitelisted', diff --git a/plugins/aws/codeartifact/codeartifactDomainEncrypted.js b/plugins/aws/codeartifact/codeartifactDomainEncrypted.js index 80186bb0b5..c3def0ee5c 100644 --- a/plugins/aws/codeartifact/codeartifactDomainEncrypted.js +++ b/plugins/aws/codeartifact/codeartifactDomainEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'CodeArtifact Domain Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['codeartifact:CreateDomain', 'codeartifact:DeleteDomain'], diff --git a/plugins/aws/codeartifact/codeartifactDomainEncrypted.spec.js b/plugins/aws/codeartifact/codeartifactDomainEncrypted.spec.js index 5efe70c791..b6d8bcda5f 100644 --- a/plugins/aws/codeartifact/codeartifactDomainEncrypted.spec.js +++ b/plugins/aws/codeartifact/codeartifactDomainEncrypted.spec.js @@ -101,7 +101,7 @@ describe('codeartifactDomainEncrypted', function () { it('should FAIL if CodeArtifact domain is not encrypted with desired encyption level', function (done) { const cache = createCache(listDomains, listKeys, describeKey[1]); - codeartifactDomainEncrypted.run(cache, {codeartifact_domain_encryption_level:'awscmk'}, (err, results) => { + codeartifactDomainEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/codebuild/projectArtifactsEncrypted.js b/plugins/aws/codebuild/projectArtifactsEncrypted.js index 7cc111417d..1316415b51 100644 --- a/plugins/aws/codebuild/projectArtifactsEncrypted.js +++ b/plugins/aws/codebuild/projectArtifactsEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Project Artifacts Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['codebuild:CreateProject', 'codebuild:UpdateProject','codebuild:DeleteProject'], diff --git a/plugins/aws/codebuild/projectArtifactsEncrypted.spec.js b/plugins/aws/codebuild/projectArtifactsEncrypted.spec.js index 080bfa3271..b9e0618f8e 100644 --- a/plugins/aws/codebuild/projectArtifactsEncrypted.spec.js +++ b/plugins/aws/codebuild/projectArtifactsEncrypted.spec.js @@ -158,7 +158,7 @@ describe('projectArtifactsEncrypted', function () { it('should FAIL if CodeBuild project artifact is not encrypted with desired encryption level', function (done) { const cache = createCache(listProjects, listKeys, batchGetProjects[1], describeKey[1]); - projectArtifactsEncrypted.run(cache, { project_artifacts_desired_encryption_level: 'awscmk' }, (err, results) => { + projectArtifactsEncrypted.run(cache, { projects_artifact_desired_encryption_level: 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/codepipeline/pipelineArtifactsEncrypted.js b/plugins/aws/codepipeline/pipelineArtifactsEncrypted.js index c757e6302a..ff8c685a68 100644 --- a/plugins/aws/codepipeline/pipelineArtifactsEncrypted.js +++ b/plugins/aws/codepipeline/pipelineArtifactsEncrypted.js @@ -18,7 +18,7 @@ module.exports = { name: 'Pipeline Artifacts Desired Encrypted Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['codepipeline:CreatePipeline','codepipeline:DeletePipeline'], diff --git a/plugins/aws/codepipeline/pipelineArtifactsEncrypted.spec.js b/plugins/aws/codepipeline/pipelineArtifactsEncrypted.spec.js index 9d4f18ffbc..84b653c7bb 100644 --- a/plugins/aws/codepipeline/pipelineArtifactsEncrypted.spec.js +++ b/plugins/aws/codepipeline/pipelineArtifactsEncrypted.spec.js @@ -135,7 +135,7 @@ describe('pipelineArtifactsEncrypted', function () { describe('run', function () { it('should PASS if Pipeline Artifacts is encrypted with desired encryption level', function (done) { const cache = createCache([listPipelines[0]], listKeys, listAliases, getPipeline[0], describeKey[0]); - pipelineArtifactsEncrypted.run(cache, { pipeline_artifacts_desired_encryption_level : 'awscmk' }, (err, results) => { + pipelineArtifactsEncrypted.run(cache, { pipeline_artifacts_encryption : 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); expect(results[0].region).to.equal('us-east-1'); @@ -145,7 +145,7 @@ describe('pipelineArtifactsEncrypted', function () { it('should FAIL if Pipeline Artifacts not encrypted with desired encryption level', function (done) { const cache = createCache([listPipelines[0]], listKeys, listAliases, getPipeline[0], describeKey[1]); - pipelineArtifactsEncrypted.run(cache, { pipeline_artifacts_desired_encryption_level : 'awscmk' }, (err, results) => { + pipelineArtifactsEncrypted.run(cache, { pipeline_artifacts_encryption : 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/connect/customerProfilesDomainEncrypted.js b/plugins/aws/connect/customerProfilesDomainEncrypted.js index 76063936c5..b523e93073 100644 --- a/plugins/aws/connect/customerProfilesDomainEncrypted.js +++ b/plugins/aws/connect/customerProfilesDomainEncrypted.js @@ -16,7 +16,7 @@ module.exports = { name: 'Connect Customer Profiles Encrypted', description: 'In order (lowest to highest) awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['customerprofiles:CreateDomain', 'customerprofiles:UpdateDomain', 'customerprofile:DeleteDomain'], diff --git a/plugins/aws/connect/instanceAttachmentsEncrypted.js b/plugins/aws/connect/instanceAttachmentsEncrypted.js index cc6abdb1fb..22d2c9e264 100644 --- a/plugins/aws/connect/instanceAttachmentsEncrypted.js +++ b/plugins/aws/connect/instanceAttachmentsEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Connect Attachments Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['connect:CreateInstance', 'connect:AssociateInstanceStorageConfig', 'connect:UpdateInstanceStorageConfig', 'connect:DeleteInstance', 'connect:DisassociateInstanceStorageConfig'], diff --git a/plugins/aws/connect/instanceAttachmentsEncrypted.spec.js b/plugins/aws/connect/instanceAttachmentsEncrypted.spec.js index 2f9e9965f3..6d115ad83a 100644 --- a/plugins/aws/connect/instanceAttachmentsEncrypted.spec.js +++ b/plugins/aws/connect/instanceAttachmentsEncrypted.spec.js @@ -136,7 +136,7 @@ describe('instanceAttachmentsEncrypted', function () { describe('run', function () { it('should FAIL if Connect instance is not using desired encryption level', function (done) { const cache = createCache(listInstances, instanceAttachmentStorageConfigs[0], listKeys, describeKey[1]); - instanceAttachmentsEncrypted.run(cache, {connect_attachments_encryption_level : 'awscmk'}, (err, results) => { + instanceAttachmentsEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); done(); diff --git a/plugins/aws/connect/instanceCallRecordingEncrypted.js b/plugins/aws/connect/instanceCallRecordingEncrypted.js index 6311f158ab..68987ddf5e 100644 --- a/plugins/aws/connect/instanceCallRecordingEncrypted.js +++ b/plugins/aws/connect/instanceCallRecordingEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Connect Call Resording Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['connect:CreateInstance', 'connect:AssociateInstanceStorageConfig', 'connect:UpdateInstanceStorageConfig','connect:DeleteInstance', 'connect:DisassociateInstanceStorageConfig'], diff --git a/plugins/aws/connect/instanceCallRecordingEncrypted.spec.js b/plugins/aws/connect/instanceCallRecordingEncrypted.spec.js index bb7b9b0815..e9a81e213f 100644 --- a/plugins/aws/connect/instanceCallRecordingEncrypted.spec.js +++ b/plugins/aws/connect/instanceCallRecordingEncrypted.spec.js @@ -136,7 +136,7 @@ describe('instanceCallRecordingEncrypted', function () { describe('run', function () { it('should FAIL if Connect instance is not using desired encryption level', function (done) { const cache = createCache(listInstances, listInstanceCallRecordingStorageConfigs[0], listKeys, describeKey[1]); - instanceCallRecordingEncrypted.run(cache, {connect_call_recording_encryption_level: 'awscmk'}, (err, results) => { + instanceCallRecordingEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); done(); diff --git a/plugins/aws/connect/instanceMediaStreamsEncrypted.js b/plugins/aws/connect/instanceMediaStreamsEncrypted.js index 2592d97168..434c48a803 100644 --- a/plugins/aws/connect/instanceMediaStreamsEncrypted.js +++ b/plugins/aws/connect/instanceMediaStreamsEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Connect Media Streams Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['connect:CreateInstance', 'connect:AssociateInstanceStorageConfig', 'connect:UpdateInstanceStorageConfig','connect:DeleteInstance', 'connect:DisassociateInstanceStorageConfig'], diff --git a/plugins/aws/connect/instanceMediaStreamsEncrypted.spec.js b/plugins/aws/connect/instanceMediaStreamsEncrypted.spec.js index 8024d015d0..36fa52b903 100644 --- a/plugins/aws/connect/instanceMediaStreamsEncrypted.spec.js +++ b/plugins/aws/connect/instanceMediaStreamsEncrypted.spec.js @@ -142,7 +142,7 @@ describe('instanceMediaStreamsEncrypted', function () { describe('run', function () { it('should FAIL if Connect instance is not using desired encryption level', function (done) { const cache = createCache(listInstances, listInstanceMediaStreamStorageConfigs[0], listKeys, describeKey[1]); - instanceMediaStreamsEncrypted.run(cache, {connect_media_streams_encryption_level: 'awscmk'}, (err, results) => { + instanceMediaStreamsEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); done(); diff --git a/plugins/aws/connect/instanceReportsEncrypted.js b/plugins/aws/connect/instanceReportsEncrypted.js index d4cc6a1b5e..ea4bed1916 100644 --- a/plugins/aws/connect/instanceReportsEncrypted.js +++ b/plugins/aws/connect/instanceReportsEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Connect Exported Reports Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['connect:CreateInstance', 'connect:AssociateInstanceStorageConfig', 'connect:UpdateInstanceStorageConfig','connect:DeleteInstance', 'connect:DisassociateInstanceStorageConfig'], diff --git a/plugins/aws/connect/instanceReportsEncrypted.spec.js b/plugins/aws/connect/instanceReportsEncrypted.spec.js index deb0aab8bb..2fa15b71f1 100644 --- a/plugins/aws/connect/instanceReportsEncrypted.spec.js +++ b/plugins/aws/connect/instanceReportsEncrypted.spec.js @@ -136,7 +136,7 @@ describe('instanceReportsEncrypted', function () { describe('run', function () { it('should FAIL if Connect instance is not using desired encryption level', function (done) { const cache = createCache(listInstances, listInstanceExportedReportStorageConfigs[0], listKeys, describeKey[1]); - instanceReportsEncrypted.run(cache, {connect_exported_reports_encryption_level : 'awscmk'}, (err, results) => { + instanceReportsEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); done(); diff --git a/plugins/aws/connect/instanceTranscriptsEncrypted.js b/plugins/aws/connect/instanceTranscriptsEncrypted.js index 217ea2a6d8..8035988918 100644 --- a/plugins/aws/connect/instanceTranscriptsEncrypted.js +++ b/plugins/aws/connect/instanceTranscriptsEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Connect Chat Transcripts Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['connect:CreateInstance', 'connect:AssociateInstanceStorageConfig', 'connect:UpdateInstanceStorageConfig','connect:DeleteInstance', 'connect:DisassociateInstanceStorageConfig'], diff --git a/plugins/aws/connect/instanceTranscriptsEncrypted.spec.js b/plugins/aws/connect/instanceTranscriptsEncrypted.spec.js index 83b6ff6e65..e2b7fa991f 100644 --- a/plugins/aws/connect/instanceTranscriptsEncrypted.spec.js +++ b/plugins/aws/connect/instanceTranscriptsEncrypted.spec.js @@ -136,7 +136,7 @@ describe('instanceTranscriptsEncrypted', function () { describe('run', function () { it('should FAIL if Connect instance is not using desired encryption level', function (done) { const cache = createCache(listInstances, listInstanceChatTranscriptStorageConfigs[0], listKeys, describeKey[1]); - instanceTranscriptsEncrypted.run(cache, {connect_chat_transcripts_encryption_level: 'awscmk'}, (err, results) => { + instanceTranscriptsEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); done(); diff --git a/plugins/aws/documentDB/docdbClusterEncrypted.js b/plugins/aws/documentDB/docdbClusterEncrypted.js index 6eff45ad78..4ff795f685 100644 --- a/plugins/aws/documentDB/docdbClusterEncrypted.js +++ b/plugins/aws/documentDB/docdbClusterEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'DocumentDB Cluster Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['docdb:CreateDBCluster','docdb:CreateDBInstance','docdb:DeleteDBCluster'], diff --git a/plugins/aws/ec2/ebsEncryptionEnabled.js b/plugins/aws/ec2/ebsEncryptionEnabled.js index 877c86d80b..c6144cd468 100644 --- a/plugins/aws/ec2/ebsEncryptionEnabled.js +++ b/plugins/aws/ec2/ebsEncryptionEnabled.js @@ -55,7 +55,7 @@ module.exports = { name: 'EBS Minimum Encryption Level at rest', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', }, }, diff --git a/plugins/aws/ecr/ecrRepositoryEncrypted.js b/plugins/aws/ecr/ecrRepositoryEncrypted.js index fffc5ccd1d..a416b14761 100644 --- a/plugins/aws/ecr/ecrRepositoryEncrypted.js +++ b/plugins/aws/ecr/ecrRepositoryEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'ECR Repository Encryption', description: 'In order (lowest to highest) sse=AES-256; awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(sse|awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['ecr:CreateRepository', 'ecr:DeleteRepository'], diff --git a/plugins/aws/eks/eksKubernetesVersion.spec.js b/plugins/aws/eks/eksKubernetesVersion.spec.js index 0997f85358..b53206f8d2 100644 --- a/plugins/aws/eks/eksKubernetesVersion.spec.js +++ b/plugins/aws/eks/eksKubernetesVersion.spec.js @@ -82,7 +82,7 @@ describe('eksKubernetesVersion', function () { "cluster": { "name": "mycluster", "arn": "arn:aws:eks:us-east-1:012345678911:cluster/mycluster", - "version": "1.29", + "version": "1.27", } } ); diff --git a/plugins/aws/elasticache/redisClusterEncryptionAtRest.js b/plugins/aws/elasticache/redisClusterEncryptionAtRest.js index 966ddd086e..01b033ca38 100644 --- a/plugins/aws/elasticache/redisClusterEncryptionAtRest.js +++ b/plugins/aws/elasticache/redisClusterEncryptionAtRest.js @@ -17,7 +17,7 @@ module.exports = { name: 'ElastiCache Cluster Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['elasticache:CreateCacheCluster', 'elasticache:DeleteCacheCluster', 'elasticache:CreateReplicationGroup'], diff --git a/plugins/aws/elasticache/redisClusterEncryptionAtRest.spec.js b/plugins/aws/elasticache/redisClusterEncryptionAtRest.spec.js index 92b16c3717..e2d9b5052c 100644 --- a/plugins/aws/elasticache/redisClusterEncryptionAtRest.spec.js +++ b/plugins/aws/elasticache/redisClusterEncryptionAtRest.spec.js @@ -219,7 +219,7 @@ describe('redisClusterEncryptionAtRest', function () { describe('run', function () { it('should PASS if Redis Cluster at-rest is encrypted with desired encryption level', function (done) { const cache = createCache(describeCacheClusters[0], listKeys, describeReplicationGroups[0], describeKey[0]); - redisClusterEncryptionAtRest.run(cache, { ec_cluster_target_encryption_level: 'awscmk' }, (err, results) => { + redisClusterEncryptionAtRest.run(cache, { ec_atrest_desired_encryption_level: 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); expect(results[0].region).to.equal('us-east-1'); @@ -229,7 +229,7 @@ describe('redisClusterEncryptionAtRest', function () { it('should FAIL if Redis Cluster at-rest is not encrypted with desired encryption level', function (done) { const cache = createCache([describeCacheClusters[1]],listKeys, describeReplicationGroups[1], describeKey[1]); - redisClusterEncryptionAtRest.run(cache, { ec_cluster_target_encryption_level: 'awscmk' }, (err, results) => { + redisClusterEncryptionAtRest.run(cache, { ec_atrest_desired_encryption_level: 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/elbv2/elbv2UnhealthyInstance.js b/plugins/aws/elbv2/elbv2UnhealthyInstance.js index e7b1acf66a..3d7ff74573 100644 --- a/plugins/aws/elbv2/elbv2UnhealthyInstance.js +++ b/plugins/aws/elbv2/elbv2UnhealthyInstance.js @@ -37,27 +37,27 @@ module.exports = { } describeLoadBalancers.data.forEach(function(lb) { - var resource = lb.LoadBalancerArn; + var resource = describeLoadBalancers.LoadBalancerArn; var unhealthyInstances = 0; var describeTargetGroups = helpers.addSource(cache, source, ['elbv2', 'describeTargetGroups', region, lb.DNSName]); - + if (!describeTargetGroups || describeTargetGroups.err || !describeTargetGroups.data) { helpers.addResult(results, 3, `Unable to query for Application/Network load balancer target groups: ${helpers.addError(describeTargetGroups)}`, region, resource); return; } - + if (!describeTargetGroups.data.TargetGroups || !describeTargetGroups.data.TargetGroups.length) { helpers.addResult(results, 2, 'No Application/Network load balancer target groups found', region, resource); return; } - + describeTargetGroups.data.TargetGroups.forEach(function(tg) { var describeTargetHealth = helpers.addSource(cache, source, ['elbv2', 'describeTargetHealth', region, tg.TargetGroupArn]); - + if (!describeTargetHealth || describeTargetHealth.err || !describeTargetHealth.data || !describeTargetHealth.data.TargetHealthDescriptions || !describeTargetHealth.data.TargetHealthDescriptions.length) { return; @@ -81,10 +81,10 @@ module.exports = { region, resource); } }); - + rcb(); }, function(){ callback(null, results, source); }); } -}; +}; \ No newline at end of file diff --git a/plugins/aws/elbv2/elbv2WafEnabled.js b/plugins/aws/elbv2/elbv2WafEnabled.js index f238e2805d..d9f67c5ad1 100644 --- a/plugins/aws/elbv2/elbv2WafEnabled.js +++ b/plugins/aws/elbv2/elbv2WafEnabled.js @@ -12,7 +12,7 @@ module.exports = { recommended_action: '1. Enter the WAF service. 2. Enter Web ACLs and filter by the region the Application Load Balancer is in. 3. If no Web ACL is found, Create a new Web ACL in the region the ALB resides and in Resource type to associate with web ACL, select the Load Balancer. ', apis: ['ELBv2:describeLoadBalancers', 'WAFV2:listWebACLs', 'WAFRegional:listWebACLs', 'WAFV2:listResourcesForWebACL', 'WAFRegional:listResourcesForWebACL'], realtime_triggers: ['elasticloadbalancing:CreateLoadBalancer', 'wafv2:CreateWebAcl', 'wafv2:UpdateWebAcl', 'wafregional:CreateWebAcl', 'wafregional:UpdateWebAcl', 'wafv2:DeleteWebAcl', 'wafregional:DeleteWebAcl'], - + run: function(cache, settings, callback) { var results = []; var source = {}; @@ -83,25 +83,15 @@ module.exports = { return lcb(); } - var appElbFound = false; - loadBalancers.data.forEach(loadBalancer => { - if (loadBalancer.Type && - loadBalancer.Type.toLowerCase() === 'application') { - appElbFound = true; - if (loadBalancer.LoadBalancerArn && (resourcesToCheck.indexOf(loadBalancer.LoadBalancerArn) > -1)) { - resourcesToCheck.splice(resourcesToCheck.indexOf(loadBalancer.LoadBalancerArn), 1); - helpers.addResult(results, 0, 'The Application Load Balancer has WAF enabled', loc, loadBalancer.LoadBalancerArn); - } else { - helpers.addResult(results, 2, 'The Application Load Balancer does not have WAF enabled', loc, loadBalancer.LoadBalancerArn); - } + if (loadBalancer.LoadBalancerArn && (resourcesToCheck.indexOf(loadBalancer.LoadBalancerArn) > -1)) { + resourcesToCheck.splice(resourcesToCheck.indexOf(loadBalancer.LoadBalancerArn), 1); + helpers.addResult(results, 0, 'The Application Load Balancer has WAF enabled', loc, loadBalancer.LoadBalancerArn); + } else { + helpers.addResult(results, 2, 'The Application Load Balancer does not have WAF enabled', loc, loadBalancer.LoadBalancerArn); } }); - if (!appElbFound) { - helpers.addResult(results, 0, 'No Application Load Balancers found', loc); - } - lcb(); }, function() { callback(null, results, source); diff --git a/plugins/aws/firehose/deliveryStreamEncrypted.js b/plugins/aws/firehose/deliveryStreamEncrypted.js index c7579cebe4..8f544a7b19 100644 --- a/plugins/aws/firehose/deliveryStreamEncrypted.js +++ b/plugins/aws/firehose/deliveryStreamEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Firehose Delivery Stream Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['firehose:CreateDeliveryStreams','firehose:UpdateDestination', 'firehose:DeleteliveryStreams'], diff --git a/plugins/aws/firehose/deliveryStreamEncrypted.spec.js b/plugins/aws/firehose/deliveryStreamEncrypted.spec.js index 39ccf0b75f..5d7c45b1fe 100644 --- a/plugins/aws/firehose/deliveryStreamEncrypted.spec.js +++ b/plugins/aws/firehose/deliveryStreamEncrypted.spec.js @@ -308,7 +308,7 @@ describe('deliveryStreamEncrypted', function () { deliveryStreamEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('Firehose delivery stream is encrypted with awskms'); + expect(results[0].message).to.include('Firehose delivery stream destination bucket is encrypted with awscmk'); expect(results[0].region).to.equal('us-east-1'); done(); }); @@ -316,7 +316,7 @@ describe('deliveryStreamEncrypted', function () { it('should FAIL if Firehose Delivery Stream not encrypted with desired encryption level', function (done) { const cache = createCache([listDeliveryStreams[0]], listKeys, describeDeliveryStream[1], describeKey[1]); - deliveryStreamEncrypted.run(cache, {delivery_stream_desired_encryption_level: 'awscmk'}, (err, results) => { + deliveryStreamEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('Firehose delivery stream destination bucket is encrypted with awskms'); diff --git a/plugins/aws/frauddetector/fraudDetectorDataEncrypted.js b/plugins/aws/frauddetector/fraudDetectorDataEncrypted.js index 803225679c..0b96918845 100644 --- a/plugins/aws/frauddetector/fraudDetectorDataEncrypted.js +++ b/plugins/aws/frauddetector/fraudDetectorDataEncrypted.js @@ -16,7 +16,7 @@ module.exports = { name: 'Fraud Detector Data Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['frauddetector:PutKMSEncryptionKey', 'frauddetector:DeleteDetector'], diff --git a/plugins/aws/fsx/fsxFileSystemEncrypted.js b/plugins/aws/fsx/fsxFileSystemEncrypted.js index 418c3b7d40..fbb7b8e65d 100644 --- a/plugins/aws/fsx/fsxFileSystemEncrypted.js +++ b/plugins/aws/fsx/fsxFileSystemEncrypted.js @@ -16,7 +16,7 @@ module.exports = { name: 'FSx File Systems Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['fsx:CreateFileSystem', 'fsx:DeleteFileSystem'], diff --git a/plugins/aws/glue/glueS3EncryptionEnabled.js b/plugins/aws/glue/glueS3EncryptionEnabled.js index fc0c091426..72e1602f99 100644 --- a/plugins/aws/glue/glueS3EncryptionEnabled.js +++ b/plugins/aws/glue/glueS3EncryptionEnabled.js @@ -16,7 +16,7 @@ module.exports = { name: 'Glue S3 Encryption Level', description: 'In order (lowest to highest) sse=S3 Server-Side; awskms=AWS-managed KMS; awscmk=Customer managed KMS;', regex: '^(sse|awskms|awscmk)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['glue:CreateSecurityConfiguration','glue:DeleteSecurityConfiguration'], diff --git a/plugins/aws/guardduty/exportedFindingsEncrypted.js b/plugins/aws/guardduty/exportedFindingsEncrypted.js index 370bf4ff60..348da066e6 100644 --- a/plugins/aws/guardduty/exportedFindingsEncrypted.js +++ b/plugins/aws/guardduty/exportedFindingsEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'GuardDuty Findings Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['guardduty:CreateDetector', 'guardduty:DeleteDetector'], diff --git a/plugins/aws/guardduty/exportedFindingsEncrypted.spec.js b/plugins/aws/guardduty/exportedFindingsEncrypted.spec.js index 3f56095442..a43bcac0a1 100644 --- a/plugins/aws/guardduty/exportedFindingsEncrypted.spec.js +++ b/plugins/aws/guardduty/exportedFindingsEncrypted.spec.js @@ -147,7 +147,7 @@ describe('exportedFindingsEncrypted', function () { it('should PASS if GuardDuty Export Findings is encrypted with desired level', function (done) { const cache = createCache([listDetectors[0]], [listPublishingDestinations[0]], describePublishingDestination[0], listKeys, describeKey[0]); - exportedFindingsEncrypted.run(cache, { findings_desired_encryption_level: 'awscmk' }, (err, results) => { + exportedFindingsEncrypted.run(cache, { exported_findings_desired_encryption_level: 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); expect(results[0].region).to.equal('us-east-1'); @@ -157,7 +157,7 @@ describe('exportedFindingsEncrypted', function () { it('should FAIL if GuardDuty Export Findings is not encrypted with desired level ', function (done) { const cache = createCache([listDetectors[0]], [listPublishingDestinations[0]], describePublishingDestination[1], listKeys, describeKey[1]); - exportedFindingsEncrypted.run(cache, {findings_desired_encryption_level: 'cloudhsm' }, (err, results) => { + exportedFindingsEncrypted.run(cache, { exported_findings_desired_encryption_level: 'externalcmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); @@ -167,7 +167,7 @@ describe('exportedFindingsEncrypted', function () { it('should PASS if on GuardDuty detectors found', function (done) { const cache = createCache([]); - exportedFindingsEncrypted.run(cache, { findings_desired_encryption_level: 'awscmk' }, (err, results) => { + exportedFindingsEncrypted.run(cache, { exported_findings_desired_encryption_level: 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -176,7 +176,7 @@ describe('exportedFindingsEncrypted', function () { it('should UNKNOWN if unable to list GuardDuty detectors', function (done) { const cache = createCache(null, null, null, null, null, { message: 'Unable to list GuardDuty detectors'}); - exportedFindingsEncrypted.run(cache, { findings_desired_encryption_level: 'awscmk' }, (err, results) => { + exportedFindingsEncrypted.run(cache, { exported_findings_desired_encryption_level: 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); @@ -185,7 +185,7 @@ describe('exportedFindingsEncrypted', function () { it('should UNKNOWN if unable to list GuardDuty publishing destinations', function (done) { const cache = createCache([listDetectors[0]], {}, describePublishingDestination[0], null, null, null, null, { message: 'Unable to query GuardDuty publishing destinations'}); - exportedFindingsEncrypted.run(cache, { findings_desired_encryption_level: 'awscmk' }, (err, results) => { + exportedFindingsEncrypted.run(cache, { exported_findings_desired_encryption_level: 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); @@ -194,7 +194,7 @@ describe('exportedFindingsEncrypted', function () { it('should not return anything if list detectors response not found', function (done) { const cache = createNullCache(); - exportedFindingsEncrypted.run(cache, { findings_desired_encryption_level: 'awscmk' }, (err, results) => { + exportedFindingsEncrypted.run(cache, { exported_findings_desired_encryption_level: 'awscmk' }, (err, results) => { expect(results.length).to.equal(0); done(); }); diff --git a/plugins/aws/healthlake/dataStoreEncrypted.js b/plugins/aws/healthlake/dataStoreEncrypted.js index c543c87851..edab34abd1 100644 --- a/plugins/aws/healthlake/dataStoreEncrypted.js +++ b/plugins/aws/healthlake/dataStoreEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'HealthLake Data Store Desired Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['healthlake:CreateFHIRDatastore', 'healthlake:DeleteFHIRDatastore'], diff --git a/plugins/aws/healthlake/dataStoreEncrypted.spec.js b/plugins/aws/healthlake/dataStoreEncrypted.spec.js index c7beb1d3b9..fa022a78f4 100644 --- a/plugins/aws/healthlake/dataStoreEncrypted.spec.js +++ b/plugins/aws/healthlake/dataStoreEncrypted.spec.js @@ -102,7 +102,7 @@ describe('dataStoreEncrypted', function () { describe('run', function () { it('should PASS if HealthLake Data Store is encrypted with desired encryption level', function (done) { const cache = createCache(listFHIRDatastores, listKeys, describeKey[0]); - dataStoreEncrypted.run(cache, { healthlake_datastore_desired_encryption_level: 'awscmk' }, (err, results) => { + dataStoreEncrypted.run(cache, { healthLake_data_store_encryption: 'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); expect(results[0].message).to.include('HealthLake data store is encrypted with awscmk'); @@ -113,7 +113,7 @@ describe('dataStoreEncrypted', function () { it('should FAIL if HealthLake Data Store is not encrypted with desired encyption level', function (done) { const cache = createCache(listFHIRDatastores, listKeys, describeKey[1]); - dataStoreEncrypted.run(cache, { healthlake_datastore_desired_encryption_level:'awscmk' }, (err, results) => { + dataStoreEncrypted.run(cache, { healthLake_data_store_encryption:'awscmk' }, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('HealthLake data store is encrypted with awskms'); diff --git a/plugins/aws/iam/iamMasterManagerRoles.js b/plugins/aws/iam/iamMasterManagerRoles.js index 1bf9f4f532..153461e679 100644 --- a/plugins/aws/iam/iamMasterManagerRoles.js +++ b/plugins/aws/iam/iamMasterManagerRoles.js @@ -202,7 +202,7 @@ module.exports = { var getRolePolicy = helpers.addSource(cache, source, ['iam', 'getRolePolicy', region, role.RoleName]); - if (!listRolePolicies || listRolePolicies.err || !listRolePolicies.data || !listRolePolicies.data.PolicyNames) { + if (listRolePolicies.err || !listRolePolicies.data || !listRolePolicies.data.PolicyNames) { helpers.addResult(results, 3, 'Unable to query for IAM role policy for role: ' + role.RoleName + ': ' + helpers.addError(listRolePolicies), 'global', role.Arn); return cb(); diff --git a/plugins/aws/imagebuilder/dockerfileTemplateEncrypted.js b/plugins/aws/imagebuilder/dockerfileTemplateEncrypted.js index fae28c29e4..23f761758a 100644 --- a/plugins/aws/imagebuilder/dockerfileTemplateEncrypted.js +++ b/plugins/aws/imagebuilder/dockerfileTemplateEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Docker File Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['imagebuilder:CreateContainerRecipe','imagebuilder:DeleteContainerRecipe'], diff --git a/plugins/aws/imagebuilder/dockerfileTemplateEncrypted.spec.js b/plugins/aws/imagebuilder/dockerfileTemplateEncrypted.spec.js index baa7bbafae..91b54b093a 100644 --- a/plugins/aws/imagebuilder/dockerfileTemplateEncrypted.spec.js +++ b/plugins/aws/imagebuilder/dockerfileTemplateEncrypted.spec.js @@ -196,7 +196,7 @@ describe('dockerfileTemplateEncrypted', function () { it('should FAIL if Dockerfile Template is encrypted with awskms', function (done) { const cache = createCache([listContainerRecipes[1]], listKeys, listAliases, getContainerRecipe[1], describeKey[1]); - dockerfileTemplateEncrypted.run(cache, {docker_file_desired_encryption_level: 'awscmk'}, (err, results) => { + dockerfileTemplateEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('Dockerfile Template is encrypted with'); diff --git a/plugins/aws/imagebuilder/imageRecipeVolumeEncrypted.js b/plugins/aws/imagebuilder/imageRecipeVolumeEncrypted.js index 6f93d806ad..9d2d4d5b25 100644 --- a/plugins/aws/imagebuilder/imageRecipeVolumeEncrypted.js +++ b/plugins/aws/imagebuilder/imageRecipeVolumeEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Image Recipe EBS Volumes Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['Imagebuilder:CreateImageRecipe','Imagebuilder:DeleteImageRecipe'], diff --git a/plugins/aws/imagebuilder/imgBuilderComponentsEncrypted.js b/plugins/aws/imagebuilder/imgBuilderComponentsEncrypted.js index 53458a6b2d..36f33356b5 100644 --- a/plugins/aws/imagebuilder/imgBuilderComponentsEncrypted.js +++ b/plugins/aws/imagebuilder/imgBuilderComponentsEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Image Builder Component Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['Imagebuilder:CreateComponent','Imagebuilder:DeleteComponent'], diff --git a/plugins/aws/imagebuilder/imgBuilderComponentsEncrypted.spec.js b/plugins/aws/imagebuilder/imgBuilderComponentsEncrypted.spec.js index 6eeb0bba47..d2219c817d 100644 --- a/plugins/aws/imagebuilder/imgBuilderComponentsEncrypted.spec.js +++ b/plugins/aws/imagebuilder/imgBuilderComponentsEncrypted.spec.js @@ -180,7 +180,7 @@ describe('imgBuilderComponentsEncrypted', function () { it('should FAIL if Image Builder component is encrypted with awskms', function (done) { const cache = createCache([listComponents[1]], listKeys, listAliases, getComponent[1], describeKey[1]); - imgBuilderComponentsEncrypted.run(cache, {image_component_desired_encryption_level: 'awscmk'}, (err, results) => { + imgBuilderComponentsEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('Image Builder component is encrypted with'); diff --git a/plugins/aws/iotsitewise/iotsitewiseDataEncrypted.js b/plugins/aws/iotsitewise/iotsitewiseDataEncrypted.js index 8b39d18681..53cdc7de4c 100644 --- a/plugins/aws/iotsitewise/iotsitewiseDataEncrypted.js +++ b/plugins/aws/iotsitewise/iotsitewiseDataEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'IoT SiteWise Data Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['imagebuilder:PutDefaultEncryptionConfiguration'], diff --git a/plugins/aws/kendra/kendraIndexEncrypted.js b/plugins/aws/kendra/kendraIndexEncrypted.js index 8494393f3a..bc8d9854a5 100644 --- a/plugins/aws/kendra/kendraIndexEncrypted.js +++ b/plugins/aws/kendra/kendraIndexEncrypted.js @@ -16,7 +16,7 @@ module.exports = { name: 'Kendra Index Encrypted', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['kendra:CreateIndex','kendra:UpdateIndex','kendra:DeleteIndex'], diff --git a/plugins/aws/kinesis/kinesisDataStreamsEncrypted.js b/plugins/aws/kinesis/kinesisDataStreamsEncrypted.js index 8aa16ea2a6..4694709351 100644 --- a/plugins/aws/kinesis/kinesisDataStreamsEncrypted.js +++ b/plugins/aws/kinesis/kinesisDataStreamsEncrypted.js @@ -18,7 +18,7 @@ module.exports = { name: 'Kinesis Data Stream Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['kinesis:CreateStream','kinesis:StartStreamEncryption','kinesis:StopStreamEncryption','kinesis:DeleteStream'], diff --git a/plugins/aws/kinesis/kinesisDataStreamsEncrypted.spec.js b/plugins/aws/kinesis/kinesisDataStreamsEncrypted.spec.js index 12edd5e58c..8ffb787aa1 100644 --- a/plugins/aws/kinesis/kinesisDataStreamsEncrypted.spec.js +++ b/plugins/aws/kinesis/kinesisDataStreamsEncrypted.spec.js @@ -218,7 +218,7 @@ describe('kinesisDataStreamsEncrypted', function () { it('should FAIL if Kinesis stream not encrypted with desired encryption level', function (done) { const cache = createCache([listStreams[1]], listKeys, [listAliases[1]], describeStream[1], describeKey[1]); - kinesisDataStreamsEncrypted.run(cache, {data_streams_desired_encryption_level: 'awscmk'}, (err, results) => { + kinesisDataStreamsEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('Kinesis stream is encrypted with awskms'); diff --git a/plugins/aws/kinesisvideo/videostreamDataEncrypted.js b/plugins/aws/kinesisvideo/videostreamDataEncrypted.js index a1146e4225..f181178871 100644 --- a/plugins/aws/kinesisvideo/videostreamDataEncrypted.js +++ b/plugins/aws/kinesisvideo/videostreamDataEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Kinesis Video Streams Data Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['kinesisvideo:CreateStream', 'kinesisvideo:DeleteStream'], diff --git a/plugins/aws/location/geoCollectionDataEncrypted.js b/plugins/aws/location/geoCollectionDataEncrypted.js index 4eb4c8a68e..db8755acbf 100644 --- a/plugins/aws/location/geoCollectionDataEncrypted.js +++ b/plugins/aws/location/geoCollectionDataEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Location Geofence Collection Data Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['location:CreateGeofenceCollection', 'location:DeleteGeofenceCollection'], diff --git a/plugins/aws/location/trackerDataEncrypted.js b/plugins/aws/location/trackerDataEncrypted.js index dfd529aebd..8a35338ffb 100644 --- a/plugins/aws/location/trackerDataEncrypted.js +++ b/plugins/aws/location/trackerDataEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Location Tracker Data Desired Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['location:CreateTracker', 'location:UpdateTracker', 'location:DeleteTracker'], diff --git a/plugins/aws/lookout/anomalyDetectorEncrypted.js b/plugins/aws/lookout/anomalyDetectorEncrypted.js index 054a2fdfe0..ca7d8f70f7 100644 --- a/plugins/aws/lookout/anomalyDetectorEncrypted.js +++ b/plugins/aws/lookout/anomalyDetectorEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'LookoutMetrics Anomaly Detector Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['lookoutmetrics:CreateAnomalyDetector', 'lookoutmetrics:UpdateAnomalyDetector', 'lookoutmetrics:DeleteAnomalyDetector'], diff --git a/plugins/aws/lookout/anomalyDetectorEncrypted.spec.js b/plugins/aws/lookout/anomalyDetectorEncrypted.spec.js index c928fe8438..5e23fba913 100644 --- a/plugins/aws/lookout/anomalyDetectorEncrypted.spec.js +++ b/plugins/aws/lookout/anomalyDetectorEncrypted.spec.js @@ -143,7 +143,7 @@ describe('anomalyDetectorEncrypted', function () { it('should FAIL if LookoutMetrics Anomaly Detector is not encrypted with desired encryption level', function (done) { const cache = createCache([listAnomalyDetectors[1]], listKeys, describeAnomalyDetector[1], describeKey[1]); - anomalyDetectorEncrypted.run(cache, {lookoutmetrics_anomalydetectors_desired_encryption_level: 'awscmk'}, (err, results) => { + anomalyDetectorEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/lookout/equipmentdatasetEncrypted.js b/plugins/aws/lookout/equipmentdatasetEncrypted.js index ba873bed3f..6125b8cee3 100644 --- a/plugins/aws/lookout/equipmentdatasetEncrypted.js +++ b/plugins/aws/lookout/equipmentdatasetEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Equipement Dataset Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['lookoutequipment:CreateDataset', 'lookoutequipment:DeleteDataset'], diff --git a/plugins/aws/lookout/equipmentdatasetEncrypted.spec.js b/plugins/aws/lookout/equipmentdatasetEncrypted.spec.js index 0c4f402180..639a7fdd86 100644 --- a/plugins/aws/lookout/equipmentdatasetEncrypted.spec.js +++ b/plugins/aws/lookout/equipmentdatasetEncrypted.spec.js @@ -135,7 +135,7 @@ describe('equipmentdatasetEncrypted', function () { it('should FAIL if LookoutEquipment Dataset is not encrypted with desired encryption level', function (done) { const cache = createCache([listDatasets[1]], listKeys, describeDataset[1], describeKey[1]); - equipmentdatasetEncrypted.run(cache, {equipment_dataset_desired_encryption_level: 'awscmk'}, (err, results) => { + equipmentdatasetEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/lookout/modelDataEncrypted.js b/plugins/aws/lookout/modelDataEncrypted.js index d956d2f8e5..7f93f1b8a4 100644 --- a/plugins/aws/lookout/modelDataEncrypted.js +++ b/plugins/aws/lookout/modelDataEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Vision Data Target Encryption Level', description: 'In order (lowest to highest) sse=S3-SSE; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(sse|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['lookoutvision:CreateModel', 'lookoutvision:DeleteModel'], diff --git a/plugins/aws/managedblockchain/networkMemberDataEncrypted.js b/plugins/aws/managedblockchain/networkMemberDataEncrypted.js index 56948232e9..7fbda0255b 100644 --- a/plugins/aws/managedblockchain/networkMemberDataEncrypted.js +++ b/plugins/aws/managedblockchain/networkMemberDataEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Managed Blockchain Member Target Encryption Level', description: 'In order (lowest to highest) sse=S3-SSE; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['managedblockchain:CreateNetwork', 'managedblockchain:DeleteMember'], diff --git a/plugins/aws/managedblockchain/networkMemberDataEncrypted.spec.js b/plugins/aws/managedblockchain/networkMemberDataEncrypted.spec.js index 6a6b3898e4..0d7a26a208 100644 --- a/plugins/aws/managedblockchain/networkMemberDataEncrypted.spec.js +++ b/plugins/aws/managedblockchain/networkMemberDataEncrypted.spec.js @@ -172,7 +172,7 @@ describe('networkMemberDataEncrypted', function () { it('should FAIL if Network member is not using desired encryption level', function (done) { const cache = createCache(listNetworks ,listMembers, getMember[1], listKeys, describeKey[0]); - networkMemberDataEncrypted.run(cache, {blockchain_member_encryption_level: 'awscmk'}, (err, results) => { + networkMemberDataEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/memorydb/memorydbClusterEncrypted.js b/plugins/aws/memorydb/memorydbClusterEncrypted.js index e242a05642..a2dcd6527b 100644 --- a/plugins/aws/memorydb/memorydbClusterEncrypted.js +++ b/plugins/aws/memorydb/memorydbClusterEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'MemoryDB Cluster Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['MemoryDB:CreateCluster', 'MemoryDB:DeleteCluster'], diff --git a/plugins/aws/memorydb/memorydbClusterEncrypted.spec.js b/plugins/aws/memorydb/memorydbClusterEncrypted.spec.js index 5018652060..e337d2a4e1 100644 --- a/plugins/aws/memorydb/memorydbClusterEncrypted.spec.js +++ b/plugins/aws/memorydb/memorydbClusterEncrypted.spec.js @@ -152,7 +152,7 @@ describe('memorydbClusterEncrypted', function () { it('should FAIL if MemoryDB Cluster for Redis is not encrypted with desired encyption level', function (done) { const cache = createCache([describeClusters[0]], listKeys, describeKey[1]); - memorydbClusterEncrypted.run(cache, {memorydb_cluster_target_encryption_level: 'awscmk'} , (err, results) => { + memorydbClusterEncrypted.run(cache, {} , (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('MemoryDB cluster is encrypted with awskms'); diff --git a/plugins/aws/mq/mqBrokerEncrypted.js b/plugins/aws/mq/mqBrokerEncrypted.js index 2e311a443f..19b67ac4b8 100644 --- a/plugins/aws/mq/mqBrokerEncrypted.js +++ b/plugins/aws/mq/mqBrokerEncrypted.js @@ -16,7 +16,7 @@ module.exports = { name: 'MQ Broker Target Encryption Level', description: 'In order (lowest to highest) sse=AWS-owned CMK awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(sse|awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['mq:CreateBrocker', 'mq:DeleteBrocker'], diff --git a/plugins/aws/mq/mqBrokerEncrypted.spec.js b/plugins/aws/mq/mqBrokerEncrypted.spec.js index 7cdf537c8f..a02f62e490 100644 --- a/plugins/aws/mq/mqBrokerEncrypted.spec.js +++ b/plugins/aws/mq/mqBrokerEncrypted.spec.js @@ -222,7 +222,7 @@ describe('mqBrokerEncrypted', function () { it('should FAIL if MQ Broker data at-rest is not encrypted with desired encryption level', function (done) { const cache = createCache([listBrokers[1]],listKeys, [describeBroker[0]], describeKey[1]); - mqBrokerEncrypted.run(cache, {mq_broker_desired_encryption_level: 'awscmk'}, (err, results) => { + mqBrokerEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); @@ -232,7 +232,7 @@ describe('mqBrokerEncrypted', function () { it('should FAIL if MQ Broker data at-rest is encrypted with AWS owned key', function (done) { const cache = createCache([listBrokers[2]],listKeys, [describeBroker[2]], describeKey[1]); - mqBrokerEncrypted.run(cache, {mq_broker_desired_encryption_level: 'awscmk'}, (err, results) => { + mqBrokerEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/aws/msk/mskClusterEncryptionAtRest.js b/plugins/aws/msk/mskClusterEncryptionAtRest.js index 847fa63fee..937bd501f5 100644 --- a/plugins/aws/msk/mskClusterEncryptionAtRest.js +++ b/plugins/aws/msk/mskClusterEncryptionAtRest.js @@ -16,7 +16,7 @@ module.exports = { name: 'MSK Cluster Desired Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['kafka:CreateCluster', 'kafka:DeleteCluster'], diff --git a/plugins/aws/msk/mskClusterEncryptionAtRest.spec.js b/plugins/aws/msk/mskClusterEncryptionAtRest.spec.js index 11dc783d18..d59ef80031 100644 --- a/plugins/aws/msk/mskClusterEncryptionAtRest.spec.js +++ b/plugins/aws/msk/mskClusterEncryptionAtRest.spec.js @@ -150,7 +150,7 @@ describe('mskClusterEncryptionAtRest', function () { it('should FAIL if MSK Cluster At-Rest is not encrypted with desired encyption level', function (done) { const cache = createCache(listClusters, listKeys, describeKey[1]); - mskClusterEncryptionAtRest.run(cache, {msk_cluster_desired_encryption_level: 'awscmk'}, (err, results) => { + mskClusterEncryptionAtRest.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].message).to.include('MSK cluster is encrypted with awskms'); diff --git a/plugins/aws/mwaa/environmentDataEncrypted.js b/plugins/aws/mwaa/environmentDataEncrypted.js index e1ddaadf6d..c8c7ac3082 100644 --- a/plugins/aws/mwaa/environmentDataEncrypted.js +++ b/plugins/aws/mwaa/environmentDataEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'MWAA Environment Data Deisred Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['mwaa:CreateEnvironment', 'mwaa:DeleteEnvironment'], diff --git a/plugins/aws/openSearchServerless/opensearchCollectionCmkEncrypted.js b/plugins/aws/openSearchServerless/opensearchCollectionCmkEncrypted.js index 94287c22cd..5e795789af 100644 --- a/plugins/aws/openSearchServerless/opensearchCollectionCmkEncrypted.js +++ b/plugins/aws/openSearchServerless/opensearchCollectionCmkEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'OpenSearch Collection Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['opensearchserverless:CreateCollection', 'opensearchserverless:DeleteCollection'], diff --git a/plugins/aws/opensearch/opensearchDomainEncryptionEnabled.js b/plugins/aws/opensearch/opensearchDomainEncryptionEnabled.js index 05821f3284..bebeb61710 100644 --- a/plugins/aws/opensearch/opensearchDomainEncryptionEnabled.js +++ b/plugins/aws/opensearch/opensearchDomainEncryptionEnabled.js @@ -16,7 +16,7 @@ module.exports = { name: 'OpenSearch Domain Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['opensearch:CreateDomain', 'opensearch:UpdateDomainConfig', 'opensearch:DeleteDomain'], diff --git a/plugins/aws/proton/environmentTemplateEncrypted.js b/plugins/aws/proton/environmentTemplateEncrypted.js index b1f9cadf41..0cf5634df5 100644 --- a/plugins/aws/proton/environmentTemplateEncrypted.js +++ b/plugins/aws/proton/environmentTemplateEncrypted.js @@ -16,7 +16,7 @@ module.exports = { name: 'Environment Template Desired Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['proton:CreateEnviromentTemplate', 'proton:DeleteEnviromentTemplate'], diff --git a/plugins/aws/qldb/ledgerEncrypted.js b/plugins/aws/qldb/ledgerEncrypted.js index 59e1760706..230c4fa5fd 100644 --- a/plugins/aws/qldb/ledgerEncrypted.js +++ b/plugins/aws/qldb/ledgerEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'QLDB ledger desired encryption level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['qldb:CreateLedger', 'qldb:UpdateLedger', 'qldb:DeleteLedger'], diff --git a/plugins/aws/s3/s3Encryption.js b/plugins/aws/s3/s3Encryption.js index 6289677708..f693811562 100644 --- a/plugins/aws/s3/s3Encryption.js +++ b/plugins/aws/s3/s3Encryption.js @@ -62,7 +62,7 @@ module.exports = { name: 'S3 Minimum Default Encryption Level', description: 'In order (low to high) sse=Server-Side Encryption; awskms=AWS KMS; awscmk=Customer KMS; externalcmk=Customer external KMS; cloudhsm=Customer CloudHSM', regex: '^(sse|awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'sse', }, s3_allow_unencrypted_static_websites: { name: 'S3 Allow Unencrypted Static Websites', diff --git a/plugins/aws/securityhub/securityHubEnabled.js b/plugins/aws/securityhub/securityHubEnabled.js index ceeb221127..68ef107182 100644 --- a/plugins/aws/securityhub/securityHubEnabled.js +++ b/plugins/aws/securityhub/securityHubEnabled.js @@ -28,7 +28,7 @@ module.exports = { } else if (describeHub.err || !describeHub.data) { helpers.addResult(results, 3, `Unable to query for Security Hub: ${helpers.addError(describeHub)}`, region); } else { - helpers.addResult(results, 0, 'Security Hub is enabled', region,describeHub.data.HubArn); + helpers.addResult(results, 0, 'Security Hub is enabled', region,describeHub.HubArn); } return rcb(); diff --git a/plugins/aws/ses/emailMessagesEncrypted.js b/plugins/aws/ses/emailMessagesEncrypted.js index 183dfc2193..58b13560ff 100644 --- a/plugins/aws/ses/emailMessagesEncrypted.js +++ b/plugins/aws/ses/emailMessagesEncrypted.js @@ -16,7 +16,7 @@ module.exports = { name: 'SES Email Desired Encryption Level', description: 'Desired encryption level for email messages to encrypt them before they get saves on S3', regex: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['ses:CreateEmailIdentity','ses:SetActiveReceiptRuleSet','ses:DeleteEmailIdentity'], diff --git a/plugins/aws/sqs/sqsEncryptionEnabled.js b/plugins/aws/sqs/sqsEncryptionEnabled.js index d4957752af..40eb3ab4d3 100644 --- a/plugins/aws/sqs/sqsEncryptionEnabled.js +++ b/plugins/aws/sqs/sqsEncryptionEnabled.js @@ -18,7 +18,7 @@ module.exports = { name: 'SQS Queues Target Encryption Level', description: 'In order (lowest to highest) sse=SSE-SQS; awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(sse|awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['sqs:CreateQueue', 'sqs:SetQueueAttributes', 'sqs:DeleteQueue'], diff --git a/plugins/aws/timestreamwrite/timestreamDatabaseEncrypted.js b/plugins/aws/timestreamwrite/timestreamDatabaseEncrypted.js index dd6b36ff2d..c8dbcd8db5 100644 --- a/plugins/aws/timestreamwrite/timestreamDatabaseEncrypted.js +++ b/plugins/aws/timestreamwrite/timestreamDatabaseEncrypted.js @@ -18,7 +18,7 @@ module.exports = { name: 'Timestream Database Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms' + default: 'awscmk' } }, realtime_triggers: ['timestreamwrite:CreateDatabase', 'timestreamwrite:UpdateDatabase', 'timestreamwrite:DeleteDatabase'], diff --git a/plugins/aws/translate/translateJobOutputEncrypted.js b/plugins/aws/translate/translateJobOutputEncrypted.js index e178e77e7d..feba36c78c 100644 --- a/plugins/aws/translate/translateJobOutputEncrypted.js +++ b/plugins/aws/translate/translateJobOutputEncrypted.js @@ -17,7 +17,7 @@ module.exports = { name: 'Translate Job Target Encryption Level', description: 'In order (lowest to highest) awskms=AWS-managed KMS; awscmk=Customer managed KMS; externalcmk=Customer managed externally sourced KMS; cloudhsm=Customer managed CloudHSM sourced KMS', regex: '^(awskms|awscmk|externalcmk|cloudhsm)$', - default: 'awskms', + default: 'awscmk', } }, realtime_triggers: ['translate:StartTextTranslationJob', 'translate:StopTextTranslationJob'], diff --git a/plugins/aws/translate/translateJobOutputEncrypted.spec.js b/plugins/aws/translate/translateJobOutputEncrypted.spec.js index 38eccaca76..7b870fb1f1 100644 --- a/plugins/aws/translate/translateJobOutputEncrypted.spec.js +++ b/plugins/aws/translate/translateJobOutputEncrypted.spec.js @@ -126,7 +126,7 @@ describe('translateJobOutputEncrypted', function () { it('should FAIL if Translate job is not encrypted with desired encryption level', function (done) { const cache = createCache([listTextTranslationJobs[1]], listKeys); - translateJobOutputEncrypted.run(cache, {translate_job_encryption_level: 'awscmk'}, (err, results) => { + translateJobOutputEncrypted.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); expect(results[0].region).to.equal('us-east-1'); diff --git a/plugins/azure/appservice/authEnabled.js b/plugins/azure/appservice/authEnabled.js index a50f69d139..e022e5713d 100644 --- a/plugins/azure/appservice/authEnabled.js +++ b/plugins/azure/appservice/authEnabled.js @@ -22,24 +22,12 @@ module.exports = { 'for auditing and security controls.', pci: 'Access to system components must be restricted to known users.' }, - settings: { - whitelist_functions_for_auth_enabled: { - name: 'Whitelist Functions For Authentication Enabled', - description: 'List of comma separated functions which should be whitelisted to check', - regex: '^.*$', - default: 'aqua-agentless-scanner-continuous-onboarding', - } - }, run: function(cache, settings, callback) { const results = []; const source = {}; const locations = helpers.locations(settings.govcloud); - let config = { - whitelist_functions_for_auth_enabled: settings.whitelist_functions_for_auth_enabled || this.settings.whitelist_functions_for_auth_enabled.default - }; - async.each(locations.webApps, function(location, rcb) { const webApps = helpers.addSource( @@ -63,24 +51,19 @@ module.exports = { webApps.data.forEach(function(webApp) { if (webApp.kind && webApp.kind.includes('workflowapp')) return; - if (webApp.name.includes(config.whitelist_functions_for_auth_enabled)) { - helpers.addResult(results, 0, 'The App Service is whitelisted', location, webApp.id); - } else { - - const authSettings = helpers.addSource( - cache, source, ['webApps', 'getAuthSettings', location, webApp.id] - ); + const authSettings = helpers.addSource( + cache, source, ['webApps', 'getAuthSettings', location, webApp.id] + ); - if (!authSettings || authSettings.err || !authSettings.data) { - helpers.addResult(results, 3, - 'Unable to query App Service: ' + helpers.addError(authSettings), - location, webApp.id); + if (!authSettings || authSettings.err || !authSettings.data) { + helpers.addResult(results, 3, + 'Unable to query App Service: ' + helpers.addError(authSettings), + location, webApp.id); + } else { + if (authSettings.data.enabled) { + helpers.addResult(results, 0, 'App Service has App Service Authentication enabled', location, webApp.id); } else { - if (authSettings.data.enabled) { - helpers.addResult(results, 0, 'App Service has App Service Authentication enabled', location, webApp.id); - } else { - helpers.addResult(results, 2, 'App Service does not have App Service Authentication enabled', location, webApp.id); - } + helpers.addResult(results, 2, 'App Service does not have App Service Authentication enabled', location, webApp.id); } } }); diff --git a/plugins/azure/appservice/authEnabled.spec.js b/plugins/azure/appservice/authEnabled.spec.js index eeafd97039..f2b88ffc38 100644 --- a/plugins/azure/appservice/authEnabled.spec.js +++ b/plugins/azure/appservice/authEnabled.spec.js @@ -71,41 +71,6 @@ describe('authEnabled', function() { auth.run(cache, {}, callback); }); - it('should give passing result if App Service is whitelisted', 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('App Service is whitelisted') - expect(results[0].region).to.equal('eastus') - done() - }; - - const cache = createCache( - null, - [ - { - "id": "/subscriptions/abcdef-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/devresourcegroup/providers/Microsoft.Web/sites/aqua-agentless-scanner-continuous-onboarding-i2hxc3is", - "name": "aqua-agentless-scanner-continuous-onboarding-i2hxc3is", - "type": "Microsoft.Web/sites", - "kind": "app,linux,container", - "location": "East US", - "state": "Running" - } - ], - { - "/subscriptions/abcdef-ebf6-437f-a3b0-28fc0d22117e/resourceGroups/devresourcegroup/providers/Microsoft.Web/sites/test-webapp": { - "data": { - "name": "authsettings", - "type": "Microsoft.Web/sites/config", - "enabled": false - } - } - } - ); - - auth.run(cache, {whitelist_functions_for_auth_enabled: 'aqua-agentless-scanner-continuous-onboarding'}, callback); - }); - it('should give passing result if enabled App Service', function(done) { const callback = (err, results) => { expect(results.length).to.equal(1) @@ -173,4 +138,4 @@ describe('authEnabled', function() { auth.run(cache, {}, callback); }); }) -}) +}) \ No newline at end of file diff --git a/plugins/azure/appservice/httpsOnlyEnabled.js b/plugins/azure/appservice/httpsOnlyEnabled.js index eb9400604f..2f15c12d17 100644 --- a/plugins/azure/appservice/httpsOnlyEnabled.js +++ b/plugins/azure/appservice/httpsOnlyEnabled.js @@ -26,24 +26,12 @@ module.exports = { 'App Service HTTPS redirection should be used to ensure site visitors ' + 'are always connecting over a secure channel.' }, - settings: { - whitelist_functions_for_https_only: { - name: 'Whitelist Functions For HTTPS Only', - description: 'List of comma separated functions which should be whitelisted to check', - regex: '^.*$', - default: 'aqua-agentless-scanner-continuous-onboarding', - } - }, run: function(cache, settings, callback) { const results = []; const source = {}; const locations = helpers.locations(settings.govcloud); - let config = { - whitelist_functions_for_https_only: settings.whitelist_functions_for_https_only || this.settings.whitelist_functions_for_https_only.default - }; - async.each(locations.webApps, function(location, rcb) { const webApps = helpers.addSource( @@ -64,16 +52,10 @@ module.exports = { } webApps.data.forEach(function(webApp) { - - if (webApp.name.includes(config.whitelist_functions_for_https_only)) { - helpers.addResult(results, 0, 'The App Service is whitelisted', location, webApp.id); + if (webApp.httpsOnly) { + helpers.addResult(results, 0, 'The App Service has HTTPS Only enabled', location, webApp.id); } else { - - if (webApp.httpsOnly) { - helpers.addResult(results, 0, 'The App Service has HTTPS Only enabled', location, webApp.id); - } else { - helpers.addResult(results, 2, 'The App Service does not have HTTPS Only enabled', location, webApp.id); - } + helpers.addResult(results, 2, 'The App Service does not have HTTPS Only enabled', location, webApp.id); } }); diff --git a/plugins/azure/appservice/httpsOnlyEnabled.spec.js b/plugins/azure/appservice/httpsOnlyEnabled.spec.js index 3aed7bb557..79feef203f 100644 --- a/plugins/azure/appservice/httpsOnlyEnabled.spec.js +++ b/plugins/azure/appservice/httpsOnlyEnabled.spec.js @@ -11,11 +11,6 @@ const webApps = [ 'id': '/subscriptions/123/resourceGroups/aqua-resource-group/providers/Microsoft.Web/sites/app1', 'name': 'app1', 'httpsOnly': false - }, - { - 'id': '/subscriptions/123/resourceGroups/aqua-resource-group/providers/Microsoft.Web/sites/aqua-agentless-scanner-continuous-onboarding-i2hxc3is', - 'name': 'aqua-agentless-scanner-continuous-onboarding-i2hxc3is', - 'httpsOnly': false } ]; @@ -86,16 +81,5 @@ describe('httpsOnlyEnabled', function() { done(); }); }); - - it('should PASS if app service gets whitelisted', function (done) { - const cache = createCache([webApps[2]]); - httpsOnlyEnabled.run(cache, { whitelist_functions_for_https_only:'aqua-agentless-scanner-continuous-onboarding' }, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('The App Service is whitelisted'); - expect(results[0].region).to.equal('eastus'); - done(); - }); - }); }); -}); +}); \ No newline at end of file diff --git a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js index 81ddbbb611..ed3b46012e 100644 --- a/plugins/azure/machinelearning/workspacePublicAccessDisabled.js +++ b/plugins/azure/machinelearning/workspacePublicAccessDisabled.js @@ -11,7 +11,7 @@ module.exports = { recommended_action: 'Ensure that Azure Machine Learning workspaces have public network access disabled.', link: 'https://learn.microsoft.com/en-us/azure/machine-learning/how-to-secure-workspace-vnet', apis: ['machineLearning:listWorkspaces'], - realtime_triggers: ['microsoft:machinelearningservices:workspaces:write', 'microsoft:machinelearningservices:workspaces:delete'], + realtime_triggers: ['microsoftcognitiveservices:accounts:write','microsoftcognitiveservices:accounts:delete'], run: function(cache, settings, callback) { const results = []; diff --git a/plugins/azure/securitycenter/appWhitelistingEnabled.js b/plugins/azure/securitycenter/appWhitelistingEnabled.js new file mode 100644 index 0000000000..9c6be1a148 --- /dev/null +++ b/plugins/azure/securitycenter/appWhitelistingEnabled.js @@ -0,0 +1,36 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Application Whitelisting Enabled', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that Security Center Monitor Adaptive Application Whitelisting is enabled', + more_info: 'Adaptive application controls work in conjunction with machine learning to analyze processes running in a VM and help control which applications can run, hardening the VM against malware.', + recommended_action: 'Enable Adaptive Application Controls for Virtual Machines from the Azure Security Center by ensuring AuditIfNotExists setting is used.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-adaptive-application', + apis: ['policyAssignments:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, function(location, rcb) { + const policyAssignments = helpers.addSource( + cache, source, ['policyAssignments', 'list', location] + ); + + helpers.checkPolicyAssignment(policyAssignments, + 'adaptiveApplicationControlsMonitoringEffect', + 'Monitor Adaptive Application Whitelisting', results, location); + + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/securitycenter/appWhitelistingEnabled.spec.js b/plugins/azure/securitycenter/appWhitelistingEnabled.spec.js new file mode 100644 index 0000000000..c1931e5d71 --- /dev/null +++ b/plugins/azure/securitycenter/appWhitelistingEnabled.spec.js @@ -0,0 +1,266 @@ +var expect = require('chai').expect; +var appWhitelistingEnabled = require('./appWhitelistingEnabled'); + +const policyAssignments = [ + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Disabled" + }, + "diskEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Disabled" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": "Disabled" + }, + "sqlAuditingMonitoringEffect": { + "value": "Disabled" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/7a9dabe0d6244e9683d56a79", + "type": "Microsoft.Authorization/policyAssignments", + "name": "7a9dabe0d6244e9683d56a79", + "location": "eastus", + "displayName": "Monitor unencrypted SASASA", + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0961003e-5a0a-4549-abde-af6a37f2724d", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "effect": { + "value": "Disabled" + } + }, + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "d0222e76-19f3-46f1-8705-af53043c1ff8", + "createdOn": "2019-04-18T00:34:32.2202483Z", + "updatedBy": null, + "updatedOn": null + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Disabled" + }, + "diskEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Disabled" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": '' + }, + "sqlAuditingMonitoringEffect": { + "value": "Disabled" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, +]; + +const createCache = (policyAssignment) => { + let settings = {}; + if (policyAssignment) { + settings['data'] = policyAssignment; + } + return { + policyAssignments: { + list: { + 'eastus': settings + } + } + }; +}; + +describe('appWhitelistingEnabled', function() { + describe('run', function() { + it('should give failing result if No existing Policy Assignments found', function(done) { + const cache = createCache([]); + appWhitelistingEnabled.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[1]]); + appWhitelistingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if Unable to query for Policy Assignments', function(done) { + const cache = createCache(); + appWhitelistingEnabled.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Monitor Adaptive Application Whitelisting enabled', function(done) { + const cache = createCache([policyAssignments[0]]); + appWhitelistingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Monitor Adaptive Application Whitelisting disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + appWhitelistingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/autoProvisioningEnabled.js b/plugins/azure/securitycenter/autoProvisioningEnabled.js new file mode 100644 index 0000000000..233572f2e3 --- /dev/null +++ b/plugins/azure/securitycenter/autoProvisioningEnabled.js @@ -0,0 +1,52 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Auto Provisioning Enabled', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that automatic provisioning of the monitoring agent is enabled', + more_info: 'The Microsoft Monitoring Agent scans for various security-related configurations and events such as system updates, OS vulnerabilities, and endpoint protection and provides alerts.', + recommended_action: 'Ensure that the data collection settings of the subscription have Auto Provisioning set to enabled.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-enable-data-collection', + apis: ['autoProvisioningSettings:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.autoProvisioningSettings, (location, rcb) => { + const autoProvisioningSettings = helpers.addSource( cache, source, + ['autoProvisioningSettings', 'list', location]); + + if (!autoProvisioningSettings) return rcb(); + + if (autoProvisioningSettings.err || !autoProvisioningSettings.data) { + helpers.addResult(results, 3, + 'Unable to query auto provisioning settings: ' + helpers.addError(autoProvisioningSettings),location); + return rcb(); + } + + if (!autoProvisioningSettings.data.length) { + helpers.addResult(results, 2, 'No existing auto provisioning settings found', location); + return rcb(); + } + + autoProvisioningSettings.data.forEach(function(aps){ + if (aps.autoProvision && aps.autoProvision.toLowerCase() == 'on') { + helpers.addResult(results, 0, 'Monitoring Agent Auto Provisioning is enabled', location, aps.id); + } else { + helpers.addResult(results, 2, 'Monitoring Agent Auto Provisioning is disabled', location, aps.id); + } + }); + + rcb(); + }, function(){ + // Global checking goes here + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/securitycenter/autoProvisioningEnabled.spec.js b/plugins/azure/securitycenter/autoProvisioningEnabled.spec.js new file mode 100644 index 0000000000..890d82bb8b --- /dev/null +++ b/plugins/azure/securitycenter/autoProvisioningEnabled.spec.js @@ -0,0 +1,85 @@ +var expect = require('chai').expect; +var autoProvisioningEnabled = require('./autoProvisioningEnabled'); + +const autoProvisioningSettings = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Security/autoProvisioningSettings/default', + 'name': 'default', + 'autoProvision': 'On' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Security/autoProvisioningSettings/default', + 'name': 'default', + 'autoProvision': 'Off' + } +]; + +const createCache = (autoProvisioningSettings) => { + return { + autoProvisioningSettings: { + list: { + global:{ + data: autoProvisioningSettings + } + } + } + }; +}; + +const createErrorCache = () => { + return { + autoProvisioningSettings: { + list: { + global: {} + } + } + }; +}; + +describe('autoProvisioningEnabled', function() { + describe('run', function() { + it('should give failing result if no auto provisioning settings', function(done) { + const cache = createCache([]); + autoProvisioningEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('No existing auto provisioning settings found'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give unknown result if unable to query auto provisioning settings', function(done) { + const cache = createErrorCache(); + autoProvisioningEnabled.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 auto provisioning settings'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give passing result if auto provisioning is enabled', function(done) { + const cache = createCache([autoProvisioningSettings[0]]); + autoProvisioningEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Monitoring Agent Auto Provisioning is enabled'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give failing result if auto provisioning is disabled', function(done) { + const cache = createCache([autoProvisioningSettings[1]]); + autoProvisioningEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Monitoring Agent Auto Provisioning is disabled'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/highSeverityAlertsEnabled.js b/plugins/azure/securitycenter/highSeverityAlertsEnabled.js new file mode 100644 index 0000000000..655a018159 --- /dev/null +++ b/plugins/azure/securitycenter/highSeverityAlertsEnabled.js @@ -0,0 +1,77 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +const SEVERITY_LEVELS = ['low', 'medium', 'high']; + +module.exports = { + title: 'High Severity Alerts Enabled', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that high severity alerts are enabled and properly configured.', + more_info: 'Enabling high severity alerts ensures that microsoft alerts for potential security issues are sent and allows for quick mitigation of the associated risks.', + recommended_action: 'Enable email alert notification and configure its severity level.', + link: 'https://learn.microsoft.com/en-us/azure/defender-for-cloud/configure-email-notifications', + apis: ['securityContactv2:listAll'], + settings: { + alert_notifications_min_severity_level: { + name: 'Alert Notifications Minimum Severity Level', + description: 'Security issues severity level for which notifications should be sent. Use "low" option to receive notification for all security issues.', + regex: '^(high|medium|low)$', + default: 'medium' + } + }, + realtime_triggers: ['microsoftsecurity:securitycontacts:write','microsoftsecurity:securitycontacts:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + const config = { + alert_notifications_min_severity_level: settings.alert_notifications_min_severity_level || this.settings.alert_notifications_min_severity_level.default + }; + + let desiredSeverityLevel = SEVERITY_LEVELS.indexOf(config.alert_notifications_min_severity_level.toLowerCase()); + + async.each(locations.securityContactv2, (location, rcb) => { + var securityContacts = helpers.addSource(cache, source, + ['securityContactv2', 'listAll', location]); + if (!securityContacts) return rcb(); + + if (securityContacts.err || !securityContacts.data) { + helpers.addResult(results, 3, + 'Unable to query for security contacts: ' + helpers.addError(securityContacts), location); + return rcb(); + } + + if (!securityContacts.data.length) { + helpers.addResult(results, 2, 'No existing security contacts found', location); + return rcb(); + } + + for (let contact of securityContacts.data) { + if (!contact.id) continue; + + if ( contact.alertNotifications && contact.alertNotifications.state && + contact.alertNotifications.state.toLowerCase() === 'off') { + helpers.addResult(results, 2, 'Security contacts email alert notification are not enabled', location, contact.id); + } else { + let currentSeverityLevel = contact.alertNotifications.minimalSeverity.toLowerCase(); + if (contact.alertNotifications.minimalSeverity && + SEVERITY_LEVELS.indexOf(currentSeverityLevel) >= desiredSeverityLevel) { + helpers.addResult(results, 0, `Security contacts email alert notifications enabled with minimum severity level + ${currentSeverityLevel} which is greater or equal to + the desired severity level ${SEVERITY_LEVELS[desiredSeverityLevel]}`, location, contact.id); + } else { + helpers.addResult(results, 2, `Security contacts email alert notifications enabled with minimum severity + level ${currentSeverityLevel} which is less than the desired severity level ${SEVERITY_LEVELS[desiredSeverityLevel]}`, location, contact.id); + } + } + } + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/securitycenter/highSeverityAlertsEnabled.spec.js b/plugins/azure/securitycenter/highSeverityAlertsEnabled.spec.js new file mode 100644 index 0000000000..7e95b5d1f5 --- /dev/null +++ b/plugins/azure/securitycenter/highSeverityAlertsEnabled.spec.js @@ -0,0 +1,128 @@ +var expect = require('chai').expect; +var highSeverityAlertsEnabled = require('./highSeverityAlertsEnabled'); + +const securityContacts = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'alertsToAdmins': 'On', + 'email': 'xyz@gmail.com', + + "notificationsByRole": { + "state": "On", + "roles": [ + "Owner", + ] + }, + alertNotifications : { state: "On", minimalSeverity: 'High' }, + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'alertsToAdmins': 'Off', + 'email': '', + + "notificationsByRole": { + "state": "On", + "roles": [ + "Admin" + ] + }, + alertNotifications : { state: "On", minimalSeverity: 'Low' }, + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'alertsToAdmins': 'Off', + 'email': '', + + "notificationsByRole": { + "state": "On", + "roles": [ + "Admin" + ] + }, + alertNotifications : { state: "Off", minimalSeverity: 'Low' }, + } +]; + +const createCache = (securityContacts) => { + return { + securityContactv2: { + listAll: { + global:{ + data: securityContacts + } + } + } + }; +}; + +const createErrorCache = () => { + return { + securityContactv2: { + listAll: { + global: {} + } + } + }; +}; + +describe('highSeverityAlertsEnabled', function() { + describe('run', function() { + it('should give failing result if no security contacts', function(done) { + const cache = createCache([]); + highSeverityAlertsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('No existing security contacts found'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give unknown result if unable to query for security contacts', function(done) { + const cache = createErrorCache(); + highSeverityAlertsEnabled.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 security contacts'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give passing result if Security Contact email alert severity is greater or equal then desired', function(done) { + const cache = createCache([securityContacts[0]]); + highSeverityAlertsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Security contacts email alert notifications enabled with minimum severity level'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give failing result if Security Contact email alert severity is less then desired', function(done) { + const cache = createCache([securityContacts[1]]); + highSeverityAlertsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Security contacts email alert notifications enabled with minimum severity'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give failing result if Security Contact email alert notification not enabled', function(done) { + const cache = createCache([securityContacts[2]]); + highSeverityAlertsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Security contacts email alert notification are not enabled'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorEndpointProtection.js b/plugins/azure/securitycenter/monitorEndpointProtection.js new file mode 100644 index 0000000000..130943e89c --- /dev/null +++ b/plugins/azure/securitycenter/monitorEndpointProtection.js @@ -0,0 +1,36 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Monitor Endpoint Protection', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures Endpoint Protection monitoring is enabled in Security Center', + more_info: 'When this setting is enabled, Security Center audits the Endpoint Protection setting for all virtual machines for malware protection.', + recommended_action: 'Enable Adaptive Application Controls for Endpoint Protection from the Azure Security Center by ensuring AuditIfNotExists setting is used to monitor missing Endpoint Protection.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-policy-definitions', + apis: ['policyAssignments:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, (location, rcb) => { + + const policyAssignments = helpers.addSource(cache, source, + ['policyAssignments', 'list', location]); + + helpers.checkPolicyAssignment(policyAssignments, + 'endpointProtectionMonitoringEffect', + 'Monitor Endpoint Protection', results, location); + + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorEndpointProtection.spec.js b/plugins/azure/securitycenter/monitorEndpointProtection.spec.js new file mode 100644 index 0000000000..a68276d09a --- /dev/null +++ b/plugins/azure/securitycenter/monitorEndpointProtection.spec.js @@ -0,0 +1,266 @@ +var expect = require('chai').expect; +var monitorEndpointProtection = require('./monitorEndpointProtection'); + +const policyAssignments = [ + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Audit" + }, + "diskEncryptionMonitoringEffect": { + "value": "Audit" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Disabled" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": "Disabled" + }, + "sqlAuditingMonitoringEffect": { + "value": "Disabled" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/7a9dabe0d6244e9683d56a79", + "type": "Microsoft.Authorization/policyAssignments", + "name": "7a9dabe0d6244e9683d56a79", + "location": "eastus", + "displayName": "Monitor unencrypted SASASA", + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0961003e-5a0a-4549-abde-af6a37f2724d", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "effect": { + "value": "Disabled" + } + }, + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "d0222e76-19f3-46f1-8705-af53043c1ff8", + "createdOn": "2019-04-18T00:34:32.2202483Z", + "updatedBy": null, + "updatedOn": null + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Disabled" + }, + "diskEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Disabled" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": '' + }, + "sqlAuditingMonitoringEffect": { + "value": "Disabled" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, +]; + +const createCache = (policyAssignment) => { + let settings = {}; + if (policyAssignment) { + settings['data'] = policyAssignment; + } + return { + policyAssignments: { + list: { + 'eastus': settings + } + } + }; +}; + +describe('monitorEndpointProtection', function() { + describe('run', function() { + it('should give failing result if No existing Policy Assignments found', function(done) { + const cache = createCache([]); + monitorEndpointProtection.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[1]]); + monitorEndpointProtection.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if Unable to query for Policy Assignments', function(done) { + const cache = createCache(); + monitorEndpointProtection.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Monitor Endpoint Protection enabled', function(done) { + const cache = createCache([policyAssignments[0]]); + monitorEndpointProtection.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Monitor Endpoint Protection disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + monitorEndpointProtection.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorExternalAccounts.js b/plugins/azure/securitycenter/monitorExternalAccounts.js new file mode 100644 index 0000000000..841068fc92 --- /dev/null +++ b/plugins/azure/securitycenter/monitorExternalAccounts.js @@ -0,0 +1,36 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Monitor External Accounts with Write Permissions', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that External Accounts with Write Permissions are being Monitored in Security Center', + more_info: 'External Accounts with Write Permissions should be monitored to meet you organization\'s security compliance requirements.', + recommended_action: 'Enable Monitor for External Accounts with Write Permissions by ensuring AuditIfNotExists setting is used for \'External accounts with write permissions should be removed from your subscription\' from the Azure Security Center.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-policy-definitions', + apis: ['policyAssignments:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, function(location, rcb) { + + const policyAssignments = helpers.addSource(cache, source, + ['policyAssignments', 'list', location]); + + helpers.checkPolicyAssignment(policyAssignments, + 'identityRemoveExternalAccountWithWritePermissionsMonitoringEffect', + 'Monitor for External Accounts with Write Permissions', results, location); + + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorExternalAccounts.spec.js b/plugins/azure/securitycenter/monitorExternalAccounts.spec.js new file mode 100644 index 0000000000..91f453537c --- /dev/null +++ b/plugins/azure/securitycenter/monitorExternalAccounts.spec.js @@ -0,0 +1,132 @@ +var expect = require('chai').expect; +var monitorExternalAccounts = require('./monitorExternalAccounts'); + +const policyAssignments = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/456', + 'displayName': 'Test Policy', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': '456', + 'location': 'eastus' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'identityRemoveExternalAccountWithWritePermissionsMonitoringEffect': { + 'value': 'AuditIfNotExists' + } + } + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'identityRemoveExternalAccountWithWritePermissionsMonitoringEffect': { + 'value': 'Disabled' + } + } + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'testParam': { + 'value': 'Disabled' + } + } + } +]; + +const createCache = (policyAssignments) => { + let assignment = {}; + if (policyAssignments) { + assignment['data'] = policyAssignments; + } + return { + policyAssignments: { + list: { + 'eastus': assignment + } + }, + }; +}; + +describe('monitorExternalAccounts', function() { + describe('run', function() { + it('should give passing result if no policy assignments', function(done) { + const cache = createCache([]); + monitorExternalAccounts.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if unable to query for Policy Assignments', function(done) { + const cache = createCache(null); + monitorExternalAccounts.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[0]]); + monitorExternalAccounts.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if external accounts with write permissions monitoring is enabled', function(done) { + const cache = createCache([policyAssignments[1]]); + monitorExternalAccounts.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Monitor for External Accounts with Write Permissions is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if external accounts with write permissions monitoring is disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + monitorExternalAccounts.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Monitor for External Accounts with Write Permissions is disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if external accounts with write permissions monitoring is enabled by default', function (done) { + const cache = createCache([policyAssignments[3]]); + monitorExternalAccounts.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Monitor for External Accounts with Write Permissions is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorIpForwarding.js b/plugins/azure/securitycenter/monitorIpForwarding.js new file mode 100644 index 0000000000..1575065008 --- /dev/null +++ b/plugins/azure/securitycenter/monitorIpForwarding.js @@ -0,0 +1,36 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Monitor IP Forwarding', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that Virtual Machine IP Forwarding Monitoring is enabled in Security Center', + more_info: 'IP Forwarding feature should be monitored to meet you organization\'s security compliance requirements.', + recommended_action: 'Enable IP Forwarding Monitoring by ensuring AuditIfNotExists setting is used for \'IP Forwarding on your virtual machine should be disabled\' from the Azure Security Center.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-policy-definitions', + apis: ['policyAssignments:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, function(location, rcb) { + + const policyAssignments = helpers.addSource(cache, source, + ['policyAssignments', 'list', location]); + + helpers.checkPolicyAssignment(policyAssignments, + 'disableIPForwardingMonitoringEffect', + 'IP Forwarding Monitoring', results, location); + + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorIpForwarding.spec.js b/plugins/azure/securitycenter/monitorIpForwarding.spec.js new file mode 100644 index 0000000000..d200e72de1 --- /dev/null +++ b/plugins/azure/securitycenter/monitorIpForwarding.spec.js @@ -0,0 +1,132 @@ +var expect = require('chai').expect; +var monitorIpForwarding = require('./monitorIpForwarding'); + +const policyAssignments = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/456', + 'displayName': 'Test Policy', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': '456', + 'location': 'eastus' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'disableIPForwardingMonitoringEffect': { + 'value': 'AuditIfNotExists' + } + } + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'disableIPForwardingMonitoringEffect': { + 'value': 'Disabled' + } + } + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'testParam': { + 'value': 'Disabled' + } + } + } +]; + +const createCache = (policyAssignments) => { + let assignment = {}; + if (policyAssignments) { + assignment['data'] = policyAssignments; + } + return { + policyAssignments: { + list: { + 'eastus': assignment + } + }, + }; +}; + +describe('monitorIpForwarding', function() { + describe('run', function() { + it('should give passing result if no policy assignments', function(done) { + const cache = createCache([]); + monitorIpForwarding.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if unable to query for Policy Assignments', function(done) { + const cache = createCache(null); + monitorIpForwarding.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[0]]); + monitorIpForwarding.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if IP Forwarding Monitoring is enabled', function(done) { + const cache = createCache([policyAssignments[1]]); + monitorIpForwarding.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('IP Forwarding Monitoring is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if IP Forwarding Monitoring is disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + monitorIpForwarding.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('IP Forwarding Monitoring is disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if external accounts with write permissions monitoring is enabled by default', function (done) { + const cache = createCache([policyAssignments[3]]); + monitorIpForwarding.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('IP Forwarding Monitoring is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorJitNetworkAccess.js b/plugins/azure/securitycenter/monitorJitNetworkAccess.js new file mode 100644 index 0000000000..19d98fb555 --- /dev/null +++ b/plugins/azure/securitycenter/monitorJitNetworkAccess.js @@ -0,0 +1,35 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Monitor JIT Network Access', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures Just In Time Network Access monitoring is enabled in Security Center', + more_info: 'When this setting is enabled, Security Center audits Just In Time Network Access on all virtual machines (Windows and Linux as well) to enhance data protection at rest', + recommended_action: 'Ensure JIT Network Access monitoring is configured for compute and apps from the Azure Security Center.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-policy-definitions', + apis: ['policyAssignments:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, (location, rcb) => { + const policyAssignments = helpers.addSource(cache, source, + ['policyAssignments', 'list', location]); + + helpers.checkPolicyAssignment(policyAssignments, + 'jitNetworkAccessMonitoringEffect', + 'Monitor JIT Network Access', results, location); + + rcb(); + }, function(){ + // Global checking goes here + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/securitycenter/monitorJitNetworkAccess.spec.js b/plugins/azure/securitycenter/monitorJitNetworkAccess.spec.js new file mode 100644 index 0000000000..979a6c0668 --- /dev/null +++ b/plugins/azure/securitycenter/monitorJitNetworkAccess.spec.js @@ -0,0 +1,266 @@ +var expect = require('chai').expect; +var monitorJitNetworkAccess = require('./monitorJitNetworkAccess'); + +const policyAssignments = [ + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Audit" + }, + "diskEncryptionMonitoringEffect": { + "value": "Audit" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Disabled" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Audit" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": "Disabled" + }, + "sqlAuditingMonitoringEffect": { + "value": "Disabled" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/7a9dabe0d6244e9683d56a79", + "type": "Microsoft.Authorization/policyAssignments", + "name": "7a9dabe0d6244e9683d56a79", + "location": "eastus", + "displayName": "Monitor unencrypted SASASA", + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0961003e-5a0a-4549-abde-af6a37f2724d", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "effect": { + "value": "Disabled" + } + }, + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "d0222e76-19f3-46f1-8705-af53043c1ff8", + "createdOn": "2019-04-18T00:34:32.2202483Z", + "updatedBy": null, + "updatedOn": null + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Disabled" + }, + "diskEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Disabled" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": '' + }, + "sqlAuditingMonitoringEffect": { + "value": "Disabled" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, +]; + +const createCache = (policyAssignment) => { + let settings = {}; + if (policyAssignment) { + settings['data'] = policyAssignment; + } + return { + policyAssignments: { + list: { + 'eastus': settings + } + } + }; +}; + +describe('monitorJitNetworkAccess', function() { + describe('run', function() { + it('should give failing result if No existing Policy Assignments found', function(done) { + const cache = createCache([]); + monitorJitNetworkAccess.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[1]]); + monitorJitNetworkAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if Unable to query for Policy Assignments', function(done) { + const cache = createCache(); + monitorJitNetworkAccess.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Monitor JIT Network Access enabled', function(done) { + const cache = createCache([policyAssignments[0]]); + monitorJitNetworkAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Monitor JIT Network Access disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + monitorJitNetworkAccess.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorNextGenerationFirewall.js b/plugins/azure/securitycenter/monitorNextGenerationFirewall.js new file mode 100644 index 0000000000..8a6fa27b76 --- /dev/null +++ b/plugins/azure/securitycenter/monitorNextGenerationFirewall.js @@ -0,0 +1,36 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Monitor Next Generation Firewall', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that Next Generation Firewall (NGFW) Monitoring is enabled in Security Center', + more_info: 'When this setting is enabled, Security Center will search for deployments where a NGFW is recommended.', + recommended_action: 'Enable Next Generation Firewall Monitoring by ensuring AuditIfNotExists setting is used for \'All network ports should be restricted on network security groups associated to your virtual machine\' from the Azure Security Center.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-policy-definitions', + apis: ['policyAssignments:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, function(location, rcb) { + + const policyAssignments = helpers.addSource(cache, source, + ['policyAssignments', 'list', location]); + + helpers.checkPolicyAssignment(policyAssignments, + 'nextGenerationFirewallMonitoringEffect', + 'Next Generation Firewall Monitoring', results, location); + + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorNextGenerationFirewall.spec.js b/plugins/azure/securitycenter/monitorNextGenerationFirewall.spec.js new file mode 100644 index 0000000000..ba5564dcd6 --- /dev/null +++ b/plugins/azure/securitycenter/monitorNextGenerationFirewall.spec.js @@ -0,0 +1,132 @@ +var expect = require('chai').expect; +var monitorNextGenerationFirewall = require('./monitorNextGenerationFirewall'); + +const policyAssignments = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/456', + 'displayName': 'Test Policy', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': '456', + 'location': 'eastus' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'nextGenerationFirewallMonitoringEffect': { + 'value': 'AuditIfNotExists' + } + } + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'nextGenerationFirewallMonitoringEffect': { + 'value': 'Disabled' + } + } + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'testParam': { + 'value': 'Disabled' + } + } + } +]; + +const createCache = (policyAssignments) => { + let assignment = {}; + if (policyAssignments) { + assignment['data'] = policyAssignments; + } + return { + policyAssignments: { + list: { + 'eastus': assignment + } + }, + }; +}; + +describe('monitorNextGenerationFirewall', function() { + describe('run', function() { + it('should give passing result if no policy assignments', function(done) { + const cache = createCache([]); + monitorNextGenerationFirewall.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if unable to query for Policy Assignments', function(done) { + const cache = createCache(null); + monitorNextGenerationFirewall.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[0]]); + monitorNextGenerationFirewall.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Next Generation Firewall Monitoring is enabled', function(done) { + const cache = createCache([policyAssignments[1]]); + monitorNextGenerationFirewall.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Next Generation Firewall Monitoring is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Next Generation Firewall Monitoring is disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + monitorNextGenerationFirewall.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Next Generation Firewall Monitoring is disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if external accounts with write permissions monitoring is enabled by default', function (done) { + const cache = createCache([policyAssignments[3]]); + monitorNextGenerationFirewall.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Next Generation Firewall Monitoring is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorSubscriptionOwners.js b/plugins/azure/securitycenter/monitorSubscriptionOwners.js new file mode 100644 index 0000000000..dcd29b4c63 --- /dev/null +++ b/plugins/azure/securitycenter/monitorSubscriptionOwners.js @@ -0,0 +1,36 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Monitor Total Number of Subscription Owners', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that Total Number of Subscription Owners is being Monitored in Security Center', + more_info: 'Total Number of Subscription Owners should be monitored to meet you organization\'s security compliance requirements.', + recommended_action: 'Enable Monitor for Total Number of Subscription Owners by ensuring AuditIfNotExists setting is used for \'A maximum of 3 owners should be designated for your subscription\' from the Azure Security Center.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-policy-definitions', + apis: ['policyAssignments:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, function(location, rcb) { + + const policyAssignments = helpers.addSource(cache, source, + ['policyAssignments', 'list', location]); + + helpers.checkPolicyAssignment(policyAssignments, + 'identityDesignateLessThanOwnersMonitoringEffect', + 'Monitor for Total Number of Subscription Owners', results, location); + + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorSubscriptionOwners.spec.js b/plugins/azure/securitycenter/monitorSubscriptionOwners.spec.js new file mode 100644 index 0000000000..399b54f216 --- /dev/null +++ b/plugins/azure/securitycenter/monitorSubscriptionOwners.spec.js @@ -0,0 +1,132 @@ +var expect = require('chai').expect; +var monitorSubscriptionOwners = require('./monitorSubscriptionOwners'); + +const policyAssignments = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/456', + 'displayName': 'Test Policy', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': '456', + 'location': 'eastus' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'identityDesignateLessThanOwnersMonitoringEffect': { + 'value': 'AuditIfNotExists' + } + } + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'identityDesignateLessThanOwnersMonitoringEffect': { + 'value': 'Disabled' + } + } + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn', + 'displayName': 'ASC Default (subscription: 123)', + 'type': 'Microsoft.Authorization/policyAssignments', + 'name': 'SecurityCenterBuiltIn', + 'location': 'eastus', + 'parameters': { + 'testParam': { + 'value': 'Disabled' + } + } + } +]; + +const createCache = (policyAssignments) => { + let assignment = {}; + if (policyAssignments) { + assignment['data'] = policyAssignments; + } + return { + policyAssignments: { + list: { + 'eastus': assignment + } + }, + }; +}; + +describe('monitorSubscriptionOwners', function() { + describe('run', function() { + it('should give passing result if no policy assignments', function(done) { + const cache = createCache([]); + monitorSubscriptionOwners.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if unable to query for Policy Assignments', function(done) { + const cache = createCache(null); + monitorSubscriptionOwners.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[0]]); + monitorSubscriptionOwners.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Monitor for Total Number of Subscription Owners is enabled', function(done) { + const cache = createCache([policyAssignments[1]]); + monitorSubscriptionOwners.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Monitor for Total Number of Subscription Owners is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Monitor for Total Number of Subscription Owners is disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + monitorSubscriptionOwners.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Monitor for Total Number of Subscription Owners is disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if external accounts with write permissions monitoring is enabled by default', function (done) { + const cache = createCache([policyAssignments[3]]); + monitorSubscriptionOwners.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Monitor for Total Number of Subscription Owners is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorSystemUpdates.js b/plugins/azure/securitycenter/monitorSystemUpdates.js new file mode 100644 index 0000000000..d3a33dd8aa --- /dev/null +++ b/plugins/azure/securitycenter/monitorSystemUpdates.js @@ -0,0 +1,39 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Monitor System Updates', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that Monitor System Updates is enabled in Security Center', + more_info: 'When this setting is enabled, Security Center will audit virtual machines for pending OS or system updates.', + recommended_action: 'Ensure System Update monitoring is configured for virtual machines from the Azure Security Center.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-policy-definitions', + apis: ['policyAssignments:list'], + compliance: { + pci: 'PCI requires all system components have the latest updates ' + + 'and patches installed within a month of release.' + }, + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, (location, rcb) => { + const policyAssignments = helpers.addSource(cache, source, + ['policyAssignments', 'list', location]); + + helpers.checkPolicyAssignment(policyAssignments, + 'systemUpdatesMonitoringEffect', + 'Monitor System Updates', results, location); + + rcb(); + }, function(){ + // Global checking goes here + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/azure/securitycenter/monitorSystemUpdates.spec.js b/plugins/azure/securitycenter/monitorSystemUpdates.spec.js new file mode 100644 index 0000000000..ae090ca56c --- /dev/null +++ b/plugins/azure/securitycenter/monitorSystemUpdates.spec.js @@ -0,0 +1,272 @@ +var expect = require('chai').expect; +var monitorSystemUpdates = require('./monitorSystemUpdates'); + +const policyAssignments = [ + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemUpdatesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Audit" + }, + "diskEncryptionMonitoringEffect": { + "value": "Audit" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Audit" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Audit" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": "Disabled" + }, + "sqlAuditingMonitoringEffect": { + "value": "Audit" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Audit" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/7a9dabe0d6244e9683d56a79", + "type": "Microsoft.Authorization/policyAssignments", + "name": "7a9dabe0d6244e9683d56a79", + "location": "eastus", + "displayName": "Monitor unencrypted SASASA", + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0961003e-5a0a-4549-abde-af6a37f2724d", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "effect": { + "value": "Disabled" + } + }, + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "d0222e76-19f3-46f1-8705-af53043c1ff8", + "createdOn": "2019-04-18T00:34:32.2202483Z", + "updatedBy": null, + "updatedOn": null + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemUpdatesMonitoringEffect": { + "value": "Disabled" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Disabled" + }, + "diskEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Disabled" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": '' + }, + "sqlAuditingMonitoringEffect": { + "value": "Disabled" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, +]; + +const createCache = (policyAssignment) => { + let settings = {}; + if (policyAssignment) { + settings['data'] = policyAssignment; + } + return { + policyAssignments: { + list: { + 'eastus': settings + } + } + }; +}; + +describe('monitorSystemUpdates', function() { + describe('run', function() { + it('should give failing result if No existing Policy Assignments found', function(done) { + const cache = createCache([]); + monitorSystemUpdates.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[1]]); + monitorSystemUpdates.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if Unable to query for Policy Assignments', function(done) { + const cache = createCache(); + monitorSystemUpdates.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Monitor System Updates enabled', function(done) { + const cache = createCache([policyAssignments[0]]); + monitorSystemUpdates.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Monitor System Updates disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + monitorSystemUpdates.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/securityConfigMonitoring.js b/plugins/azure/securitycenter/securityConfigMonitoring.js new file mode 100644 index 0000000000..f4e8336308 --- /dev/null +++ b/plugins/azure/securitycenter/securityConfigMonitoring.js @@ -0,0 +1,36 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Security Configuration Monitoring', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that Security Configuration Monitoring is enabled in Security Center', + more_info: 'When this setting is enabled, Security Center will monitor virtual machines for security configurations.', + recommended_action: 'Ensure Security Configuration Monitoring is configured for virtual machines from the Azure Security Center.', + link: 'https://learn.microsoft.com/en-us/azure/governance/policy/overview', + apis: ['policyAssignments:list'], + realtime_triggers: ['microsoftauthorization:policyassignments:write','microsoftauthorization:policyassignments:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.policyAssignments, function(location, rcb) { + + const policyAssignments = helpers.addSource(cache, source, + ['policyAssignments', 'list', location]); + + helpers.checkPolicyAssignment(policyAssignments, + 'systemConfigurationsMonitoringEffect', + 'Monitor Security Configuration', results, location); + + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/securitycenter/securityConfigMonitoring.spec.js b/plugins/azure/securitycenter/securityConfigMonitoring.spec.js new file mode 100644 index 0000000000..785d91e428 --- /dev/null +++ b/plugins/azure/securitycenter/securityConfigMonitoring.spec.js @@ -0,0 +1,272 @@ +var expect = require('chai').expect; +var securityConfigMonitoring = require('./securityConfigMonitoring'); + +const policyAssignments = [ + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemUpdatesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemConfigurationsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "endpointProtectionMonitoringEffect": { + "value": "Audit" + }, + "diskEncryptionMonitoringEffect": { + "value": "Audit" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Audit" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Audit" + }, + "storageEncryptionMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Audit" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": "Disabled" + }, + "sqlAuditingMonitoringEffect": { + "value": "Audit" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Audit" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/7a9dabe0d6244e9683d56a79", + "type": "Microsoft.Authorization/policyAssignments", + "name": "7a9dabe0d6244e9683d56a79", + "location": "eastus", + "displayName": "Monitor unencrypted SASASA", + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0961003e-5a0a-4549-abde-af6a37f2724d", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "effect": { + "value": "Disabled" + } + }, + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "d0222e76-19f3-46f1-8705-af53043c1ff8", + "createdOn": "2019-04-18T00:34:32.2202483Z", + "updatedBy": null, + "updatedOn": null + }, + "enforcementMode": "Default" + }, + { + "sku": { + "name": "A0", + "tier": "Free" + }, + "id": "/subscriptions/1234/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn", + "type": "Microsoft.Authorization/policyAssignments", + "name": "SecurityCenterBuiltIn", + "location": "eastus", + "displayName": "ASC Default (subscription: 1234)", + "policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8", + "scope": "/subscriptions/1234", + "notScopes": [], + "parameters": { + "vmssOsVulnerabilitiesMonitoringEffect": { + "value": "AuditIfNotExists" + }, + "systemUpdatesMonitoringEffect": { + "value": "Disabled" + }, + "systemConfigurationsMonitoringEffect": { + "value": "Disabled" + }, + "endpointProtectionMonitoringEffect": { + "value": "Disabled" + }, + "diskEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "networkSecurityGroupsMonitoringEffect": { + "value": "Disabled" + }, + "nextGenerationFirewallMonitoringEffect": { + "value": "Disabled" + }, + "vulnerabilityAssesmentMonitoringEffect": { + "value": "Disabled" + }, + "storageEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "jitNetworkAccessMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsMonitoringEffect": { + "value": "Disabled" + }, + "adaptiveApplicationControlsUpdateMonitoringEffect": { + "value": '' + }, + "sqlAuditingMonitoringEffect": { + "value": "Disabled" + }, + "sqlEncryptionMonitoringEffect": { + "value": "Disabled" + }, + "sqlServerAuditingMonitoringEffect": { + "value": "Disabled" + }, + "secureTransferToStorageAccountMonitoringEffect": { + "value": "Disabled" + }, + "identityDesignateLessThanOwnersMonitoringEffect": { + "value": "Disabled" + }, + "identityRemoveExternalAccountWithWritePermissionsMonitoringEffect": { + "value": "Disabled" + }, + "disableIPForwardingMonitoringEffect": { + "value": "Disabled" + } + }, + "description": "This is the default set of policies monitored by Azure Security Center. It was automatically assigned as part of onboarding to Security Center. The default assignment contains only audit policies. For more information please visit https://aka.ms/ascpolicies", + "metadata": { + "assignedBy": "cariel@cloudsploit.com ", + "parameterScopes": {}, + "createdBy": "709d03b9-72f9-4c49-ba6e-935fd066886f", + "createdOn": "2019-02-22T01:37:48.8576719Z", + "updatedBy": "ef21d0c2-9e1a-422c-b324-c39f584fd4b9", + "updatedOn": "2021-06-30T01:58:11.9141222Z" + }, + "enforcementMode": "Default" + }, +]; + +const createCache = (policyAssignment) => { + let settings = {}; + if (policyAssignment) { + settings['data'] = policyAssignment; + } + return { + policyAssignments: { + list: { + 'eastus': settings + } + } + }; +}; + +describe('securityConfigMonitoring', function() { + describe('run', function() { + it('should give failing result if No existing Policy Assignments found', function(done) { + const cache = createCache([]); + securityConfigMonitoring.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 Policy Assignments found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if There are no ASC Default Policy Assignments', function(done) { + const cache = createCache([policyAssignments[1]]); + securityConfigMonitoring.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('There are no ASC Default Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if Unable to query for Policy Assignments', function(done) { + const cache = createCache(); + securityConfigMonitoring.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 Policy Assignments'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if Monitor Security Configuration enabled', function(done) { + const cache = createCache([policyAssignments[0]]); + securityConfigMonitoring.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('is enabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if Monitor Security Configuration disabled', function(done) { + const cache = createCache([policyAssignments[2]]); + securityConfigMonitoring.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('disabled'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/securityContactAdditionalEmail.js b/plugins/azure/securitycenter/securityContactAdditionalEmail.js new file mode 100644 index 0000000000..874153a103 --- /dev/null +++ b/plugins/azure/securitycenter/securityContactAdditionalEmail.js @@ -0,0 +1,52 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Security Contact Additional Email', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Low', + description: 'Ensure Additional email addresses are configured with security contact email.', + more_info: 'Microsoft Defender for Cloud emails the Subscription Owner to notify them about security alerts. Adding your Security Contact\'s email address to the Additional email addresses field ensures that your organization\'s Security Team is included in these alerts. This ensures that the proper people are aware of any potential compromise in order to mitigate the risk in a timely fashion.', + recommended_action: 'Modify security contact information and add additional emails.', + link: 'https://learn.microsoft.com/en-us/azure/defender-for-cloud/configure-email-notifications', + apis: ['securityContactv2:listAll'], + realtime_triggers: ['microsoftsecurity:securitycontacts:write','microsoftsecurity:securitycontacts:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.securityContacts, (location, rcb) => { + + var securityContacts = helpers.addSource(cache, source, + ['securityContactv2', 'listAll', location]); + + if (!securityContacts) return rcb(); + + if (securityContacts.err || !securityContacts.data) { + helpers.addResult(results, 3, + 'Unable to query for security contacts: ' + helpers.addError(securityContacts), location); + return rcb(); + } + + if (!securityContacts.data.length) { + helpers.addResult(results, 2, 'No existing security contacts', location); + return rcb(); + } + + let additionalEmails = securityContacts.data.find(contact => contact.emails && contact.emails.length); + + if (additionalEmails){ + helpers.addResult(results, 0, 'Additional email address is configured with security contact email', location); + } else { + helpers.addResult(results, 2, 'Additional email address is not configured with security contact email', location); + } + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/securitycenter/securityContactAdditionalEmail.spec.js b/plugins/azure/securitycenter/securityContactAdditionalEmail.spec.js new file mode 100644 index 0000000000..84e7de6f7b --- /dev/null +++ b/plugins/azure/securitycenter/securityContactAdditionalEmail.spec.js @@ -0,0 +1,87 @@ +var expect = require('chai').expect; +var securityContactAdditionalEmail = require('./securityContactAdditionalEmail'); + +const securityContacts = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'alertsToAdmins': 'On', + 'emails': 'xyz@gmail.com;abc@email.com' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'alertsToAdmins': 'Off', + 'emails': '' + } +]; + +const createCache = (securityContacts) => { + return { + securityContactv2: { + listAll: { + global:{ + data: securityContacts + } + } + } + }; +}; + +const createErrorCache = () => { + return { + securityContactv2: { + listAll: { + global: {} + } + } + }; +}; + +describe('securityContactAdditionalEmail', function() { + describe('run', function() { + it('should give failing result if no security contacts', function(done) { + const cache = createCache([]); + securityContactAdditionalEmail.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('No existing security contacts'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give unknown result if unable to query for security contacts', function(done) { + const cache = createErrorCache(); + securityContactAdditionalEmail.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 security contacts'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give passing result if additional email is configured', function(done) { + const cache = createCache([securityContacts[0]]); + securityContactAdditionalEmail.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Additional email address is configured with security contact email'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give failing result if additional email is not configured', function(done) { + const cache = createCache([securityContacts[1]]); + securityContactAdditionalEmail.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Additional email address is not configured with security contact email'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/securityContactRoleSetToOwner.js b/plugins/azure/securitycenter/securityContactRoleSetToOwner.js new file mode 100644 index 0000000000..c1632e7153 --- /dev/null +++ b/plugins/azure/securitycenter/securityContactRoleSetToOwner.js @@ -0,0 +1,57 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Security Contact Enabled for Subscription Owner', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensure that security alert emails are enabled to subscription owners.', + more_info: 'Enabling security alert emails to subscription owners ensures that they receive security alert emails from Microsoft. This ensures that they are aware of any potential security issues and can mitigate the risk in a timely fashion.', + recommended_action: 'Modify security contact information and enable emails for subscription owners', + link: 'https://learn.microsoft.com/en-us/azure/defender-for-cloud/configure-email-notifications', + apis: ['securityContactv2:listAll'], + realtime_triggers: ['microsoftsecurity:securitycontacts:write','microsoftsecurity:securitycontacts:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.securityContactv2, (location, rcb) => { + + var securityContacts = helpers.addSource(cache, source, + ['securityContactv2', 'listAll', location]); + if (!securityContacts) return rcb(); + + if (securityContacts.err || !securityContacts.data) { + helpers.addResult(results, 3, + 'Unable to query for security contacts: ' + helpers.addError(securityContacts), location); + return rcb(); + } + + if (!securityContacts.data.length) { + helpers.addResult(results, 2, 'No existing security contacts', location); + return rcb(); + } + let ownerExists; + for (let contact of securityContacts.data){ + if (!contact.id) continue; + if (contact.notificationsByRole && contact.notificationsByRole.roles && contact.notificationsByRole.roles.includes('Owner')){ + ownerExists = true; + break; + } + } + + if (ownerExists) { + helpers.addResult(results, 0, 'Security Contact email is configured for subscription owners', location); + } else { + helpers.addResult(results, 2, 'Security Contact email is not configured for subscription owners', location); + } + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/securitycenter/securityContactRoleSetToOwner.spec.js b/plugins/azure/securitycenter/securityContactRoleSetToOwner.spec.js new file mode 100644 index 0000000000..3cd4340e45 --- /dev/null +++ b/plugins/azure/securitycenter/securityContactRoleSetToOwner.spec.js @@ -0,0 +1,101 @@ +var expect = require('chai').expect; +var securityContactRoleSetToOwner = require('./securityContactRoleSetToOwner'); + +const securityContacts = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'alertsToAdmins': 'On', + 'email': 'xyz@gmail.com', + + "notificationsByRole": { + "state": "On", + "roles": [ + "Owner", + ] + }, + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'alertsToAdmins': 'Off', + 'email': '', + + "notificationsByRole": { + "state": "On", + "roles": [ + "Admin" + ] + } + } +]; + +const createCache = (securityContacts) => { + return { + securityContactv2: { + listAll: { + global:{ + data: securityContacts + } + } + } + }; +}; + +const createErrorCache = () => { + return { + securityContactv2: { + listAll: { + global: {} + } + } + }; +}; + +describe('securityContactRoleSetToOwner', function() { + describe('run', function() { + it('should give failing result if no security contacts', function(done) { + const cache = createCache([]); + securityContactRoleSetToOwner.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('No existing security contacts'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give unknown result if unable to query for security contacts', function(done) { + const cache = createErrorCache(); + securityContactRoleSetToOwner.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 security contacts'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give passing result if Security Contact email is configured for subscription owners', function(done) { + const cache = createCache([securityContacts[0]]); + securityContactRoleSetToOwner.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Security Contact email is configured for subscription owners'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give failing result if Security Contact email is not configured for subscription owners', function(done) { + const cache = createCache([securityContacts[1]]); + securityContactRoleSetToOwner.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Security Contact email is not configured for subscription owners'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/securityContactsEnabled.js b/plugins/azure/securitycenter/securityContactsEnabled.js new file mode 100644 index 0000000000..136650a808 --- /dev/null +++ b/plugins/azure/securitycenter/securityContactsEnabled.js @@ -0,0 +1,59 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Security Contacts Enabled', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that security contact phone number and email address are set', + more_info: 'Setting security contacts ensures that any security incidents detected by Azure are sent to a security team equipped to handle the incident.', + recommended_action: 'Ensure that email notifications are configured for the subscription from the Security Center.', + link: 'https://learn.microsoft.com/en-us/azure/security-center/security-center-provide-security-contact-details', + apis: ['securityContacts:list'], + realtime_triggers: ['microsoftsecurity:securitycontacts:write','microsoftsecurity:securitycontacts:delete'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.securityContacts, (location, rcb) => { + + var securityContacts = helpers.addSource(cache, source, + ['securityContacts', 'list', location]); + + if (!securityContacts) return rcb(); + + if (securityContacts.err || !securityContacts.data) { + helpers.addResult(results, 3, + 'Unable to query for security contacts: ' + helpers.addError(securityContacts), location); + return rcb(); + } + + if (!securityContacts.data.length) { + helpers.addResult(results, 2, 'No existing security contacts', location); + return rcb(); + } + + securityContacts.data.forEach(securityContact => { + if (securityContact.phone) { + helpers.addResult(results, 0, 'Security Contact phone number is set on the subscription', location, securityContact.id); + } else { + helpers.addResult(results, 2, 'Security Contact phone number is not set on the subscription', location, securityContact.id); + } + + if (securityContact.email) { + helpers.addResult(results, 0, 'Security Contact email address is set on the subscription', location, securityContact.id); + } else { + helpers.addResult(results, 2, 'Security Contact email address is not set on the subscription', location, securityContact.id); + } + }); + + rcb(); + }, function(){ + // Global checking goes here + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/azure/securitycenter/securityContactsEnabled.spec.js b/plugins/azure/securitycenter/securityContactsEnabled.spec.js new file mode 100644 index 0000000000..25e29fa97e --- /dev/null +++ b/plugins/azure/securitycenter/securityContactsEnabled.spec.js @@ -0,0 +1,141 @@ +var expect = require('chai').expect; +var securityContactsEnabled = require('./securityContactsEnabled'); + +const securityContacts = [ + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'email': 'test@test.com' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'phone': '0123456789' + }, + { + 'id': '/subscriptions/123/providers/Microsoft.Security/securityContacts/contact1', + 'name': 'contact1', + 'email': 'test@test.com', + 'phone': '0123456789' + } +]; + +const createCache = (securityContacts) => { + return { + securityContacts: { + list: { + global:{ + data: securityContacts + } + } + } + }; +}; + +const createErrorCache = () => { + return { + securityContacts: { + list: { + global: {} + } + } + }; +}; + +describe('securityContactsEnabled', function() { + describe('run', function() { + it('should give failing result if no security contacts', function(done) { + const cache = createCache([]); + securityContactsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('No existing security contacts'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give unknown result if unable to query for security contacts', function(done) { + const cache = createErrorCache(); + securityContactsEnabled.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 security contacts'); + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give passing result if security contact has both phone and email set', function(done) { + const cache = createCache([securityContacts[3]]); + securityContactsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(2); + + //Phone verification + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Security Contact phone number is set on the subscription'); + //Email verification + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('Security Contact email address is set on the subscription'); + + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give failing result for missing phone number', function(done) { + const cache = createCache([securityContacts[1]]); + securityContactsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(2); + + //Phone verification + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Security Contact phone number is not set on the subscription'); + //Email verification + expect(results[1].status).to.equal(0); + expect(results[1].message).to.include('Security Contact email address is set on the subscription'); + + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give failing result for missing email', function(done) { + const cache = createCache([securityContacts[2]]); + securityContactsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(2); + + //Phone verification + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Security Contact phone number is set on the subscription'); + //Email verification + expect(results[1].status).to.equal(2); + expect(results[1].message).to.include('Security Contact email address is not set on the subscription'); + + expect(results[0].region).to.equal('global'); + done(); + }); + }); + + it('should give failing result for missing email and phone number', function(done) { + const cache = createCache([securityContacts[0]]); + securityContactsEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(2); + + //Phone verification + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Security Contact phone number is not set on the subscription'); + //Email verification + expect(results[1].status).to.equal(2); + expect(results[1].message).to.include('Security Contact email address is not set on the subscription'); + + expect(results[0].region).to.equal('global'); + done(); + }); + }); + }); +}); \ No newline at end of file diff --git a/plugins/azure/securitycenter/standardPricingEnabled.js b/plugins/azure/securitycenter/standardPricingEnabled.js new file mode 100644 index 0000000000..5df694d463 --- /dev/null +++ b/plugins/azure/securitycenter/standardPricingEnabled.js @@ -0,0 +1,53 @@ +var async = require('async'); +var helpers = require('../../../helpers/azure/'); + +module.exports = { + title: 'Standard Pricing Enabled', + category: 'Security Center', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures that standard pricing is enabled in the security center', + more_info: 'Enabling standard pricing increases the security posture of the subscription. This enables advanced security monitoring for the services covered under the security center.', + recommended_action: 'Ensure that standard pricing is enabled in the security center.', + link: 'https://azure.microsoft.com/en-us/pricing/details/security-center/', + apis: ['pricings:list'], + realtime_triggers: ['microsoftsecurity:pricings:write','microsoftsecurity:pricings:delete'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var locations = helpers.locations(settings.govcloud); + + async.each(locations.pricings, function(location, rcb){ + var pricings = helpers.addSource(cache, source, + ['pricings', 'list', location]); + + if (!pricings) return rcb(); + + if (pricings.err || !pricings.data) { + helpers.addResult(results, 3, + 'Unable to query for security center pricing: ' + helpers.addError(pricings), location); + return rcb(); + } + + if (!pricings.data.length) { + helpers.addResult(results, 0, 'No security center pricings found', location); + return rcb(); + } + + pricings.data.forEach(pricing => { + if (pricing.pricingTier && + pricing.pricingTier === 'Standard') { + helpers.addResult(results, 0, 'Standard pricing is enabled for the service', location, pricing.id); + } else { + helpers.addResult(results, 2, 'Standard pricing is not enabled for the service', location, pricing.id); + } + }); + + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/securitycenter/standardPricingEnabled.spec.js b/plugins/azure/securitycenter/standardPricingEnabled.spec.js new file mode 100644 index 0000000000..6738c028a5 --- /dev/null +++ b/plugins/azure/securitycenter/standardPricingEnabled.spec.js @@ -0,0 +1,87 @@ +var assert = require('assert'); +var expect = require('chai').expect; +var auth = require('./standardPricingEnabled'); + +const createCache = (err, data) => { + return { + pricings: { + list: { + 'global': { + err: err, + data: data + } + } + } + } +}; + +describe('standardPricingEnabled', function() { + describe('run', function() { + it('should give passing result if no pricings found', function(done) { + const callback = (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No security center pricings found'); + expect(results[0].region).to.equal('global'); + done() + }; + + const cache = createCache( + null, + [] + ); + + auth.run(cache, {}, callback); + }); + + it('should give failing result if disable App Service', 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('Standard pricing is not enabled for the service'); + expect(results[0].region).to.equal('global'); + done() + }; + + const cache = createCache( + null, + [ + { + "id": "/subscriptions/e79d9a03-3ab3-4481-bdcd-c5db1d55420a/providers/Microsoft.Security/pricings/default", + "name": "default", + "type": "Microsoft.Security/pricings", + "pricingTier": "free", + "location": "global" + } + ] + ); + + auth.run(cache, {}, callback); + }); + + it('should give passing result if enabled App Service', 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('Standard pricing is enabled for the service'); + expect(results[0].region).to.equal('global'); + done() + }; + + const cache = createCache( + null, + [ + { + "id": "/subscriptions/e79d9a03-3ab3-4481-bdcd-c5db1d55420a/providers/Microsoft.Security/pricings/default", + "name": "default", + "type": "Microsoft.Security/pricings", + "pricingTier": "Standard", + "location": "global" + } + ] + ); + + auth.run(cache, {}, callback); + }) + }) +}) \ No newline at end of file diff --git a/plugins/azure/storageaccounts/blobServiceLoggingEnabled.js b/plugins/azure/storageaccounts/blobServiceLoggingEnabled.js index fc9278de8c..a1f6853f72 100644 --- a/plugins/azure/storageaccounts/blobServiceLoggingEnabled.js +++ b/plugins/azure/storageaccounts/blobServiceLoggingEnabled.js @@ -43,9 +43,6 @@ module.exports = { storageAccount.sku.tier && storageAccount.sku.tier.toLowerCase() == 'premium') { helpers.addResult(results, 0, 'Storage Account tier is premium', location, storageAccount.id); - } else if (storageAccount.kind && - storageAccount.kind.toLowerCase() != 'storagev2') { - helpers.addResult(results, 0, 'Storage Account kind is not StorageV2', location, storageAccount.id); } else { const diagnosticSettings = helpers.addSource(cache, source, diff --git a/plugins/azure/storageaccounts/blobServiceLoggingEnabled.spec.js b/plugins/azure/storageaccounts/blobServiceLoggingEnabled.spec.js index 812ea23c0e..4f4ae422c4 100644 --- a/plugins/azure/storageaccounts/blobServiceLoggingEnabled.spec.js +++ b/plugins/azure/storageaccounts/blobServiceLoggingEnabled.spec.js @@ -18,16 +18,6 @@ const storageAccounts = [ sku: { tier: 'Premium' } - }, - { - kind: 'BlobStorage', - id: '/subscriptions/1234/resourceGroups/cloud-shell-storage-eastus/providers/Microsoft.Storage/storageAccounts/csb100320011e293683', - name: 'csb100320011e293683', - type: 'Microsoft.Storage/storageAccounts', - location: 'eastus', - sku: { - tier: 'Standard' - } } ]; @@ -203,18 +193,6 @@ describe('blobServiceLoggingEnabled', function () { }); }); - it('should PASS if storage account kind in not StorageV2', function (done) { - const cache = createCache([storageAccounts[2]], []); - blobServiceLoggingEnabled.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - expect(results[0].region).to.equal('eastus'); - expect(results[0].message).to.equal('Storage Account kind is not StorageV2'); - - done(); - }); - }); - it('should UNKNOWN if Unable to query for for storage accounts', function (done) { const cache = createErrorCache('diagnostic'); blobServiceLoggingEnabled.run(cache, {}, (err, results) => { diff --git a/plugins/azure/storageaccounts/queueServiceLoggingEnabled.js b/plugins/azure/storageaccounts/queueServiceLoggingEnabled.js index 720a20f9b7..a5eb3e0ca4 100644 --- a/plugins/azure/storageaccounts/queueServiceLoggingEnabled.js +++ b/plugins/azure/storageaccounts/queueServiceLoggingEnabled.js @@ -43,9 +43,6 @@ module.exports = { storageAccount.sku.tier && storageAccount.sku.tier.toLowerCase() == 'premium') { helpers.addResult(results, 0, 'Storage Account tier is premium', location, storageAccount.id); - } else if (storageAccount.kind && - storageAccount.kind.toLowerCase() != 'storagev2') { - helpers.addResult(results, 0, 'Storage Account kind is not StorageV2', location, storageAccount.id); } else { const diagnosticSettings = helpers.addSource(cache, source, @@ -78,4 +75,4 @@ module.exports = { callback(null, results, source); }); } -}; +}; \ No newline at end of file diff --git a/plugins/azure/storageaccounts/queueServiceLoggingEnabled.spec.js b/plugins/azure/storageaccounts/queueServiceLoggingEnabled.spec.js index d83e2cd773..12c0b26261 100644 --- a/plugins/azure/storageaccounts/queueServiceLoggingEnabled.spec.js +++ b/plugins/azure/storageaccounts/queueServiceLoggingEnabled.spec.js @@ -28,16 +28,6 @@ const storageAccounts = [ sku: { tier: 'Premium' } - }, - { - kind: 'BlobStorage', - id: '/subscriptions/1234/resourceGroups/cloud-shell-storage-eastus/providers/Microsoft.Storage/storageAccounts/csb100320011e293683', - name: 'csb100320011e293683', - type: 'Microsoft.Storage/storageAccounts', - location: 'eastus', - sku: { - tier: 'Standard' - } } ]; @@ -215,18 +205,6 @@ describe('queueServiceLoggingEnabled', function () { }); }); - it('should PASS if storage account kind in not StorageV2', function (done) { - const cache = createCache([storageAccounts[2]], []); - queueServiceLoggingEnabled.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - expect(results[0].region).to.equal('eastus'); - expect(results[0].message).to.equal('Storage Account kind is not StorageV2'); - - done(); - }); - }); - it('should UNKNOWN if Unable to query for for storage accounts', function (done) { const cache = createErrorCache('storageAccounts'); queueServiceLoggingEnabled.run(cache, {}, (err, results) => { @@ -249,3 +227,4 @@ describe('queueServiceLoggingEnabled', function () { }); }); }); + diff --git a/plugins/azure/storageaccounts/storageAccountsHttps.js b/plugins/azure/storageaccounts/storageAccountsHttps.js index 2e64c6db93..9edaaff5dc 100644 --- a/plugins/azure/storageaccounts/storageAccountsHttps.js +++ b/plugins/azure/storageaccounts/storageAccountsHttps.js @@ -80,6 +80,7 @@ module.exports = { 'properties': { 'supportsHttpsTrafficOnly': true } + }; // logging diff --git a/plugins/azure/storageaccounts/tableServiceLoggingEnabled.js b/plugins/azure/storageaccounts/tableServiceLoggingEnabled.js index df0e56456d..074000d1a5 100644 --- a/plugins/azure/storageaccounts/tableServiceLoggingEnabled.js +++ b/plugins/azure/storageaccounts/tableServiceLoggingEnabled.js @@ -42,9 +42,6 @@ module.exports = { storageAccount.sku.tier && storageAccount.sku.tier.toLowerCase() == 'premium') { helpers.addResult(results, 0, 'Storage Account tier is premium', location, storageAccount.id); - } else if (storageAccount.kind && - storageAccount.kind.toLowerCase() != 'storagev2') { - helpers.addResult(results, 0, 'Storage Account kind is not StorageV2', location, storageAccount.id); } else { const diagnosticSettings = helpers.addSource(cache, source, diff --git a/plugins/azure/storageaccounts/tableServiceLoggingEnabled.spec.js b/plugins/azure/storageaccounts/tableServiceLoggingEnabled.spec.js index a73c8c9127..c6da89bd60 100644 --- a/plugins/azure/storageaccounts/tableServiceLoggingEnabled.spec.js +++ b/plugins/azure/storageaccounts/tableServiceLoggingEnabled.spec.js @@ -28,16 +28,6 @@ const storageAccounts = [ sku: { tier: 'Premium' } - }, - { - kind: 'BlobStorage', - id: '/subscriptions/1234/resourceGroups/cloud-shell-storage-eastus/providers/Microsoft.Storage/storageAccounts/csb100320011e293683', - name: 'csb100320011e293683', - type: 'Microsoft.Storage/storageAccounts', - location: 'eastus', - sku: { - tier: 'Standard' - } } ]; @@ -215,18 +205,6 @@ describe('tableServiceLoggingEnabled', function () { }); }); - it('should PASS if storage account kind in not StorageV2', function (done) { - const cache = createCache([storageAccounts[2]], []); - tableServiceLoggingEnabled.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - expect(results[0].region).to.equal('eastus'); - expect(results[0].message).to.equal('Storage Account kind is not StorageV2'); - - done(); - }); - }); - it('should UNKNOWN if Unable to query for for storage accounts', function (done) { const cache = createErrorCache('storageAccounts'); tableServiceLoggingEnabled.run(cache, {}, (err, results) => { @@ -249,3 +227,4 @@ describe('tableServiceLoggingEnabled', function () { }); }); }); +