From 7bc98ad5b934c4714229f9af18ece4c36c6db4e6 Mon Sep 17 00:00:00 2001 From: Gio Rodriguez Date: Fri, 14 May 2021 15:49:43 -0700 Subject: [PATCH 01/11] saas sync 5-14 --- .github/CODE_OF_CONDUCT.md | 2 +- collectors/aws/collector.js | 16 +- helpers/asl/asl-1.js | 354 +++++++++++------- helpers/google/functions.js | 107 +++++- helpers/google/index.js | 48 +-- package-lock.json | 101 +++-- plugins/aws/iam/iamRolePolicies.js | 12 +- .../cosmosdb/advancedThreatProtection.js | 5 +- plugins/google/clb/clbCDNEnabled.js | 2 +- plugins/google/clb/clbHttpsOnly.js | 2 +- plugins/google/clb/clbNoInstances.js | 2 +- .../google/clb/clbSecurityPolicyEnabled.js | 2 +- plugins/google/compute/autoscaleEnabled.js | 8 +- .../compute/connectSerialPortsDisabled.js | 3 +- .../google/compute/csekEncryptionEnabled.js | 3 +- .../google/compute/instanceLeastPrivilege.js | 4 +- .../google/compute/instanceLevelSSHOnly.js | 3 +- plugins/google/compute/instanceMaxCount.js | 3 +- plugins/google/compute/instancesMultiAz.js | 2 +- .../google/compute/ipForwardingDisabled.js | 3 +- plugins/google/compute/osLoginEnabled.js | 2 +- .../google/cryptographickeys/keyRotation.js | 19 +- plugins/google/dns/dnsSecEnabled.js | 2 +- plugins/google/dns/dnsSecSigningAlgorithm.js | 2 +- plugins/google/iam/corporateEmailsOnly.js | 2 +- plugins/google/iam/kmsUserSeparation.js | 2 +- plugins/google/iam/serviceAccountAdmin.js | 2 +- .../google/iam/serviceAccountKeyRotation.js | 2 +- .../google/iam/serviceAccountManagedKeys.js | 2 +- .../google/iam/serviceAccountSeparation.js | 2 +- plugins/google/iam/serviceAccountUser.js | 2 +- plugins/google/iam/serviceLimits.js | 2 +- plugins/google/iam/serviceLimits.spec.js | 2 +- .../google/kubernetes/aliasIpRangesEnabled.js | 4 +- .../kubernetes/aliasIpRangesEnabled.spec.js | 2 +- .../kubernetes/autoNodeRepairEnabled.js | 2 +- .../kubernetes/autoNodeUpgradesEnabled.js | 2 +- .../kubernetes/basicAuthenticationDisabled.js | 2 +- .../google/kubernetes/clusterLabelsAdded.js | 2 +- .../kubernetes/clusterLeastPrivilege.js | 4 +- .../kubernetes/clusterLeastPrivilege.spec.js | 2 +- plugins/google/kubernetes/cosImageEnabled.js | 2 +- .../kubernetes/defaultServiceAccount.js | 2 +- .../kubernetes/kubernetesAlphaDisabled.js | 2 +- .../kubernetes/legacyAuthorizationDisabled.js | 2 +- plugins/google/kubernetes/loggingEnabled.js | 2 +- .../kubernetes/masterAuthorizedNetwork.js | 2 +- .../google/kubernetes/monitoringEnabled.js | 2 +- .../google/kubernetes/networkPolicyEnabled.js | 4 +- .../kubernetes/networkPolicyEnabled.spec.js | 2 +- .../kubernetes/podSecurityPolicyEnabled.js | 4 +- .../podSecurityPolicyEnabled.spec.js | 4 +- .../kubernetes/privateClusterEnabled.js | 4 +- .../kubernetes/privateClusterEnabled.spec.js | 4 +- plugins/google/kubernetes/privateEndpoint.js | 2 +- .../google/kubernetes/webDashboardDisabled.js | 2 +- .../logging/auditConfigurationLogging.js | 2 +- plugins/google/logging/auditLoggingEnabled.js | 2 +- plugins/google/logging/customRoleLogging.js | 4 +- plugins/google/logging/logSinksEnabled.js | 4 +- .../google/logging/projectOwnershipLogging.js | 4 +- .../google/logging/sqlConfigurationLogging.js | 4 +- .../logging/storagePermissionsLogging.js | 4 +- .../google/logging/vpcFirewallRuleLogging.js | 4 +- plugins/google/logging/vpcNetworkLogging.js | 4 +- .../google/logging/vpcNetworkRouteLogging.js | 4 +- plugins/google/sql/anyHostRootAccess.js | 2 +- plugins/google/sql/dbAutomatedBackups.js | 2 +- plugins/google/sql/dbMultiAz.js | 2 +- plugins/google/sql/dbPubliclyAccessible.js | 2 +- plugins/google/sql/dbRestorable.js | 2 +- plugins/google/sql/dbSSLEnabled.js | 2 +- .../google/storage/bucketAllUsersPolicy.js | 17 +- .../storage/bucketAllUsersPolicy.spec.js | 78 +++- plugins/google/storage/bucketLogging.js | 5 +- .../google/storage/bucketRetentionPolicy.js | 4 +- plugins/google/storage/bucketVersioning.js | 5 +- plugins/google/vpcnetwork/defaultVpcInUse.js | 2 +- .../vpcnetwork/excessiveFirewallRules.js | 2 +- plugins/google/vpcnetwork/flowLogsEnabled.js | 2 +- plugins/google/vpcnetwork/multipleSubnets.js | 2 +- plugins/google/vpcnetwork/openAllPorts.js | 2 +- plugins/google/vpcnetwork/openCIFS.js | 2 +- plugins/google/vpcnetwork/openCassandra.js | 2 +- plugins/google/vpcnetwork/openDNS.js | 2 +- plugins/google/vpcnetwork/openDocker.js | 2 +- plugins/google/vpcnetwork/openFTP.js | 2 +- .../google/vpcnetwork/openHadoopNameNode.js | 2 +- .../vpcnetwork/openHadoopNameNodeWebUI.js | 2 +- plugins/google/vpcnetwork/openKibana.js | 2 +- plugins/google/vpcnetwork/openMongo.js | 2 +- plugins/google/vpcnetwork/openMsSQL.js | 2 +- plugins/google/vpcnetwork/openMySQL.js | 2 +- plugins/google/vpcnetwork/openNetBIOS.js | 2 +- plugins/google/vpcnetwork/openOracle.js | 2 +- .../vpcnetwork/openOracleAutoDataWarehouse.js | 2 +- plugins/google/vpcnetwork/openPostgreSQL.js | 2 +- plugins/google/vpcnetwork/openRDP.js | 2 +- plugins/google/vpcnetwork/openRPC.js | 2 +- plugins/google/vpcnetwork/openRedis.js | 2 +- plugins/google/vpcnetwork/openSMBoTCP.js | 2 +- plugins/google/vpcnetwork/openSMTP.js | 2 +- plugins/google/vpcnetwork/openSQLServer.js | 2 +- plugins/google/vpcnetwork/openSSH.js | 2 +- plugins/google/vpcnetwork/openSalt.js | 2 +- plugins/google/vpcnetwork/openTelnet.js | 2 +- plugins/google/vpcnetwork/openVNCClient.js | 2 +- plugins/google/vpcnetwork/openVNCServer.js | 2 +- .../google/vpcnetwork/privateAccessEnabled.js | 2 +- 109 files changed, 625 insertions(+), 381 deletions(-) diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index fa089418fa..652c18b74c 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at support@cloudsploit.com. All +reported by contacting the project team at support@aquasec.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. diff --git a/collectors/aws/collector.js b/collectors/aws/collector.js index a05a56747f..f729cb1f9d 100644 --- a/collectors/aws/collector.js +++ b/collectors/aws/collector.js @@ -1397,14 +1397,17 @@ var collect = function(AWSConfig, settings, callback) { var AWSXRay; var debugMode = settings.debug_mode; if (debugMode) AWSXRay = require('aws-xray-sdk'); - + AWSConfig.maxRetries = 8; AWSConfig.retryDelayOptions = {base: 100}; var regions = helpers.regions(settings); var collection = {}; - + var debugApiCalls = function(call, service, finished) { + if (!debugMode) return; + finished ? console.log(`[INFO] ${service}:${call} returned`) : console.log(`[INFO] ${service}:${call} invoked`); + }; async.eachOfLimit(calls, 10, function(call, service, serviceCb) { var serviceLower = service.toLowerCase(); if (!collection[serviceLower]) collection[serviceLower] = {}; @@ -1413,7 +1416,7 @@ var collect = function(AWSConfig, settings, callback) { async.eachOfLimit(call, 15, function(callObj, callKey, callCb) { if (settings.api_calls && settings.api_calls.indexOf(service + ':' + callKey) === -1) return callCb(); if (!collection[serviceLower][callKey]) collection[serviceLower][callKey] = {}; - + debugApiCalls(callKey, service); var callRegions; if (callObj.default) { @@ -1481,17 +1484,16 @@ var collect = function(AWSConfig, settings, callback) { // so that the injection of the NextToken doesn't break other calls var localParams = JSON.parse(JSON.stringify(callObj.params || {})); if (nextTokens) localParams[nextTokens[0]] = nextTokens[1]; - if (callObj.params || nextTokens) { executor[callKey](localParams, executorCb); } else { executor[callKey](executorCb); } } - execute(); } }, function() { + debugApiCalls(callKey, service, true); callCb(); }); }, function() { @@ -1507,7 +1509,7 @@ var collect = function(AWSConfig, settings, callback) { async.eachOfLimit(serviceObj, 1, function(callObj, callKey, callCb) { if (settings.api_calls && settings.api_calls.indexOf(service + ':' + callKey) === -1) return callCb(); if (!collection[serviceLower][callKey]) collection[serviceLower][callKey] = {}; - + debugApiCalls(callKey, service); async.eachLimit(regions[serviceLower], helpers.MAX_REGIONS_AT_A_TIME, function(region, regionCb) { if (settings.skip_regions && settings.skip_regions.indexOf(region) > -1 && @@ -1560,7 +1562,6 @@ var collect = function(AWSConfig, settings, callback) { var filter = {}; filter[callObj.filterKey] = dep[callObj.filterValue]; filter[callObj.checkMultipleKey] = thisCheck; - executor[callKey](filter, function(err, data) { if (err) { collection[serviceLower][callKey][LocalAWSConfig.region][dep[callObj.filterValue]].err = err; @@ -1606,6 +1607,7 @@ var collect = function(AWSConfig, settings, callback) { }); } }, function() { + debugApiCalls(callKey, service, true); callCb(); }); }, function() { diff --git a/helpers/asl/asl-1.js b/helpers/asl/asl-1.js index 423e1976e5..7b3442d1a6 100644 --- a/helpers/asl/asl-1.js +++ b/helpers/asl/asl-1.js @@ -4,7 +4,16 @@ var parse = function(obj, path) { var localPath = path.shift(); if (localPath.includes('[*]')){ localPath = localPath.split('[')[0]; - if (obj[localPath] && obj[localPath].length && obj[localPath].length === 1) return [obj[localPath][0], path]; + if (obj[localPath] && obj[localPath].length && obj[localPath].length === 1) { + if (!path || !path.length) { + return [obj[localPath][0], path]; + } else if (path.length === 1){ + return [obj[localPath][0][path[0]]]; + } + } + if (path.length && path.join('.').includes('[*]')) { + return parse(obj[localPath], path); + } return [obj[localPath], path]; } if (obj[localPath] || typeof obj[localPath] === 'boolean') { @@ -12,6 +21,8 @@ var parse = function(obj, path) { } else { return ['not set']; } + } else if (Array.isArray(obj)) { + return obj; } else { return [obj]; } @@ -97,27 +108,136 @@ var compositeResult = function(inputResultsArr, resource, region, results, logic } }; -function evaluateCondition(obj, condition, inputResultsArr){ - let value = validate(obj,condition, inputResultsArr); - return value; -} +var validate = function(condition, conditionResult, inputResultsArr, message, property, parsed) { + if (parsed && typeof parsed === 'object' && parsed[property]) { + condition.parsed = parsed[property]; + } + + if (condition.transform) { + try { + condition.parsed = transform(condition.parsed, condition.transform); + } catch (e) { + conditionResult = 2; + message.push(`${property}: unable to perform transformation`); + let resultObj = { + status: conditionResult, + message: message.join(', ') + }; + + inputResultsArr.push(resultObj); + return resultObj; + } + } + + // Compare the property with the operator + if (condition.op) { + if (condition.transform && condition.transform == 'EACH' && condition) { + // Recurse into the same function + var subProcessed = []; + if (!condition.parsed.length) { + conditionResult = 2; + message.push(`${property}: is not iterable using EACH transformation`); + } else { + condition.parsed.forEach(function(parsed) { + subProcessed.push(runValidation(parsed, condition, inputResultsArr)); + }); + subProcessed.forEach(function(sub) { + if (sub.status) conditionResult = sub.status; + if (sub.message) message.push(sub.message); + }); + } + } else if (condition.op == 'EQ') { + if (condition.parsed == condition.value) { + message.push(`${property}: ${condition.parsed} matched: ${condition.value}`); + return 0; + } else { + message.push(`${property}: ${condition.parsed} did not match: ${condition.value}`); + return 2; + } + } else if (condition.op == 'GT') { + if (condition.parsed > condition.value) { + message.push(`${property}: count of ${condition.parsed} was greater than: ${condition.value}`); + } else { + conditionResult = 2; + message.push(`${property}: count of ${condition.parsed} was not greater than: ${condition.value}`); + } + } else if (condition.op == 'NE') { + if (condition.parsed !== condition.value) { + message.push(`${property}: ${condition.parsed} is not: ${condition.value}`); + } else { + conditionResult = 2; + message.push(`${property}: ${condition.parsed} is: ${condition.value}`); + } + } else if (condition.op == 'MATCHES') { + var userRegex = RegExp(condition.value); + if (userRegex.test(condition.parsed)) { + message.push(`${property}: ${condition.parsed} matches the regex: ${condition.value}`); + } else { + conditionResult = 2; + message.push(`${property}: ${condition.parsed} does not match the regex: ${condition.value}`); + } + } else if (condition.op == 'EXISTS') { + if (condition.parsed !== 'not set') { + message.push(`${property}: set to ${condition.parsed}`); + return 0; + } else { + message.push(`${property}: ${condition.parsed}`); + return 2; + } + } else if (condition.op == 'ISTRUE') { + if (typeof condition.parsed == 'boolean' && condition.parsed) { + message.push(`${property} is true`); + return 0; + } else if (typeof condition.parsed == 'boolean' && !condition.parsed) { + conditionResult = 2; + message.push(`${property} is false`); + return 2; + } else { + message.push(`${property} is not a boolean value`); + return 2; + } + } else if (condition.op == 'ISFALSE') { + if (typeof condition.parsed == 'boolean' && !condition.parsed) { + message.push(`${property} is false`); + return 0; + } else if (typeof condition.parsed == 'boolean' && condition.parsed) { + conditionResult = 2; + message.push(`${property} is true`); + return 2; + } else { + message.push(`${property} is not a boolean value`); + return 2; + } + } else if (condition.op == 'CONTAINS') { + if (condition.parsed && condition.parsed.length && condition.parsed.includes(condition.value)) { + message.push(`${property}: ${condition.value} found in ${condition.parsed}`); + return 0; + } else if (condition.parsed && condition.parsed.length){ + message.push(`${condition.value} not found in ${condition.parsed}`); + return 2; + } else { + message.push(`${condition.parsed} is not the right property type for this operation`); + return 2; + } + } + return conditionResult; + } +}; -var validate = function(obj, condition, inputResultsArr) { +var runValidation = function(obj, condition, inputResultsArr, nestedResultArr) { let result = 0; let message = []; - let override = false; // Extract the values for the conditions if (condition.property) { + let conditionResult = 0; let property; - if (condition.property.length === 1) property = condition.property[0]; else if (condition.property.length > 1) property = condition.property; - condition.parsed = parse(obj, condition.property)[0]; - if (!condition.parsed || condition.parsed === 'not set'){ + if ((typeof condition.parsed !== 'boolean' && !condition.parsed)|| condition.parsed === 'not set'){ conditionResult = 2; message.push(`${property}: not set to any value`); @@ -130,118 +250,65 @@ var validate = function(obj, condition, inputResultsArr) { return resultObj; } - // Transform the property if required - if (condition.transform) { - try { - condition.parsed = transform(condition.parsed, condition.transform); - } catch (e) { - conditionResult = 2; - message.push(`${property}: unable to perform transformation`); + if (property.includes('[*]')) { + if (Array.isArray(condition.parsed)) { + if (!Array.isArray(nestedResultArr)) nestedResultArr = []; + let propertyArr = property.split('.'); + propertyArr.shift(); + property = propertyArr.join('.'); + condition.property = property; + condition.parsed.forEach(parsed => { + if (property.includes('[*]')) { + runValidation(parsed, condition, inputResultsArr, nestedResultArr); + } else { + let localConditionResult = validate(condition, conditionResult, inputResultsArr, message, property, parsed); + nestedResultArr.push(localConditionResult); + } + // [0,2,0,2,0,0,2,2] + }); + // NestedCompositeResult + if (nestedResultArr && nestedResultArr.length) { + if (!condition.nested) condition.nested = 'ONE'; + let resultObj; + if ((condition.nested.toUpperCase() === 'ONE' && nestedResultArr.indexOf(0) > -1) || (condition.nested.toUpperCase() === 'ALL' && nestedResultArr.indexOf(2) === 0)) { + resultObj = { + status: 0, + message: message.join(', ') + }; + } else { + resultObj = { + status: 2, + message: message.join(', ') + }; + } + + inputResultsArr.push(resultObj); + return resultObj; + } + } else { + if (!Array.isArray(nestedResultArr)) nestedResultArr = []; + let propertyArr = property.split('.'); + propertyArr.shift(); + property = propertyArr.join('.'); + let localConditionResult = validate(condition, conditionResult, inputResultsArr, message, condition.property, condition.parsed); + let resultObj = { - status: conditionResult, + status: localConditionResult, message: message.join(', ') }; + inputResultsArr.push(resultObj); return resultObj; - } - } - // Compare the property with the operator - if (condition.op) { - if (condition.transform && condition.transform == 'EACH' && condition) { - // Recurse into the same function - var subProcessed = []; - if (!condition.parsed.length) { - conditionResult = 2; - message.push(`${property}: is not iterable using EACH transformation`); - } else { - condition.parsed.forEach(function(parsed) { - subProcessed.push(validate(parsed, condition, inputResultsArr)); - }); - subProcessed.forEach(function(sub) { - if (sub.status) conditionResult = sub.status; - if (sub.message) message.push(sub.message); - }); - } - } else if (condition.op == 'EQ') { - if (condition.parsed == condition.value) { - message.push(`${property}: ${condition.parsed} matched: ${condition.value}`); - } else { - conditionResult = 2; - message.push(`${property}: ${condition.parsed} did not match: ${condition.value}`); - } - } else if (condition.op == 'GT') { - if (condition.parsed > condition.value) { - message.push(`${property}: count of ${condition.parsed} was greater than: ${condition.value}`); - } else { - conditionResult = 2; - message.push(`${property}: count of ${condition.parsed} was not greater than: ${condition.value}`); - } - } else if (condition.op == 'NE') { - if (condition.parsed !== condition.value) { - message.push(`${property}: ${condition.parsed} is not: ${condition.value}`); - } else { - conditionResult = 2; - message.push(`${property}: ${condition.parsed} is: ${condition.value}`); - } - } else if (condition.op == 'MATCHES') { - var userRegex = RegExp(condition.value); - if (userRegex.test(condition.parsed)) { - message.push(`${property}: ${condition.parsed} matches the regex: ${condition.value}`); - } else { - conditionResult = 2; - message.push(`${property}: ${condition.parsed} does not match the regex: ${condition.value}`); - } - } else if (condition.op == 'EXISTS') { - if (condition.parsed !== 'not set') { - message.push(`${property}: set to ${condition.parsed}`); - } else { - conditionResult = 2; - message.push(`${property}: ${condition.parsed}`); - } - } else if (condition.op == 'ISTRUE') { - if (typeof condition.parsed == 'boolean' && condition.parsed) { - message.push(`${property} is true`); - } else if (typeof condition.parsed == 'boolean' && !condition.parsed) { - conditionResult = 2; - message.push(`${property} is false`); - } else { - conditionResult = 2; - message.push(`${property} is not a boolean value`); - } - } else if (condition.op == 'ISFALSE') { - if (typeof condition.parsed == 'boolean' && !condition.parsed) { - message.push(`${property} is false`); - } else if (typeof condition.parsed == 'boolean' && condition.parsed) { - conditionResult = 2; - message.push(`${property} is true`); - } else { - conditionResult = 2; - message.push(`${property} is not a boolean value`); - } - } else if (condition.op == 'CONTAINS') { - if (condition.parsed && condition.parsed.length && condition.parsed.includes(condition.value)) { - message.push(`${property}: ${condition.value} found in ${condition.parsed}`); - } else if (condition.parsed && condition.parsed.length){ - conditionResult = 2; - message.push(`${condition.value} not found in ${condition.parsed}`); - } else { - conditionResult = 2; - message.push(`${condition.parsed} is not the right property type for this operation`); - } } + } else { + // Transform the property if required + conditionResult = validate(condition, conditionResult, inputResultsArr, message, property); + if (conditionResult) result = conditionResult; } - - if (condition.invert) conditionResult = (conditionResult ? 0 : 2); - - if (condition.override && !conditionResult) override = true; - if (conditionResult) result = conditionResult; } - - if (result && override) result = 0; - if (!message.length) { message = ['The resource matched all required conditions']; } @@ -265,21 +332,39 @@ var runConditions = function(input, data, results, resourcePath, resourceName, r let inputResultsArr = []; let logical; let localInput = JSON.parse(JSON.stringify(input)); + + // to check if top level * matches. ex: Instances[*] should be + // present in each condition if not its impossible to compare resources + let resourceConditionArr = []; localInput.conditions.forEach(condition => { logical = condition.logical; - if (condition.property && condition.property.indexOf('[*]') > -1) { - dataToValidate = parse(data, condition.property); - newPath = dataToValidate[1]; - newData = dataToValidate[0]; - condition.property = newPath; + var conditionPropArr = condition.property.split('.'); + if (condition.property && condition.property.includes('[*]')) { + if (conditionPropArr.length > 1 && conditionPropArr[1].includes('[*]')) { + resourceConditionArr.push(conditionPropArr[0]); + var firstProperty = conditionPropArr.shift(); + dataToValidate = parse(data, firstProperty.split('[*]')[0]); + condition.property = conditionPropArr.join('.'); + dataToValidate.forEach(newData => { + condition.validated = runValidation(newData, condition, inputResultsArr); + parsedResource = parse(newData, resourcePath)[0]; + if (typeof parsedResource !== 'string') parsedResource = resourceName; + }); + // result per resource + } else { + dataToValidate = parse(data, condition.property); + newPath = dataToValidate[1]; + newData = dataToValidate[0]; + condition.property = newPath; - condition.validated = evaluateCondition(newData, condition, inputResultsArr); - parsedResource = parse(data, resourcePath)[0]; - if (typeof parsedResource !== 'string') parsedResource = null; + condition.validated = runValidation(newData, condition, inputResultsArr); + parsedResource = parse(newData, resourcePath)[0]; + if (typeof parsedResource !== 'string') parsedResource = null; + } } else { dataToValidate = parse(data, condition.property); if (dataToValidate.length === 1) { - validated = evaluateCondition(data, condition, inputResultsArr); + validated = runValidation(data, condition, inputResultsArr); parsedResource = parse(data, resourcePath)[0]; if (typeof parsedResource !== 'string') parsedResource = null; } else { @@ -287,7 +372,7 @@ var runConditions = function(input, data, results, resourcePath, resourceName, r newData = dataToValidate[0]; condition.property = newPath; newData.forEach(element =>{ - condition.validated = evaluateCondition(element, condition, inputResultsArr); + condition.validated = runValidation(element, condition, inputResultsArr); parsedResource = parse(data, resourcePath)[0]; if (typeof parsedResource !== 'string') parsedResource = null; @@ -333,21 +418,13 @@ var asl = function(source, input, resourceMap, callback) { region: region }); } else if (regionVal.data && regionVal.data.length) { - if (!regionVal.data.length) { - results.push({ - status: 0, - message: 'No resources found in this region', - region: region - }); - } else { - regionVal.data.forEach(function(regionData) { - runConditions(input, regionData, results, resourcePath, '', region); - }); - } - } else if (regionVal.data && Object.keys(regionVal.data).length > 0) { + regionVal.data.forEach(function(regionData) { + runConditions(input, regionData, results, resourcePath, '', region); + }); + } else if (regionVal.data && Object.keys(regionVal.data).length) { runConditions(input, regionVal.data, results, resourcePath, '', region); } else { - if (!Object.keys(regionVal).length) { + if (!Object.keys(regionVal).length || (regionVal.data && (!regionVal.data.length || !Object.keys(regionVal.data).length))) { results.push({ status: 0, message: 'No resources found in this region', @@ -356,20 +433,13 @@ var asl = function(source, input, resourceMap, callback) { } else { for (let resourceName in regionVal) { let resourceObj = regionVal[resourceName]; - if (resourceObj.err) { + if (resourceObj.err || !resourceObj.data) { results.push({ status: 3, resource: resourceName, message: resourceObj.err.message || 'Error', region: region }); - } else if (!resourceObj.data) { - results.push({ - status: 3, - resource: resourceName, - message: 'No data returned', - region: region - }); } else { runConditions(input, resourceObj.data, results, resourcePath, resourceName, region); } diff --git a/helpers/google/functions.js b/helpers/google/functions.js index f063f4fe09..675ac923fa 100644 --- a/helpers/google/functions.js +++ b/helpers/google/functions.js @@ -1,15 +1,70 @@ var shared = require(__dirname + '/../shared.js'); +var disabledKeywords = ['has not been used', 'it is disabled']; -function addResult(results, status, message, region, resource, custom){ - // Override unknown results for regions that are opt-in - results.push({ - status: status, - message: message, - region: region || 'global', - resource: resource || null, - custom: custom || false - }); +function addResult(results, status, message, region, resource, custom, err, required) { + var pushResult = function(status, message, region, resource, custom) { + results.push({ + status: status, + message: message, + region: region || 'global', + resource: resource || null, + custom: custom || false + }); + }; + + var processError = function(errorObj) { + if (errorObj && + errorObj.code && + errorObj.code == 404) { + pushResult(0, 'Project is deleted or pending deletion.', region, resource, custom); + } else if (errorObj && + errorObj.code && + errorObj.code == 403 && + errorObj.message && + disabledKeywords.some(substring=>errorObj.message.includes(substring))) { + pushResult(required ? 2 : 0, required ? 'Service is not enabled, but it is recommended to run a secure workload in GCP.' : 'Service is not enabled', region, resource, custom); + } else if (errorObj && + errorObj.code && + errorObj.code == 403 && + errorObj.errors && + errorObj.errors.length) { + errorObj.errors.forEach(function(errError){ + if (errError && + errError.message && + disabledKeywords.some(substring=>errError.message.includes(substring))){ + pushResult(required ? 2 : 0, required ? 'Service is not enabled, but it is recommended to run a secure workload in GCP.' : 'Service is not enabled', region, resource, custom); + } else { + pushResult(3, (errError.message ? errError.message : message), region, resource, custom); + } + }); + + } else { + pushResult(3, (errorObj.message ? errorObj.message : 'Unable to query the API: ' + errorObj.code), region, resource, custom); + } + }; + + if (err && + err.code) { + processError(err); + } else if (err && + err[region] && + err[region].length) { + err[region].forEach(function(errRegion) { + if (errRegion && + err[region][errRegion] && + err[region][errRegion].code) { + processError(err[region][errRegion]); + } else { + pushResult(3, (err[region][errRegion].message ? err[region][errRegion].message : message), region, resource, custom); + } + }); + } else if (message && + disabledKeywords.some(substring=>message.includes(substring))) { + pushResult(required ? 2 : 0, required ? 'Service is not enabled, but it is recommended to run a secure workload in GCP.' : 'Service is not enabled', region, resource, custom); + } else { + pushResult(status, message, region, resource, custom); + } } function findOpenPorts(ngs, protocols, service, location, results) { @@ -22,14 +77,14 @@ function findOpenPorts(ngs, protocols, service, location, results) { let sourceAddressPrefix = sgroups.sourceRanges; if (!sourceAddressPrefix || !sourceAddressPrefix.length) continue; - + for (let firewallRule of firewallRules) { for (let protocol in protocols) { let ports = protocols[protocol]; - + for (let port of ports) { - if (sgroups['direction'] && (sgroups['direction'] === 'INGRESS') && - firewallRule['IPProtocol'] && (firewallRule['IPProtocol'] === protocol) && + if (sgroups['direction'] && (sgroups['direction'] === 'INGRESS') && + firewallRule['IPProtocol'] && (firewallRule['IPProtocol'] === protocol) && !sgroups['disabled'] && (sourceAddressPrefix.includes('*') || sourceAddressPrefix.includes('') || sourceAddressPrefix.includes('0.0.0.0/0') || sourceAddressPrefix.includes('/0') || sourceAddressPrefix.includes('/0') || sourceAddressPrefix.includes('internet'))) { var sourcefilter = (sourceAddressPrefix === '0.0.0.0/0' ? 'any IP' : sourceAddressPrefix); @@ -41,13 +96,13 @@ function findOpenPorts(ngs, protocols, service, location, results) { let endPort = portRange[1]; if (parseInt(startPort) < port && parseInt(endPort) > port) { var string = `` + (protocol === '*' ? `All protocols` : protocol.toUpperCase()) + - ` port ` + port + ` open to ` + sourcefilter; strings.push(string); + ` port ` + port + ` open to ` + sourcefilter; strings.push(string); if (strings.indexOf(string) === -1) strings.push(string); found = true; } } else if (parseInt(portRange) === port) { var string = `` + (protocol === '*' ? `All protocols` : protocol.toUpperCase()) + - ` port ` + port + ` open to ` + sourcefilter; + ` port ` + port + ` open to ` + sourcefilter; if (strings.indexOf(string) === -1) strings.push(string); found = true; } @@ -70,6 +125,7 @@ function findOpenPorts(ngs, protocols, service, location, results) { shared.addResult(results, 0, 'No public open ports found', location); } } + function findOpenAllPorts(ngs, location, results) { let found = false; let protocols = {'tcp': '*', 'udp' : '*'}; @@ -85,10 +141,10 @@ function findOpenAllPorts(ngs, location, results) { for (let firewallRule of firewallRules) { for (let protocol in protocols) { if (sgroups['direction'] && (sgroups['direction'] === 'INGRESS') && - firewallRule['IPProtocol'] && (firewallRule['IPProtocol'] === protocol) && - !sgroups['disabled'] && - sourceAddressPrefix && - (sourceAddressPrefix.includes('*') || sourceAddressPrefix.includes('') || sourceAddressPrefix.includes('0.0.0.0/0') || sourceAddressPrefix.includes('/0') || sourceAddressPrefix.includes('/0') || sourceAddressPrefix.includes('internet'))) { + firewallRule['IPProtocol'] && (firewallRule['IPProtocol'] === protocol) && + !sgroups['disabled'] && + sourceAddressPrefix && + (sourceAddressPrefix.includes('*') || sourceAddressPrefix.includes('') || sourceAddressPrefix.includes('0.0.0.0/0') || sourceAddressPrefix.includes('/0') || sourceAddressPrefix.includes('/0') || sourceAddressPrefix.includes('internet'))) { if (firewallRule['ports']) { firewallRule['ports'].forEach((portRange) => { if (portRange.includes("-")) { @@ -132,8 +188,19 @@ function findOpenAllPorts(ngs, location, results) { shared.addResult(results, 0, 'No public open ports found', location); } } + +function hasBuckets(buckets){ + if(buckets.length && + Object.keys(buckets[0]).length>1) { + return true; + } else { + return false; + } +} + module.exports = { addResult: addResult, findOpenPorts: findOpenPorts, - findOpenAllPorts: findOpenAllPorts + findOpenAllPorts: findOpenAllPorts, + hasBuckets: hasBuckets }; diff --git a/helpers/google/index.js b/helpers/google/index.js index ccc678f6b8..b229303fc9 100644 --- a/helpers/google/index.js +++ b/helpers/google/index.js @@ -7,33 +7,6 @@ const {JWT} = require('google-auth-library'); var async = require('async'); -var getProjects = async function(client, filter, callback) { - const cloudresourcemanager = google.cloudresourcemanager('v1'); - - const request = { - auth: client, - filter: `lifecycleState: ${filter}` - }; - - let response; - let projects = []; - - do { - if (response && response.nextPageToken) { - request.pageToken = response.nextPageToken; - } - response = (await cloudresourcemanager.projects.list(request)).data; - const projectsPage = response.projects; - if (projectsPage) { - for (let i = 0; i < projectsPage.length; i++) { - projects.push(projectsPage[i]); - } - } - } while (response.nextPageToken); - - return callback(projects); -}; - var regions = function() { return regRegions; }; @@ -47,23 +20,6 @@ var authenticate = async function(GoogleConfig) { return client; }; -var projectsList = function(client, cb) { - getProjects(client, 'ACTIVE', function(projects) { - cb(projects); - }); -}; - -var deletedProjectsList = function(client, cb) { - let deletedProjects = []; - - getProjects(client, 'DELETE_REQUESTED', function(delReqProjects) { - getProjects(client, 'DELETE_IN_PROGRESS', function(delInProgress) { - deletedProjects = delReqProjects.concat(delInProgress); - cb(deletedProjects); - }); - }); -}; - var processCall = function(GoogleConfig, collection, settings, regions, call, service, client, serviceCb) { // Loop through each of the service's functions if (call.manyApi) { @@ -377,9 +333,7 @@ var helpers = { regions: regions, MAX_REGIONS_AT_A_TIME: 6, authenticate: authenticate, - processCall: processCall, - projectsList: projectsList, - deletedProjectsList: deletedProjectsList + processCall: processCall }; for (var s in shared) helpers[s] = shared[s]; diff --git a/package-lock.json b/package-lock.json index 01077ecf93..c5df26fabc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -387,9 +387,9 @@ } }, "agent-base": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", - "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "requires": { "debug": "4" } @@ -593,9 +593,9 @@ "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" }, "aws-sdk": { - "version": "2.876.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.876.0.tgz", - "integrity": "sha512-M+8M2U0X12pVCYyOJjabxCzFJaql2F0Wpfjwyq5aSso0y6OiqV1nPUgpviODQ8w4ZQy61xpMUO/54jLICy7kRQ==", + "version": "2.880.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.880.0.tgz", + "integrity": "sha512-/dBk3ejw22ED2edzGfmJB83KXDA4wLIw5Hb+2YMhly+gOWecvevy0tML2+YN/cmxyTy+wT0E0sM7fm1v7kmHtw==", "requires": { "buffer": "4.9.2", "events": "1.1.1", @@ -741,9 +741,9 @@ "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==" }, "bignumber.js": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", - "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" }, "binary-extensions": { "version": "1.13.1", @@ -922,6 +922,15 @@ } } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1948,9 +1957,9 @@ "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, "fast-text-encoding": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.1.tgz", - "integrity": "sha512-x4FEgaz3zNRtJfLFqJmHWxkMDDvXVtaznj2V9jiP8ACUJrUgist4bP9FmDL2Vew2Y9mEQI/tG4GqabaitYp9CQ==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", + "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" }, "figures": { "version": "3.2.0", @@ -2832,6 +2841,16 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -2950,9 +2969,12 @@ }, "dependencies": { "qs": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==" + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "requires": { + "side-channel": "^1.0.4" + } } } }, @@ -3744,11 +3766,11 @@ "dev": true }, "json-bigint": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", - "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz", + "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==", "requires": { - "bignumber.js": "^7.0.0" + "bignumber.js": "^9.0.0" } }, "json-edm-parser": { @@ -4051,9 +4073,9 @@ } }, "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" }, "mime-db": { "version": "1.43.0", @@ -4309,9 +4331,9 @@ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-forge": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz", - "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.2.tgz", + "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==" }, "nodemon": { "version": "1.19.4", @@ -5122,6 +5144,23 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "dependencies": { + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" + } + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -6472,9 +6511,9 @@ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" }, "xmldom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz", - "integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz", + "integrity": "sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g==" }, "xpath.js": { "version": "1.1.0", @@ -6487,9 +6526,9 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, "yallist": { "version": "3.1.1", diff --git a/plugins/aws/iam/iamRolePolicies.js b/plugins/aws/iam/iamRolePolicies.js index fc2eae0632..7840dfd326 100644 --- a/plugins/aws/iam/iamRolePolicies.js +++ b/plugins/aws/iam/iamRolePolicies.js @@ -22,8 +22,8 @@ module.exports = { ignore_service_specific_wildcards: { name: 'Ignore Service Specific Wildcards', description: 'This allows enables you to allow attached policies (inline and managed) to use service specific wildcards in Action. ' + - 'Example: Consider a role has following inline policy' + - `{ + 'Example: Consider a role has following inline policy' + + `{ "Version": "2012-10-17", "Statement": [ { @@ -38,8 +38,8 @@ module.exports = { } ] }` + - 'If ignore_service_specific_wildcards is true, a PASS result will be generated. ' + - 'If ignore_service_specific_wildcards is false, a FAIL result will be generated.', + 'If ignore_service_specific_wildcards is true, a PASS result will be generated. ' + + 'If ignore_service_specific_wildcards is false, a FAIL result will be generated.', regex: '^(true|false)$', default: 'false' }, @@ -191,7 +191,7 @@ module.exports = { var policyName = listRolePolicies.data.PolicyNames[p]; if (getRolePolicy && - getRolePolicy[policyName] && + getRolePolicy[policyName] && getRolePolicy[policyName].data && getRolePolicy[policyName].data.PolicyDocument) { var statements = helpers.normalizePolicyDocument( @@ -245,4 +245,4 @@ function addRoleFailures(roleFailures, statements, policyType, ignoreServiceSpec if (failMsg && roleFailures.indexOf(failMsg) === -1) roleFailures.push(failMsg); } } -} +} \ No newline at end of file diff --git a/plugins/azure/cosmosdb/advancedThreatProtection.js b/plugins/azure/cosmosdb/advancedThreatProtection.js index 9e2340c51c..260da9f7bf 100644 --- a/plugins/azure/cosmosdb/advancedThreatProtection.js +++ b/plugins/azure/cosmosdb/advancedThreatProtection.js @@ -42,8 +42,9 @@ module.exports = { if (!advancedThreatProtection || advancedThreatProtection.err || !advancedThreatProtection.data) { helpers.addResult(results, 3, - `Unable to query advanced threat protection for Cosmos DB account: ${advancedThreatProtection}`, + 'Unable to query advanced threat protection for Cosmos DB account: ' + helpers.addError(advancedThreatProtection), location, account.id); + return cb(); } if (advancedThreatProtection.data.isEnabled) { @@ -57,6 +58,8 @@ module.exports = { helpers.addResult(results, 0, 'Advanced threat protection feature is not supported for current resource', location, account.id); } + + cb(); }); rcb(); diff --git a/plugins/google/clb/clbCDNEnabled.js b/plugins/google/clb/clbCDNEnabled.js index bc66d7860d..fe4f0cd5dd 100644 --- a/plugins/google/clb/clbCDNEnabled.js +++ b/plugins/google/clb/clbCDNEnabled.js @@ -23,7 +23,7 @@ module.exports = { if (backendServices.err || !backendServices.data) { helpers.addResult(results, 3, - 'Unable to query backend services: ' + helpers.addError(backendServices), region); + 'Unable to query backend services', region, null, null, backendServices.err); return rcb(); } diff --git a/plugins/google/clb/clbHttpsOnly.js b/plugins/google/clb/clbHttpsOnly.js index c6b31a8ed3..29f3a906ec 100644 --- a/plugins/google/clb/clbHttpsOnly.js +++ b/plugins/google/clb/clbHttpsOnly.js @@ -30,7 +30,7 @@ module.exports = { if (!httpProxies) return rcb(); if (httpProxies.err || !httpProxies.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(httpProxies), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, httpProxies.err); return rcb(); } diff --git a/plugins/google/clb/clbNoInstances.js b/plugins/google/clb/clbNoInstances.js index b3f40ee9aa..fd3287bb73 100644 --- a/plugins/google/clb/clbNoInstances.js +++ b/plugins/google/clb/clbNoInstances.js @@ -23,7 +23,7 @@ module.exports = { if (backendServices.err || !backendServices.data) { helpers.addResult(results, 3, - 'Unable to query backend services: ' + helpers.addError(backendServices), region); + 'Unable to query backend services', region, null, null, backendServices.err); return rcb(); } diff --git a/plugins/google/clb/clbSecurityPolicyEnabled.js b/plugins/google/clb/clbSecurityPolicyEnabled.js index 59ece2a7f7..9e90b70862 100644 --- a/plugins/google/clb/clbSecurityPolicyEnabled.js +++ b/plugins/google/clb/clbSecurityPolicyEnabled.js @@ -23,7 +23,7 @@ module.exports = { if (backendServices.err || !backendServices.data) { helpers.addResult(results, 3, - 'Unable to query backend services: ' + helpers.addError(backendServices), region); + 'Unable to query backend services', region, null, null, backendServices.err); return rcb(); } diff --git a/plugins/google/compute/autoscaleEnabled.js b/plugins/google/compute/autoscaleEnabled.js index b9ba6aec87..d1a4c2366a 100644 --- a/plugins/google/compute/autoscaleEnabled.js +++ b/plugins/google/compute/autoscaleEnabled.js @@ -19,7 +19,7 @@ module.exports = { ['instanceGroups', 'aggregatedList', ['global']]); if (instanceGroupsObj.err || !instanceGroupsObj.data) { - helpers.addResult(results, 3, 'Unable to query instance groups: ' + helpers.addError(instanceGroupsObj), 'global'); + helpers.addResult(results, 3, 'Unable to query instance groups', 'global', null, null, instanceGroupsObj.err); return callback(null, results, source); } @@ -45,13 +45,13 @@ module.exports = { ['clusters', 'list', ['global']]); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query autoscalers: ' + helpers.addError(clusters), 'global'); + helpers.addResult(results, 3, 'Unable to query autoscalers', 'global', null, null, clusters.err); } else if (!clusters.data.length) { helpers.addResult(results, 0, 'No instance groups found', 'global'); } else { clusters.data.forEach(cluster => { if (cluster.nodePools && - cluster.nodePools.length) { + cluster.nodePools.length) { cluster.nodePools.forEach(nodePool => { if (nodePool.autoscaling && nodePool.autoscaling.enabled && @@ -73,7 +73,7 @@ module.exports = { ['autoscalers', 'aggregatedList', ['global']]); if (autoscalersObj.err || !autoscalersObj.data) { - helpers.addResult(results, 3, 'Unable to query autoscalers: ' + helpers.addError(autoscalersObj), 'global'); + helpers.addResult(results, 3, 'Unable to query autoscalers', 'global', null, null, autoscalersObj.err); } else { var autoscalers = Object.values(autoscalersObj.data).filter(autoscaler =>{ return !autoscaler.warning; diff --git a/plugins/google/compute/connectSerialPortsDisabled.js b/plugins/google/compute/connectSerialPortsDisabled.js index 334a431761..37c08c7352 100644 --- a/plugins/google/compute/connectSerialPortsDisabled.js +++ b/plugins/google/compute/connectSerialPortsDisabled.js @@ -32,6 +32,7 @@ module.exports = { myError[region] = []; } myError[region].push(zone); + myError[region][zone] = instances.err; return zcb(); } @@ -63,7 +64,7 @@ module.exports = { if (myError[region] && zones[region] && (myError[region].join(',') === zones[region].join(','))) { - helpers.addResult(results, 3, 'Unable to query instances' , region); + helpers.addResult(results, 3, 'Unable to query instances', region, null, null, myError); } else if (noInstances[region] && zones[region] && (noInstances[region].join(',') === zones[region].join(','))) { diff --git a/plugins/google/compute/csekEncryptionEnabled.js b/plugins/google/compute/csekEncryptionEnabled.js index f79dbeff53..22a4089d5c 100644 --- a/plugins/google/compute/csekEncryptionEnabled.js +++ b/plugins/google/compute/csekEncryptionEnabled.js @@ -40,6 +40,7 @@ module.exports = { myError[region] = []; } myError[region].push(zone); + myError[region][zone] = disks.err; return zcb(); } @@ -65,7 +66,7 @@ module.exports = { if (myError[region] && zones[region] && (myError[region].join(',') === zones[region].join(','))) { - helpers.addResult(results, 3, 'Unable to query disks' , region); + helpers.addResult(results, 3, 'Unable to query disks', region, null, null, myError); } else if (!goodDisks.length && !badDisks.length) { helpers.addResult(results, 0, 'No disks found in the region' , region); } else if (badDisks.length) { diff --git a/plugins/google/compute/instanceLeastPrivilege.js b/plugins/google/compute/instanceLeastPrivilege.js index 3ab9140c9a..54d1cb88fd 100644 --- a/plugins/google/compute/instanceLeastPrivilege.js +++ b/plugins/google/compute/instanceLeastPrivilege.js @@ -39,6 +39,7 @@ module.exports = { myError[region] = []; } myError[region].push(zone); + myError[region][zone] = instances.err; return zcb(); } @@ -66,7 +67,7 @@ module.exports = { if (myError[region] && zones[region] && (myError[region].join(',') === zones[region].join(','))) { - helpers.addResult(results, 3, 'Unable to query instances', region); + helpers.addResult(results, 3, 'Unable to query instances', region, null, null, myError); } else if (noInstances[region] && zones[region] && @@ -86,7 +87,6 @@ module.exports = { rcb(); }, function() { callback(null, results, source); - // console.log("Results=", results); }); } }; diff --git a/plugins/google/compute/instanceLevelSSHOnly.js b/plugins/google/compute/instanceLevelSSHOnly.js index 56bbe2bb43..c271d1ca5d 100644 --- a/plugins/google/compute/instanceLevelSSHOnly.js +++ b/plugins/google/compute/instanceLevelSSHOnly.js @@ -32,6 +32,7 @@ module.exports = { myError[region] = []; } myError[region].push(zone); + myError[region][zone] = instances.err; return zcb(); } @@ -60,7 +61,7 @@ module.exports = { if (myError[region] && zones[region] && (myError[region].join(',') === zones[region].join(','))) { - helpers.addResult(results, 3, 'Unable to query instances' , region); + helpers.addResult(results, 3, 'Unable to query instances', region, null, null, myError); } else if (noInstances[region] && zones[region] && (noInstances[region].join(',') === zones[region].join(','))) { diff --git a/plugins/google/compute/instanceMaxCount.js b/plugins/google/compute/instanceMaxCount.js index b4b1b4e6f2..316fb26548 100644 --- a/plugins/google/compute/instanceMaxCount.js +++ b/plugins/google/compute/instanceMaxCount.js @@ -223,6 +223,7 @@ module.exports = { myError[region] = []; } myError[region].push(zone); + myError[region][zone] = instances.err; return zcb(); } @@ -247,7 +248,7 @@ module.exports = { if (myError[region] && zones[region] && (myError[region].join(',') === zones[region].join(','))) { - helpers.addResult(results, 3, 'Unable to query Instances', region); + helpers.addResult(results, 3, 'Unable to query Instances', region, null, null, myError); } else if (noInstances[region] && zones[region] && diff --git a/plugins/google/compute/instancesMultiAz.js b/plugins/google/compute/instancesMultiAz.js index a9637b8352..8eee43acc7 100644 --- a/plugins/google/compute/instancesMultiAz.js +++ b/plugins/google/compute/instancesMultiAz.js @@ -23,7 +23,7 @@ module.exports = { helpers.addResult(results, 3, 'Unable to query instance groups', region); return callback(null, results, source); } else if (instanceGroups.err || !instanceGroups.data) { - helpers.addResult(results, 3, 'Unable to query instance groups: ' + helpers.addError(instanceGroups), region); + helpers.addResult(results, 3, 'Unable to query instance groups', region, null, null, instanceGroups.err); return callback(null, results, source); } else { var groupName = []; diff --git a/plugins/google/compute/ipForwardingDisabled.js b/plugins/google/compute/ipForwardingDisabled.js index 348a72773c..df5c1a06ed 100644 --- a/plugins/google/compute/ipForwardingDisabled.js +++ b/plugins/google/compute/ipForwardingDisabled.js @@ -32,6 +32,7 @@ module.exports = { myError[region] = []; } myError[region].push(zone); + myError[region][zone] = instances.err; return zcb(); } @@ -52,7 +53,7 @@ module.exports = { if (myError[region] && zones[region] && (myError[region].join(',') === zones[region].join(','))) { - helpers.addResult(results, 3, 'Unable to query instances' , region); + helpers.addResult(results, 3, 'Unable to query instances', region, null, null, myError); } else if (noInstances[region] && zones[region] && (noInstances[region].join(',') === zones[region].join(','))) { diff --git a/plugins/google/compute/osLoginEnabled.js b/plugins/google/compute/osLoginEnabled.js index b9339eaeb0..0288c13814 100644 --- a/plugins/google/compute/osLoginEnabled.js +++ b/plugins/google/compute/osLoginEnabled.js @@ -28,7 +28,7 @@ module.exports = { if (projects.err || !projects.data) { helpers.addResult(results, 3, - 'Unable to query for projects: ' + helpers.addError(projects), region); + 'Unable to query for projects: ' + helpers.addError(projects), region, null, null, projects.err); return rcb(); } diff --git a/plugins/google/cryptographickeys/keyRotation.js b/plugins/google/cryptographickeys/keyRotation.js index 553e5a2096..dce172def7 100644 --- a/plugins/google/cryptographickeys/keyRotation.js +++ b/plugins/google/cryptographickeys/keyRotation.js @@ -25,14 +25,29 @@ module.exports = { var source = {}; var regions = helpers.regions(); - async.each(regions.cryptoKeys, function(region, rcb){ + async.each(regions.keyRings, function(region, rcb){ + let keyRings = helpers.addSource( + cache, source, ['keyRings', 'list', region]); + + if (!keyRings) return rcb(); + + if (keyRings.err || !keyRings.data) { + helpers.addResult(results, 3, 'Unable to query key rings', region, null, null, keyRings.err); + return rcb(); + } + + if (!keyRings.data.length) { + helpers.addResult(results, 0, 'No key rings found', region); + return rcb(); + } + let cryptoKeys = helpers.addSource( cache, source, ['cryptoKeys', 'list', region]); if (!cryptoKeys) return rcb(); if (cryptoKeys.err || !cryptoKeys.data) { - helpers.addResult(results, 3, 'Unable to query cryptographic keys: ' + helpers.addError(cryptoKeys), region); + helpers.addResult(results, 3, 'Unable to query cryptographic keys', region, null, null, cryptoKeys.err); return rcb(); } diff --git a/plugins/google/dns/dnsSecEnabled.js b/plugins/google/dns/dnsSecEnabled.js index 886d3b9083..4974dda4fb 100644 --- a/plugins/google/dns/dnsSecEnabled.js +++ b/plugins/google/dns/dnsSecEnabled.js @@ -22,7 +22,7 @@ module.exports = { if (!managedZones) return rcb(); if (managedZones.err || !managedZones.data) { - helpers.addResult(results, 3, 'Unable to query DNS managed zones: ' + helpers.addError(managedZones), region); + helpers.addResult(results, 3, 'Unable to query DNS managed zones: ' + helpers.addError(managedZones), region, null, null, managedZones.err); return rcb(); } diff --git a/plugins/google/dns/dnsSecSigningAlgorithm.js b/plugins/google/dns/dnsSecSigningAlgorithm.js index 411a621ebe..db8a2d15ba 100644 --- a/plugins/google/dns/dnsSecSigningAlgorithm.js +++ b/plugins/google/dns/dnsSecSigningAlgorithm.js @@ -23,7 +23,7 @@ module.exports = { if (managedZones.err || !managedZones.data) { helpers.addResult(results, 3, - 'Unable to query DNS managed zones: ' + helpers.addError(managedZones), region); + 'Unable to query DNS managed zones: ' + helpers.addError(managedZones), region, null, null, managedZones.err); return rcb(); } diff --git a/plugins/google/iam/corporateEmailsOnly.js b/plugins/google/iam/corporateEmailsOnly.js index 6610b17cf3..ab45a731ce 100644 --- a/plugins/google/iam/corporateEmailsOnly.js +++ b/plugins/google/iam/corporateEmailsOnly.js @@ -22,7 +22,7 @@ module.exports = { if (!iamPolicies) return rcb(); if (iamPolicies.err || !iamPolicies.data) { - helpers.addResult(results, 3, 'Unable to query for IAM policies: ' + helpers.addError(iamPolicies), region); + helpers.addResult(results, 3, 'Unable to query for IAM policies', region, null, null, iamPolicies.err); return rcb(); } diff --git a/plugins/google/iam/kmsUserSeparation.js b/plugins/google/iam/kmsUserSeparation.js index 8d364fb47d..7b560fda32 100644 --- a/plugins/google/iam/kmsUserSeparation.js +++ b/plugins/google/iam/kmsUserSeparation.js @@ -22,7 +22,7 @@ module.exports = { if (!iamPolicies) return rcb(); if (iamPolicies.err || !iamPolicies.data) { - helpers.addResult(results, 3, 'Unable to query for IAM policies: ' + helpers.addError(iamPolicies), region); + helpers.addResult(results, 3, 'Unable to query for IAM policies', region, null, null, iamPolicies.err); return rcb(); } diff --git a/plugins/google/iam/serviceAccountAdmin.js b/plugins/google/iam/serviceAccountAdmin.js index eea63200ad..27526e694e 100644 --- a/plugins/google/iam/serviceAccountAdmin.js +++ b/plugins/google/iam/serviceAccountAdmin.js @@ -25,7 +25,7 @@ module.exports = { if (!iamPolicies) return rcb(); if (iamPolicies.err || !iamPolicies.data) { - helpers.addResult(results, 3, 'Unable to query for IAM Policies: ' + helpers.addError(iamPolicies), region); + helpers.addResult(results, 3, 'Unable to query for IAM Policies', region, null, null, iamPolicies.err); return rcb(); } diff --git a/plugins/google/iam/serviceAccountKeyRotation.js b/plugins/google/iam/serviceAccountKeyRotation.js index 9ce0ba4698..07509beea3 100644 --- a/plugins/google/iam/serviceAccountKeyRotation.js +++ b/plugins/google/iam/serviceAccountKeyRotation.js @@ -30,7 +30,7 @@ module.exports = { if (!keys) return rcb(); if (keys.err || !keys.data) { - helpers.addResult(results, 3, 'Unable to query service account keys, check permissions.', region); + helpers.addResult(results, 3, 'Unable to query service account keys, check permissions.', region, null, null, keys.err); return rcb(); }; diff --git a/plugins/google/iam/serviceAccountManagedKeys.js b/plugins/google/iam/serviceAccountManagedKeys.js index eab5962995..b1eb74abdc 100644 --- a/plugins/google/iam/serviceAccountManagedKeys.js +++ b/plugins/google/iam/serviceAccountManagedKeys.js @@ -22,7 +22,7 @@ module.exports = { if (!keys) return rcb(); if (keys.err || !keys.data) { - helpers.addResult(results, 3, 'Unable to query service account keys, check permissions.', region); + helpers.addResult(results, 3, 'Unable to query service account keys, check permissions.', region, null, null, keys.err); return rcb(); }; diff --git a/plugins/google/iam/serviceAccountSeparation.js b/plugins/google/iam/serviceAccountSeparation.js index 5636d73211..93396c68c3 100644 --- a/plugins/google/iam/serviceAccountSeparation.js +++ b/plugins/google/iam/serviceAccountSeparation.js @@ -22,7 +22,7 @@ module.exports = { if (!iamPolicies) return rcb(); if (iamPolicies.err || !iamPolicies.data) { - helpers.addResult(results, 3, 'Unable to query for IAM policies: ' + helpers.addError(iamPolicies), region); + helpers.addResult(results, 3, 'Unable to query for IAM policies', region, null, null, iamPolicies.err); return rcb(); } diff --git a/plugins/google/iam/serviceAccountUser.js b/plugins/google/iam/serviceAccountUser.js index d4dd24947c..c991188701 100644 --- a/plugins/google/iam/serviceAccountUser.js +++ b/plugins/google/iam/serviceAccountUser.js @@ -22,7 +22,7 @@ module.exports = { if (!iamPolicies) return rcb(); if (iamPolicies.err || !iamPolicies.data) { - helpers.addResult(results, 3, 'Unable to query for IAM policies: ' + helpers.addError(iamPolicies), region); + helpers.addResult(results, 3, 'Unable to query for IAM policies', region, null, null, iamPolicies.err); return rcb(); } diff --git a/plugins/google/iam/serviceLimits.js b/plugins/google/iam/serviceLimits.js index 571cee23e9..c037a5c87b 100644 --- a/plugins/google/iam/serviceLimits.js +++ b/plugins/google/iam/serviceLimits.js @@ -43,7 +43,7 @@ module.exports = { if (!projects) return rcb(); if (projects.err || !projects.data) { - helpers.addResult(results, 3, 'Unable to query projects: ' + helpers.addError(projects), region); + helpers.addResult(results, 3, 'Unable to query projects', region, null, null, projects.err); return rcb(); }; diff --git a/plugins/google/iam/serviceLimits.spec.js b/plugins/google/iam/serviceLimits.spec.js index 87eedb4556..3a63bdd42d 100644 --- a/plugins/google/iam/serviceLimits.spec.js +++ b/plugins/google/iam/serviceLimits.spec.js @@ -21,7 +21,7 @@ describe('serviceLimits', function () { const callback = (err, results) => { expect(results.length).to.be.above(0) expect(results[0].status).to.equal(3) - expect(results[0].message).to.include('Unable to query projects:') + expect(results[0].message).to.include('Unable to query projects') expect(results[0].region).to.equal('global') done() }; diff --git a/plugins/google/kubernetes/aliasIpRangesEnabled.js b/plugins/google/kubernetes/aliasIpRangesEnabled.js index 4513158d66..2297fc4c64 100644 --- a/plugins/google/kubernetes/aliasIpRangesEnabled.js +++ b/plugins/google/kubernetes/aliasIpRangesEnabled.js @@ -22,12 +22,12 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } if (!clusters.data.length) { - helpers.addResult(results, 0, 'No clusters found', region); + helpers.addResult(results, 0, 'No Kubernetes clusters found', region); return rcb(); } diff --git a/plugins/google/kubernetes/aliasIpRangesEnabled.spec.js b/plugins/google/kubernetes/aliasIpRangesEnabled.spec.js index 2e433108ef..940b976a2a 100644 --- a/plugins/google/kubernetes/aliasIpRangesEnabled.spec.js +++ b/plugins/google/kubernetes/aliasIpRangesEnabled.spec.js @@ -37,7 +37,7 @@ describe('aliasIpRangesEnabled', function () { const callback = (err, results) => { expect(results.length).to.be.above(0); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('No clusters found'); + expect(results[0].message).to.include('No Kubernetes clusters found'); expect(results[0].region).to.equal('global'); done() }; diff --git a/plugins/google/kubernetes/autoNodeRepairEnabled.js b/plugins/google/kubernetes/autoNodeRepairEnabled.js index 714eeac1fd..b81d535def 100644 --- a/plugins/google/kubernetes/autoNodeRepairEnabled.js +++ b/plugins/google/kubernetes/autoNodeRepairEnabled.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/autoNodeUpgradesEnabled.js b/plugins/google/kubernetes/autoNodeUpgradesEnabled.js index d30e53be8f..a0848ab508 100644 --- a/plugins/google/kubernetes/autoNodeUpgradesEnabled.js +++ b/plugins/google/kubernetes/autoNodeUpgradesEnabled.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/basicAuthenticationDisabled.js b/plugins/google/kubernetes/basicAuthenticationDisabled.js index 71b0ad73df..20c42feec0 100644 --- a/plugins/google/kubernetes/basicAuthenticationDisabled.js +++ b/plugins/google/kubernetes/basicAuthenticationDisabled.js @@ -23,7 +23,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/clusterLabelsAdded.js b/plugins/google/kubernetes/clusterLabelsAdded.js index 8bb35e14f1..349b23ff15 100644 --- a/plugins/google/kubernetes/clusterLabelsAdded.js +++ b/plugins/google/kubernetes/clusterLabelsAdded.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/clusterLeastPrivilege.js b/plugins/google/kubernetes/clusterLeastPrivilege.js index 4838216e91..e4fbebec26 100644 --- a/plugins/google/kubernetes/clusterLeastPrivilege.js +++ b/plugins/google/kubernetes/clusterLeastPrivilege.js @@ -25,12 +25,12 @@ module.exports = { if (clusters.err || !clusters.data) { helpers.addResult(results, 3, - 'Unable to query for clusters: ' + helpers.addError(clusters), region); + 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } if (!clusters.data.length) { - helpers.addResult(results, 0, 'No clusters found', region); + helpers.addResult(results, 0, 'No Kubernetes clusters found', region); return rcb(); } diff --git a/plugins/google/kubernetes/clusterLeastPrivilege.spec.js b/plugins/google/kubernetes/clusterLeastPrivilege.spec.js index c726fb0fb9..cfaa614376 100644 --- a/plugins/google/kubernetes/clusterLeastPrivilege.spec.js +++ b/plugins/google/kubernetes/clusterLeastPrivilege.spec.js @@ -19,7 +19,7 @@ describe('clusterLeastPrivilege', function () { const callback = (err, results) => { expect(results.length).to.be.equal(1); expect(results[0].status).to.equal(0); - expect(results[0].message).to.equal('No clusters found'); + expect(results[0].message).to.equal('No Kubernetes clusters found'); expect(results[0].region).to.equal('global'); done() }; diff --git a/plugins/google/kubernetes/cosImageEnabled.js b/plugins/google/kubernetes/cosImageEnabled.js index 6a25c7e7ae..1d36313a23 100644 --- a/plugins/google/kubernetes/cosImageEnabled.js +++ b/plugins/google/kubernetes/cosImageEnabled.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/defaultServiceAccount.js b/plugins/google/kubernetes/defaultServiceAccount.js index d72cedfdb0..f9245ed9ac 100644 --- a/plugins/google/kubernetes/defaultServiceAccount.js +++ b/plugins/google/kubernetes/defaultServiceAccount.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/kubernetesAlphaDisabled.js b/plugins/google/kubernetes/kubernetesAlphaDisabled.js index a642904264..8faf1957d7 100644 --- a/plugins/google/kubernetes/kubernetesAlphaDisabled.js +++ b/plugins/google/kubernetes/kubernetesAlphaDisabled.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/legacyAuthorizationDisabled.js b/plugins/google/kubernetes/legacyAuthorizationDisabled.js index c7d42177a2..0d8db78c43 100644 --- a/plugins/google/kubernetes/legacyAuthorizationDisabled.js +++ b/plugins/google/kubernetes/legacyAuthorizationDisabled.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/loggingEnabled.js b/plugins/google/kubernetes/loggingEnabled.js index 8a89a931b8..9f51e191bf 100644 --- a/plugins/google/kubernetes/loggingEnabled.js +++ b/plugins/google/kubernetes/loggingEnabled.js @@ -27,7 +27,7 @@ module.exports = { if (clusters.err || !clusters.data) { helpers.addResult(results, 3, - 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/masterAuthorizedNetwork.js b/plugins/google/kubernetes/masterAuthorizedNetwork.js index 6837b0b09d..3840828fa9 100644 --- a/plugins/google/kubernetes/masterAuthorizedNetwork.js +++ b/plugins/google/kubernetes/masterAuthorizedNetwork.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/monitoringEnabled.js b/plugins/google/kubernetes/monitoringEnabled.js index 2578ff9d8c..cb5d8fcbce 100644 --- a/plugins/google/kubernetes/monitoringEnabled.js +++ b/plugins/google/kubernetes/monitoringEnabled.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/kubernetes/networkPolicyEnabled.js b/plugins/google/kubernetes/networkPolicyEnabled.js index d6fb0c5048..7c9fe51ac5 100644 --- a/plugins/google/kubernetes/networkPolicyEnabled.js +++ b/plugins/google/kubernetes/networkPolicyEnabled.js @@ -22,12 +22,12 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } if (!clusters.data.length) { - helpers.addResult(results, 0, 'No clusters found', region); + helpers.addResult(results, 0, 'No Kubernetes clusters found', region); return rcb(); } diff --git a/plugins/google/kubernetes/networkPolicyEnabled.spec.js b/plugins/google/kubernetes/networkPolicyEnabled.spec.js index dfb2124e93..ec7006664e 100644 --- a/plugins/google/kubernetes/networkPolicyEnabled.spec.js +++ b/plugins/google/kubernetes/networkPolicyEnabled.spec.js @@ -37,7 +37,7 @@ describe('networkPolicyEnabled', function () { const callback = (err, results) => { expect(results.length).to.be.above(0); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('No clusters found'); + expect(results[0].message).to.include('No Kubernetes clusters found'); expect(results[0].region).to.equal('global'); done() }; diff --git a/plugins/google/kubernetes/podSecurityPolicyEnabled.js b/plugins/google/kubernetes/podSecurityPolicyEnabled.js index ed7eb1a5e2..a16591c040 100644 --- a/plugins/google/kubernetes/podSecurityPolicyEnabled.js +++ b/plugins/google/kubernetes/podSecurityPolicyEnabled.js @@ -24,12 +24,12 @@ module.exports = { if (clusters.err || !clusters.data) { helpers.addResult(results, 3, - 'Unable to query for clusters: ' + helpers.addError(clusters), region); + 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } if (!clusters.data.length) { - helpers.addResult(results, 0, 'No clusters found', region); + helpers.addResult(results, 0, 'No Kubernetes clusters found', region); return rcb(); } diff --git a/plugins/google/kubernetes/podSecurityPolicyEnabled.spec.js b/plugins/google/kubernetes/podSecurityPolicyEnabled.spec.js index da91eab6b4..377fad0fd2 100644 --- a/plugins/google/kubernetes/podSecurityPolicyEnabled.spec.js +++ b/plugins/google/kubernetes/podSecurityPolicyEnabled.spec.js @@ -21,7 +21,7 @@ describe('podSecurityPolicyEnabled', function () { const callback = (err, results) => { expect(results.length).to.be.above(0); expect(results[0].status).to.equal(3); - expect(results[0].message).to.include('Unable to query for clusters'); + expect(results[0].message).to.include('Unable to query Kubernetes clusters'); expect(results[0].region).to.equal('global'); done() }; @@ -38,7 +38,7 @@ describe('podSecurityPolicyEnabled', function () { const callback = (err, results) => { expect(results.length).to.be.above(0); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('No clusters found'); + expect(results[0].message).to.include('No Kubernetes clusters found'); expect(results[0].region).to.equal('global'); done() }; diff --git a/plugins/google/kubernetes/privateClusterEnabled.js b/plugins/google/kubernetes/privateClusterEnabled.js index 72fc4b9b52..3fa3181a0b 100644 --- a/plugins/google/kubernetes/privateClusterEnabled.js +++ b/plugins/google/kubernetes/privateClusterEnabled.js @@ -24,12 +24,12 @@ module.exports = { if (clusters.err || !clusters.data) { helpers.addResult(results, 3, - 'Unable to query for clusters: ' + helpers.addError(clusters), region); + 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } if (!clusters.data.length) { - helpers.addResult(results, 0, 'No clusters found', region); + helpers.addResult(results, 0, 'No Kubernetes clusters found', region); return rcb(); } diff --git a/plugins/google/kubernetes/privateClusterEnabled.spec.js b/plugins/google/kubernetes/privateClusterEnabled.spec.js index 06178e2b64..e1faad7b98 100644 --- a/plugins/google/kubernetes/privateClusterEnabled.spec.js +++ b/plugins/google/kubernetes/privateClusterEnabled.spec.js @@ -21,7 +21,7 @@ describe('privateClusterEnabled', function () { const callback = (err, results) => { expect(results.length).to.be.above(0); expect(results[0].status).to.equal(3); - expect(results[0].message).to.include('Unable to query for clusters'); + expect(results[0].message).to.include('Unable to query Kubernetes clusters'); expect(results[0].region).to.equal('global'); done() }; @@ -38,7 +38,7 @@ describe('privateClusterEnabled', function () { const callback = (err, results) => { expect(results.length).to.be.above(0); expect(results[0].status).to.equal(0); - expect(results[0].message).to.include('No clusters found'); + expect(results[0].message).to.include('No Kubernetes clusters found'); expect(results[0].region).to.equal('global'); done() }; diff --git a/plugins/google/kubernetes/privateEndpoint.js b/plugins/google/kubernetes/privateEndpoint.js index 5a8ee04a11..b6e1f31b77 100644 --- a/plugins/google/kubernetes/privateEndpoint.js +++ b/plugins/google/kubernetes/privateEndpoint.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); }; diff --git a/plugins/google/kubernetes/webDashboardDisabled.js b/plugins/google/kubernetes/webDashboardDisabled.js index 0f3fd36ff2..dec47b1f84 100644 --- a/plugins/google/kubernetes/webDashboardDisabled.js +++ b/plugins/google/kubernetes/webDashboardDisabled.js @@ -22,7 +22,7 @@ module.exports = { if (!clusters) return rcb(); if (clusters.err || !clusters.data) { - helpers.addResult(results, 3, 'Unable to query Kubernetes clusters: ' + helpers.addError(clusters), region); + helpers.addResult(results, 3, 'Unable to query Kubernetes clusters', region, null, null, clusters.err); return rcb(); } diff --git a/plugins/google/logging/auditConfigurationLogging.js b/plugins/google/logging/auditConfigurationLogging.js index 0e87d61443..124d31e4d1 100644 --- a/plugins/google/logging/auditConfigurationLogging.js +++ b/plugins/google/logging/auditConfigurationLogging.js @@ -32,7 +32,7 @@ module.exports = { if ((metrics.err && metrics.err.length > 0) || !metrics.data) { helpers.addResult(results, 3, - 'Unable to query for log metrics: ' + helpers.addError(metrics), region); + 'Unable to query for log metrics: ' + helpers.addError(metrics), region, null, null, metrics.err); return rcb(); } diff --git a/plugins/google/logging/auditLoggingEnabled.js b/plugins/google/logging/auditLoggingEnabled.js index 3dfebba6c9..29ef831e18 100644 --- a/plugins/google/logging/auditLoggingEnabled.js +++ b/plugins/google/logging/auditLoggingEnabled.js @@ -22,7 +22,7 @@ module.exports = { if (!iamPolicies) return rcb(); if (iamPolicies.err || !iamPolicies.data) { - helpers.addResult(results, 3, 'Unable to query for IAM policies: ' + helpers.addError(iamPolicies), region); + helpers.addResult(results, 3, 'Unable to query for IAM policies', region, null, null, iamPolicies.err); return rcb(); } diff --git a/plugins/google/logging/customRoleLogging.js b/plugins/google/logging/customRoleLogging.js index 8f9e108c17..e68f975eab 100644 --- a/plugins/google/logging/customRoleLogging.js +++ b/plugins/google/logging/customRoleLogging.js @@ -30,13 +30,13 @@ module.exports = { if ((metrics.err && metrics.err.length > 0) || !metrics.data) { helpers.addResult(results, 3, - 'Unable to query for log metrics: ' + helpers.addError(metrics), region); + 'Unable to query for log metrics: ' + helpers.addError(metrics), region, null, null, metrics.err); return rcb(); } if ((alertPolicies.err && alertPolicies.err.length > 0) || !alertPolicies.data ) { helpers.addResult(results, 3, - 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region); + 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region, null, null, alertPolicies.err); return rcb(); } diff --git a/plugins/google/logging/logSinksEnabled.js b/plugins/google/logging/logSinksEnabled.js index f01e4dc5c9..1ede0a8801 100644 --- a/plugins/google/logging/logSinksEnabled.js +++ b/plugins/google/logging/logSinksEnabled.js @@ -22,7 +22,7 @@ module.exports = { if (!sinks) return rcb(); if (sinks.err || !sinks.data) { - helpers.addResult(results, 3, 'Unable to query sinks: ' + helpers.addError(sinks), region); + helpers.addResult(results, 3, 'Unable to query sinks: ' + helpers.addError(sinks), region, null, null, sinks.err); return rcb(); } @@ -51,7 +51,7 @@ module.exports = { ['buckets', 'list', region]); if (!buckets || buckets.err || !buckets.data) { - helpers.addResult(results, 3, 'Unable to query buckets: ' + helpers.addError(buckets), region); + helpers.addResult(results, 3, 'Unable to query buckets: ' + helpers.addError(buckets), region, null, null, buckets.err); } else if (!buckets.data.length) { helpers.addResult(results, 2, 'No log bucket found', region); rcb(); diff --git a/plugins/google/logging/projectOwnershipLogging.js b/plugins/google/logging/projectOwnershipLogging.js index c78c6cc117..ac9732ca2c 100644 --- a/plugins/google/logging/projectOwnershipLogging.js +++ b/plugins/google/logging/projectOwnershipLogging.js @@ -33,13 +33,13 @@ module.exports = { if ((metrics.err && metrics.err.length > 0) || !metrics.data) { helpers.addResult(results, 3, - 'Unable to query for log metrics: ' + helpers.addError(metrics), region); + 'Unable to query for log metrics: ' + helpers.addError(metrics), region, null, null, metrics.err); return rcb(); } if ((alertPolicies.err && alertPolicies.err.length > 0) || !alertPolicies.data ) { helpers.addResult(results, 3, - 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region); + 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region, null, null, alertPolicies.err); return rcb(); } diff --git a/plugins/google/logging/sqlConfigurationLogging.js b/plugins/google/logging/sqlConfigurationLogging.js index 8a5228bf7d..0e10533fc8 100644 --- a/plugins/google/logging/sqlConfigurationLogging.js +++ b/plugins/google/logging/sqlConfigurationLogging.js @@ -30,13 +30,13 @@ module.exports = { if ((metrics.err && metrics.err.length > 0) || !metrics.data) { helpers.addResult(results, 3, - 'Unable to query for log metrics: ' + helpers.addError(metrics), region); + 'Unable to query for log metrics: ' + helpers.addError(metrics), region, null, null, metrics.err); return rcb(); } if ((alertPolicies.err && alertPolicies.err.length > 0) || !alertPolicies.data ) { helpers.addResult(results, 3, - 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region); + 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region, null, null, alertPolicies.err); return rcb(); } diff --git a/plugins/google/logging/storagePermissionsLogging.js b/plugins/google/logging/storagePermissionsLogging.js index 94ce3fe6c3..88a35690a0 100644 --- a/plugins/google/logging/storagePermissionsLogging.js +++ b/plugins/google/logging/storagePermissionsLogging.js @@ -34,13 +34,13 @@ module.exports = { if ((metrics.err && metrics.err.length > 0) || !metrics.data) { helpers.addResult(results, 3, - 'Unable to query for log metrics: ' + helpers.addError(metrics), region); + 'Unable to query for log metrics: ' + helpers.addError(metrics), region, null, null, metrics.err); return rcb(); } if ((alertPolicies.err && alertPolicies.err.length > 0) || !alertPolicies.data ) { helpers.addResult(results, 3, - 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region); + 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region, null, null, alertPolicies.err); return rcb(); } diff --git a/plugins/google/logging/vpcFirewallRuleLogging.js b/plugins/google/logging/vpcFirewallRuleLogging.js index bfdb0209be..69e590a811 100644 --- a/plugins/google/logging/vpcFirewallRuleLogging.js +++ b/plugins/google/logging/vpcFirewallRuleLogging.js @@ -30,13 +30,13 @@ module.exports = { if ((metrics.err && metrics.err.length > 0) || !metrics.data) { helpers.addResult(results, 3, - 'Unable to query for log metrics: ' + helpers.addError(metrics), region); + 'Unable to query for log metrics: ' + helpers.addError(metrics), region, null, null, metrics.err); return rcb(); } if ((alertPolicies.err && alertPolicies.err.length > 0) || !alertPolicies.data ) { helpers.addResult(results, 3, - 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region); + 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region, null, null, alertPolicies.err); return rcb(); } diff --git a/plugins/google/logging/vpcNetworkLogging.js b/plugins/google/logging/vpcNetworkLogging.js index 598b5310a4..015b361847 100644 --- a/plugins/google/logging/vpcNetworkLogging.js +++ b/plugins/google/logging/vpcNetworkLogging.js @@ -33,13 +33,13 @@ module.exports = { if ((metrics.err && metrics.err.length > 0) || !metrics.data) { helpers.addResult(results, 3, - 'Unable to query for log metrics: ' + helpers.addError(metrics), region); + 'Unable to query for log metrics: ' + helpers.addError(metrics), region, null, null, metrics.err); return rcb(); } if ((alertPolicies.err && alertPolicies.err.length > 0) || !alertPolicies.data ) { helpers.addResult(results, 3, - 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region); + 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region, null, null, alertPolicies.err); return rcb(); } diff --git a/plugins/google/logging/vpcNetworkRouteLogging.js b/plugins/google/logging/vpcNetworkRouteLogging.js index 0beed0744b..9b98a109cc 100644 --- a/plugins/google/logging/vpcNetworkRouteLogging.js +++ b/plugins/google/logging/vpcNetworkRouteLogging.js @@ -30,13 +30,13 @@ module.exports = { if ((metrics.err && metrics.err.length > 0) || !metrics.data) { helpers.addResult(results, 3, - 'Unable to query for log metrics: ' + helpers.addError(metrics), region); + 'Unable to query for log metrics: ' + helpers.addError(metrics), region, null, null, metrics.err); return rcb(); } if ((alertPolicies.err && alertPolicies.err.length > 0) || !alertPolicies.data ) { helpers.addResult(results, 3, - 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region); + 'Unable to query for log alert policies: ' + helpers.addError(alertPolicies), region, null, null, alertPolicies.err); return rcb(); } diff --git a/plugins/google/sql/anyHostRootAccess.js b/plugins/google/sql/anyHostRootAccess.js index 7f59163271..ee482eef9e 100644 --- a/plugins/google/sql/anyHostRootAccess.js +++ b/plugins/google/sql/anyHostRootAccess.js @@ -23,7 +23,7 @@ module.exports = { if (!users) return rcb(); if (users.err || !users.data) { - helpers.addResult(results, 3, 'Unable to query SQL users: ' + helpers.addError(users), region); + helpers.addResult(results, 3, 'Unable to query SQL users: ' + helpers.addError(users), region, null, null, users.err); return rcb(); } diff --git a/plugins/google/sql/dbAutomatedBackups.js b/plugins/google/sql/dbAutomatedBackups.js index 28f8311a00..c9c8444361 100644 --- a/plugins/google/sql/dbAutomatedBackups.js +++ b/plugins/google/sql/dbAutomatedBackups.js @@ -22,7 +22,7 @@ module.exports = { if (!sqlInstances) return rcb(); if (sqlInstances.err || !sqlInstances.data) { - helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region); + helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region, null, null, sqlInstances.err); return rcb(); } diff --git a/plugins/google/sql/dbMultiAz.js b/plugins/google/sql/dbMultiAz.js index c75c24a720..0761e443a8 100644 --- a/plugins/google/sql/dbMultiAz.js +++ b/plugins/google/sql/dbMultiAz.js @@ -22,7 +22,7 @@ module.exports = { if (!sqlInstances) return rcb(); if (sqlInstances.err || !sqlInstances.data) { - helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region); + helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region, null, null, sqlInstances.err); return rcb(); } diff --git a/plugins/google/sql/dbPubliclyAccessible.js b/plugins/google/sql/dbPubliclyAccessible.js index 037614dec5..c53cbbb6c5 100644 --- a/plugins/google/sql/dbPubliclyAccessible.js +++ b/plugins/google/sql/dbPubliclyAccessible.js @@ -32,7 +32,7 @@ module.exports = { if (!sqlInstances) return rcb(); if (sqlInstances.err || !sqlInstances.data) { - helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region); + helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region, null, null, sqlInstances.err); return rcb(); } diff --git a/plugins/google/sql/dbRestorable.js b/plugins/google/sql/dbRestorable.js index aa7e1e312c..725340b587 100644 --- a/plugins/google/sql/dbRestorable.js +++ b/plugins/google/sql/dbRestorable.js @@ -27,7 +27,7 @@ module.exports = { if (!sqlInstances) return rcb(); if (sqlInstances.err || !sqlInstances.data) { - helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region); + helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region, null, null, sqlInstances.err); return rcb(); } diff --git a/plugins/google/sql/dbSSLEnabled.js b/plugins/google/sql/dbSSLEnabled.js index 01770e5db6..39c6f85738 100644 --- a/plugins/google/sql/dbSSLEnabled.js +++ b/plugins/google/sql/dbSSLEnabled.js @@ -29,7 +29,7 @@ module.exports = { if (!sqlInstances) return rcb(); if (sqlInstances.err || !sqlInstances.data) { - helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region); + helpers.addResult(results, 3, 'Unable to query SQL instances: ' + helpers.addError(sqlInstances), region, null, null, sqlInstances.err); return rcb(); } diff --git a/plugins/google/storage/bucketAllUsersPolicy.js b/plugins/google/storage/bucketAllUsersPolicy.js index 58f3adc3cf..44c4f0ec91 100644 --- a/plugins/google/storage/bucketAllUsersPolicy.js +++ b/plugins/google/storage/bucketAllUsersPolicy.js @@ -16,13 +16,28 @@ module.exports = { var regions = helpers.regions(); async.each(regions.buckets, function(region, rcb){ + let buckets = helpers.addSource( + cache, source, ['buckets', 'list', region]); + + if (!buckets) return rcb(); + + if (buckets.err || !buckets.data) { + helpers.addResult(results, 3, 'Unable to query storage buckets: ' + helpers.addError(buckets), region, null, null, buckets.err); + return rcb(); + } + + if (!helpers.hasBuckets(buckets.data)) { + helpers.addResult(results, 0, 'No storage buckets found', region); + return rcb(); + } + let bucketPolicyPolicies = helpers.addSource(cache, source, ['buckets', 'getIamPolicy', region]); if (!bucketPolicyPolicies) return rcb(); if (bucketPolicyPolicies.err || !bucketPolicyPolicies.data) { - helpers.addResult(results, 3, 'Unable to query storage buckets: ' + helpers.addError(bucketPolicyPolicies), region); + helpers.addResult(results, 3, 'Unable to query bucket policies: ' + helpers.addError(bucketPolicyPolicies), region, null, null, bucketPolicyPolicies.err); return rcb(); } diff --git a/plugins/google/storage/bucketAllUsersPolicy.spec.js b/plugins/google/storage/bucketAllUsersPolicy.spec.js index 28d24d6fc9..04e8bb86a9 100644 --- a/plugins/google/storage/bucketAllUsersPolicy.spec.js +++ b/plugins/google/storage/bucketAllUsersPolicy.spec.js @@ -2,9 +2,15 @@ var assert = require('assert'); var expect = require('chai').expect; var plugin = require('./bucketAllUsersPolicy'); -const createCache = (err, data) => { +const createCache = (err, data, bucketErr, bucketData) => { return { buckets: { + list: { + 'global': { + err: bucketErr, + data: bucketData + } + }, getIamPolicy: { 'global': { err: err, @@ -29,6 +35,8 @@ describe('bucketAllUsersPolicy', function () { const cache = createCache( ['error'], null, + ['error'], + null, ); plugin.run(cache, {}, callback); @@ -44,12 +52,18 @@ describe('bucketAllUsersPolicy', function () { const cache = createCache( null, - [], + null, + null, + [ + { + "kind": "storage#buckets" + } + ] ); plugin.run(cache, {}, callback); }); - it('should give passing result if no bucks have anonymous or public access', function (done) { + it('should give passing result if no buckets have anonymous or public access', function (done) { const callback = (err, results) => { expect(results.length).to.be.above(0); expect(results[0].status).to.equal(0); @@ -95,6 +109,35 @@ describe('bucketAllUsersPolicy', function () { "visibility": "public", "kind": "dns#managedZone" } + ], + null, + [ + { + "kind": "storage#bucket", + "selfLink": "https://www.googleapis.com/storage/v1/b/testio", + "id": "testio", + "name": "testio", + "projectNumber": "664367550207", + "metageneration": "1", + "location": "US", + "storageClass": "STANDARD", + "etag": "CAE=", + "defaultEventBasedHold": false, + "timeCreated": "2021-04-06T16:06:14.799Z", + "updated": "2021-04-06T16:06:14.799Z", + "iamConfiguration": { + "bucketPolicyOnly": { + "enabled": true, + "lockedTime": "2021-07-05T16:06:14.799Z" + }, + "uniformBucketLevelAccess": { + "enabled": true, + "lockedTime": "2021-07-05T16:06:14.799Z" + } + }, + "locationType": "multi-region", + "satisfiesPZS": false + } ] ); @@ -182,6 +225,35 @@ describe('bucketAllUsersPolicy', function () { ], "etag": "CAs=" } + ], + null, + [ + { + "kind": "storage#bucket", + "selfLink": "https://www.googleapis.com/storage/v1/b/testio", + "id": "testio", + "name": "testio", + "projectNumber": "664367550207", + "metageneration": "1", + "location": "US", + "storageClass": "STANDARD", + "etag": "CAE=", + "defaultEventBasedHold": false, + "timeCreated": "2021-04-06T16:06:14.799Z", + "updated": "2021-04-06T16:06:14.799Z", + "iamConfiguration": { + "bucketPolicyOnly": { + "enabled": true, + "lockedTime": "2021-07-05T16:06:14.799Z" + }, + "uniformBucketLevelAccess": { + "enabled": true, + "lockedTime": "2021-07-05T16:06:14.799Z" + } + }, + "locationType": "multi-region", + "satisfiesPZS": false + } ] ); diff --git a/plugins/google/storage/bucketLogging.js b/plugins/google/storage/bucketLogging.js index 451a46569c..72e66c4be0 100644 --- a/plugins/google/storage/bucketLogging.js +++ b/plugins/google/storage/bucketLogging.js @@ -26,14 +26,15 @@ module.exports = { if (!buckets) return rcb(); if (buckets.err || !buckets.data) { - helpers.addResult(results, 3, 'Unable to query storage buckets: ' + helpers.addError(buckets), region); + helpers.addResult(results, 3, 'Unable to query storage buckets: ' + helpers.addError(buckets), region, null, null, buckets.err); return rcb(); } - if (!buckets.data.length) { + if (!helpers.hasBuckets(buckets.data)) { helpers.addResult(results, 0, 'No storage buckets found', region); return rcb(); } + var bucketFound = false; buckets.data.forEach(bucket => { if (bucket.id) { diff --git a/plugins/google/storage/bucketRetentionPolicy.js b/plugins/google/storage/bucketRetentionPolicy.js index 77296d242f..56c6e00a75 100644 --- a/plugins/google/storage/bucketRetentionPolicy.js +++ b/plugins/google/storage/bucketRetentionPolicy.js @@ -34,11 +34,11 @@ module.exports = { if (!buckets) return rcb(); if (buckets.err || !buckets.data) { - helpers.addResult(results, 3, 'Unable to query storage buckets: ' + helpers.addError(buckets), region); + helpers.addResult(results, 3, 'Unable to query storage buckets: ' + helpers.addError(buckets), region, null, null, buckets.err); return rcb(); } - if (!buckets.data.length) { + if (!helpers.hasBuckets(buckets.data)) { helpers.addResult(results, 0, 'No storage buckets found', region); return rcb(); } diff --git a/plugins/google/storage/bucketVersioning.js b/plugins/google/storage/bucketVersioning.js index 65747743f3..3db21e9fa4 100644 --- a/plugins/google/storage/bucketVersioning.js +++ b/plugins/google/storage/bucketVersioning.js @@ -23,14 +23,15 @@ module.exports = { if (!buckets) return rcb(); if (buckets.err || !buckets.data) { - helpers.addResult(results, 3, 'Unable to query storage buckets: ' + helpers.addError(buckets), region); + helpers.addResult(results, 3, 'Unable to query storage buckets: ' + helpers.addError(buckets), region, null, null, buckets.err); return rcb(); } - if (!buckets.data.length) { + if (!helpers.hasBuckets(buckets.data)) { helpers.addResult(results, 0, 'No storage buckets found', region); return rcb(); } + buckets.data.forEach(bucket => { if (bucket.id) { if (bucket.versioning && diff --git a/plugins/google/vpcnetwork/defaultVpcInUse.js b/plugins/google/vpcnetwork/defaultVpcInUse.js index 62b62abb6d..88901246fc 100644 --- a/plugins/google/vpcnetwork/defaultVpcInUse.js +++ b/plugins/google/vpcnetwork/defaultVpcInUse.js @@ -28,7 +28,7 @@ module.exports = { if (!networks) return rcb(); if (networks.err || !networks.data) { - helpers.addResult(results, 3, 'Unable to query networks: ' + helpers.addError(networks), region); + helpers.addResult(results, 3, 'Unable to query networks: ' + helpers.addError(networks), region, null, null, networks.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/excessiveFirewallRules.js b/plugins/google/vpcnetwork/excessiveFirewallRules.js index f33b98dd2c..cebb5f670c 100644 --- a/plugins/google/vpcnetwork/excessiveFirewallRules.js +++ b/plugins/google/vpcnetwork/excessiveFirewallRules.js @@ -49,7 +49,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/flowLogsEnabled.js b/plugins/google/vpcnetwork/flowLogsEnabled.js index e5b6c0c42e..b83d72b8f0 100644 --- a/plugins/google/vpcnetwork/flowLogsEnabled.js +++ b/plugins/google/vpcnetwork/flowLogsEnabled.js @@ -29,7 +29,7 @@ module.exports = { if (!subnetworks) return rcb(); if (subnetworks.err || !subnetworks.data) { - helpers.addResult(results, 3, 'Unable to query subnets: ' + helpers.addError(subnetworks), region); + helpers.addResult(results, 3, 'Unable to query subnets: ' + helpers.addError(subnetworks), region, null, null, subnetworks.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/multipleSubnets.js b/plugins/google/vpcnetwork/multipleSubnets.js index f989cf6b0c..f7443e2c51 100644 --- a/plugins/google/vpcnetwork/multipleSubnets.js +++ b/plugins/google/vpcnetwork/multipleSubnets.js @@ -22,7 +22,7 @@ module.exports = { if (!networks) return rcb(); if (networks.err || !networks.data) { - helpers.addResult(results, 3, 'Unable to query networks: ' + helpers.addError(networks), region); + helpers.addResult(results, 3, 'Unable to query networks: ' + helpers.addError(networks), region, null, null, networks.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openAllPorts.js b/plugins/google/vpcnetwork/openAllPorts.js index 51aac53657..4b7c54538c 100644 --- a/plugins/google/vpcnetwork/openAllPorts.js +++ b/plugins/google/vpcnetwork/openAllPorts.js @@ -31,7 +31,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openCIFS.js b/plugins/google/vpcnetwork/openCIFS.js index a9708d362e..4f1341caeb 100644 --- a/plugins/google/vpcnetwork/openCIFS.js +++ b/plugins/google/vpcnetwork/openCIFS.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openCassandra.js b/plugins/google/vpcnetwork/openCassandra.js index 6968fcd763..1de1c01331 100644 --- a/plugins/google/vpcnetwork/openCassandra.js +++ b/plugins/google/vpcnetwork/openCassandra.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openDNS.js b/plugins/google/vpcnetwork/openDNS.js index 6702e17ee7..560997bca4 100644 --- a/plugins/google/vpcnetwork/openDNS.js +++ b/plugins/google/vpcnetwork/openDNS.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openDocker.js b/plugins/google/vpcnetwork/openDocker.js index c71bc36cd3..0e6cc4bd60 100644 --- a/plugins/google/vpcnetwork/openDocker.js +++ b/plugins/google/vpcnetwork/openDocker.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openFTP.js b/plugins/google/vpcnetwork/openFTP.js index d78b8fcfb6..600cbf22ce 100644 --- a/plugins/google/vpcnetwork/openFTP.js +++ b/plugins/google/vpcnetwork/openFTP.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openHadoopNameNode.js b/plugins/google/vpcnetwork/openHadoopNameNode.js index be216da3fb..9f94f278b2 100644 --- a/plugins/google/vpcnetwork/openHadoopNameNode.js +++ b/plugins/google/vpcnetwork/openHadoopNameNode.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openHadoopNameNodeWebUI.js b/plugins/google/vpcnetwork/openHadoopNameNodeWebUI.js index 578730644d..652ba145d3 100644 --- a/plugins/google/vpcnetwork/openHadoopNameNodeWebUI.js +++ b/plugins/google/vpcnetwork/openHadoopNameNodeWebUI.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openKibana.js b/plugins/google/vpcnetwork/openKibana.js index 4e6dae22cb..990e60eb87 100644 --- a/plugins/google/vpcnetwork/openKibana.js +++ b/plugins/google/vpcnetwork/openKibana.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openMongo.js b/plugins/google/vpcnetwork/openMongo.js index c12a244666..f26227e27a 100644 --- a/plugins/google/vpcnetwork/openMongo.js +++ b/plugins/google/vpcnetwork/openMongo.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openMsSQL.js b/plugins/google/vpcnetwork/openMsSQL.js index 4795e88f00..0d08ec25c5 100644 --- a/plugins/google/vpcnetwork/openMsSQL.js +++ b/plugins/google/vpcnetwork/openMsSQL.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openMySQL.js b/plugins/google/vpcnetwork/openMySQL.js index 2100e5998d..b620ea1e74 100644 --- a/plugins/google/vpcnetwork/openMySQL.js +++ b/plugins/google/vpcnetwork/openMySQL.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openNetBIOS.js b/plugins/google/vpcnetwork/openNetBIOS.js index 2ecccaaae0..2b53220c15 100644 --- a/plugins/google/vpcnetwork/openNetBIOS.js +++ b/plugins/google/vpcnetwork/openNetBIOS.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openOracle.js b/plugins/google/vpcnetwork/openOracle.js index 8f7d6bd61f..2ccdf3a586 100644 --- a/plugins/google/vpcnetwork/openOracle.js +++ b/plugins/google/vpcnetwork/openOracle.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openOracleAutoDataWarehouse.js b/plugins/google/vpcnetwork/openOracleAutoDataWarehouse.js index 1bee1cb54e..523630b4f7 100644 --- a/plugins/google/vpcnetwork/openOracleAutoDataWarehouse.js +++ b/plugins/google/vpcnetwork/openOracleAutoDataWarehouse.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openPostgreSQL.js b/plugins/google/vpcnetwork/openPostgreSQL.js index 07f04928fc..4f30b1367d 100644 --- a/plugins/google/vpcnetwork/openPostgreSQL.js +++ b/plugins/google/vpcnetwork/openPostgreSQL.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openRDP.js b/plugins/google/vpcnetwork/openRDP.js index c4038fccbb..ef57663882 100644 --- a/plugins/google/vpcnetwork/openRDP.js +++ b/plugins/google/vpcnetwork/openRDP.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openRPC.js b/plugins/google/vpcnetwork/openRPC.js index cd9d907102..d8f6df5583 100644 --- a/plugins/google/vpcnetwork/openRPC.js +++ b/plugins/google/vpcnetwork/openRPC.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openRedis.js b/plugins/google/vpcnetwork/openRedis.js index 71cb9672c0..fec2b8fff4 100644 --- a/plugins/google/vpcnetwork/openRedis.js +++ b/plugins/google/vpcnetwork/openRedis.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openSMBoTCP.js b/plugins/google/vpcnetwork/openSMBoTCP.js index 180f203a2f..84960dda1f 100644 --- a/plugins/google/vpcnetwork/openSMBoTCP.js +++ b/plugins/google/vpcnetwork/openSMBoTCP.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openSMTP.js b/plugins/google/vpcnetwork/openSMTP.js index b17b20911e..e53d35efb5 100644 --- a/plugins/google/vpcnetwork/openSMTP.js +++ b/plugins/google/vpcnetwork/openSMTP.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openSQLServer.js b/plugins/google/vpcnetwork/openSQLServer.js index 5fb0e4a84b..81f0352c52 100644 --- a/plugins/google/vpcnetwork/openSQLServer.js +++ b/plugins/google/vpcnetwork/openSQLServer.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openSSH.js b/plugins/google/vpcnetwork/openSSH.js index 2f4ad008dc..d9f6e7eb67 100644 --- a/plugins/google/vpcnetwork/openSSH.js +++ b/plugins/google/vpcnetwork/openSSH.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openSalt.js b/plugins/google/vpcnetwork/openSalt.js index bda2cca35e..f783cdcd82 100644 --- a/plugins/google/vpcnetwork/openSalt.js +++ b/plugins/google/vpcnetwork/openSalt.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openTelnet.js b/plugins/google/vpcnetwork/openTelnet.js index a0d7836b0c..fc1a7d44ea 100644 --- a/plugins/google/vpcnetwork/openTelnet.js +++ b/plugins/google/vpcnetwork/openTelnet.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openVNCClient.js b/plugins/google/vpcnetwork/openVNCClient.js index b1c9691239..d4ae5a4d95 100644 --- a/plugins/google/vpcnetwork/openVNCClient.js +++ b/plugins/google/vpcnetwork/openVNCClient.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/openVNCServer.js b/plugins/google/vpcnetwork/openVNCServer.js index db6078208c..72410632ea 100644 --- a/plugins/google/vpcnetwork/openVNCServer.js +++ b/plugins/google/vpcnetwork/openVNCServer.js @@ -22,7 +22,7 @@ module.exports = { if (!firewalls) return rcb(); if (firewalls.err || !firewalls.data) { - helpers.addResult(results, 3, 'Unable to query firewall rules: ' + helpers.addError(firewalls), region); + helpers.addResult(results, 3, 'Unable to query firewall rules', region, null, null, firewalls.err); return rcb(); } diff --git a/plugins/google/vpcnetwork/privateAccessEnabled.js b/plugins/google/vpcnetwork/privateAccessEnabled.js index dea7a2d611..d3fc3c10ba 100644 --- a/plugins/google/vpcnetwork/privateAccessEnabled.js +++ b/plugins/google/vpcnetwork/privateAccessEnabled.js @@ -27,7 +27,7 @@ module.exports = { if (!subnetworks) return rcb(); if (subnetworks.err || !subnetworks.data) { - helpers.addResult(results, 3, 'Unable to query subnetworks: ' + helpers.addError(subnetworks), region); + helpers.addResult(results, 3, 'Unable to query subnetworks: ' + helpers.addError(subnetworks), region, null, null, subnetworks.err); return rcb(); }; From 0b1e84641f48c4dca8dba4a3afbfe1333c8b9d62 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 15 May 2021 03:55:56 +0500 Subject: [PATCH 02/11] feature: Added collector for Alibaba cloud (#683) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector --- collectors/alibaba/collector.js | 319 ++++++++++++++++++++++++++++++++ collectors/alibaba/index.js | 26 +++ helpers/alibaba/functions.js | 0 helpers/alibaba/index.js | 17 ++ helpers/alibaba/regions.js | 37 ++++ index.js | 43 +++-- 6 files changed, 429 insertions(+), 13 deletions(-) create mode 100644 collectors/alibaba/collector.js create mode 100644 collectors/alibaba/index.js create mode 100644 helpers/alibaba/functions.js create mode 100644 helpers/alibaba/index.js create mode 100644 helpers/alibaba/regions.js diff --git a/collectors/alibaba/collector.js b/collectors/alibaba/collector.js new file mode 100644 index 0000000000..d2454340c1 --- /dev/null +++ b/collectors/alibaba/collector.js @@ -0,0 +1,319 @@ +/********************* +Collector - The collector will query Alibaba APIs for the information required +to run the CloudSploit scans. This data will be returned in the callback +as a JSON object. + +Arguments: +- AlibabaConfig: If using an access key/secret, pass in the config object. Pass null if not. +- settings: custom settings for the scan. Properties: +- skip_regions: (Optional) List of regions to skip +- api_calls: (Optional) If provided, will only query these APIs. +- Example: +{ + "skip_regions": ["cn-hangzhou", "cn-shanghai"], + "api_calls": ["ECS:DescribeInstances", "VPC:DescribeFlowLogs"] +} +- callback: Function to call when the collection is complete +*********************/ + +var alicloud = require('@alicloud/pop-core'); +var async = require('async'); +var helpers = require(__dirname + '/../../helpers/alibaba'); + +var regions = helpers.regions(); + +var regionEndpointMap = {}; + +var globalServices = [ + 'RAM' +]; + +var calls = { + ECS: { + DescribeInstances: { + property: 'Instances', + subProperty: 'Instance', + paginate: 'NextToken', + apiVersion: '2014-05-26' + } + }, + RAM: { + ListPolicies: { + property: 'Policies', + subProperty: 'Policy', + apiVersion: '2015-05-01', + paginate: 'Marker' + }, + ListUsers: { + property: 'Users', + subProperty: 'User', + apiVersion: '2015-05-01', + paginate: 'Marker' + } + }, + GBDB: { + DescribeDBInstances: { + property: 'Items', + subProperty: 'DBInstance', + apiVersion: '2015-05-01', + paginate: 'Pages' + } + }, + VPC: { + DescribeVpcs: { + property: 'Vpcs', + subProperty: 'Vpc', + apiVersion: '2016-04-28', + paginate: 'Pages' + }, + DescribeVSwitches: { + property: 'VSwitches', + subProperty: 'VSwitch', + apiVersion: '2016-04-28', + paginate: 'Pages' + } + }, + RDS: { + DescribeDBInstances: { + property: 'Items', + subProperty: 'DBInstance', + apiVersion: '2014-08-15', + paginate: 'Pages' + } + }, + POLARDB: { + DescribeDBClusters: { + property: 'Items', + subProperty: 'DBCluster', + apiVersion: '2017-08-01', + paginate: 'Pages' + } + } +}; + +var postcalls = [ + { + ECS: { + DescribeInstanceStatus: { + reliesOnService: 'ecs', + reliesOnCall: 'DescribeInstances', + filterKey: ['InstanceId'], + filterValue: ['InstanceId'], + apiVersion: '2014-05-26' + } + }, + RAM: { + GetPolicy: { + reliesOnService: 'ram', + reliesOnCall: 'ListPolicies', + filterKey: ['PolicyName', 'PolicyType'], + filterValue: ['PolicyName', 'PolicyType'], + resultFilter: 'DefaultPolicyVersion', + apiVersion: '2015-05-01' + }, + GetUser: { + reliesOnService: 'ram', + reliesOnCall: 'ListUsers', + filterKey: ['UserName'], + filterValue: ['UserName'], + resultFilter: 'User', + apiVersion: '2015-05-01' + } + } + } +]; + +var collect = function(AlibabaConfig, settings, callback) { + if (settings.gather) { + return callback(null, calls, postcalls); + } + + var collection = {}; + + async.eachOfLimit(calls, 10, function(call, service, serviceCb) { + let serviceLower = service.toLowerCase(); + if (!collection[serviceLower]) collection[serviceLower] = {}; + + async.eachOfLimit(call, 15, function(callObj, callKey, callCb) { + if (settings.api_calls && settings.api_calls.indexOf(service + ':' + callKey) === -1) return callCb(); + if (!collection[serviceLower][callKey]) collection[serviceLower][callKey] = {}; + + let callRegions = regions[serviceLower]; + let requestOption = { method: callObj.method || 'POST' }; + + async.eachLimit(callRegions, helpers.MAX_REGIONS_AT_A_TIME, function(region, regionCb) { + if (settings.skip_regions && + settings.skip_regions.indexOf(region) > -1 && + globalServices.indexOf(service) === -1) return regionCb(); + if (!collection[serviceLower][callKey][region]) collection[serviceLower][callKey][region] = {}; + + let LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); + + let endpoint = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? + `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; + LocalAlibabaConfig['endpoint'] = endpoint; + LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; + let client = new alicloud(LocalAlibabaConfig); + let paginating = false; + let pageNumber = 1; + var clientCb = function(err, data) { + if (err) collection[serviceLower][callKey][region].err = err; + if (!data) return regionCb(); + if (callObj.property && !data[callObj.property]) return regionCb(); + if (callObj.subProperty && !data[callObj.property][callObj.subProperty]) return regionCb(); + + var dataToAdd = callObj.subProperty ? data[callObj.property][callObj.subProperty] : data[callObj.property]; + + if (paginating) { + collection[serviceLower][callKey][region].data = collection[serviceLower][callKey][region].data.concat(dataToAdd); + } else { + collection[serviceLower][callKey][region].data = dataToAdd; + } + + if (callObj.paginate && callObj.paginate == 'Pages' && settings.paginate) { + if (data['PageNumber'] && data['PageSize'] && data['TotalCount']) { + let pageSize = callObj.pageSize || parseInt(data['PageSize']); + let totalCount = parseInt(data['TotalCount']); + + if ((pageNumber*pageSize) < totalCount) { + paginating = true; + pageNumber += 1; + let paginateParams = { PageNumber: pageNumber, PageSize: pageSize}; + return execute(null, paginateParams); + } + } + } + + var nextToken = callObj.paginate; + if (settings.paginate && nextToken && data[nextToken]) { + paginating = true; + var paginateProp = callObj.paginateReqProp ? callObj.paginateReqProp : nextToken; + return execute([paginateProp, data[nextToken]]); + } + + if (callObj.rateLimit) { + setTimeout(function() { + regionCb(); + }, callObj.rateLimit); + } else { + regionCb(); + } + }; + + function execute(nextToken, paginateParams) { + let localParams = JSON.parse(JSON.stringify(callObj.params || {})); + localParams['RegionId'] = region; + if (nextToken) localParams[nextToken[0]] = nextToken[1]; + else if (paginateParams) localParams = {...localParams, ...paginateParams}; + + client.request(callKey, localParams, requestOption).then((result) => { + clientCb(null, result); + }, (err) => { + clientCb(err); + }); + } + + execute(); + }, function() { + callCb(); + }); + }, function() { + serviceCb(); + }); + }, function() { + async.eachSeries(postcalls, function(postcallObj, postcallCb) { + async.eachOfLimit(postcallObj, 10, function(serviceObj, service, serviceCb) { + var serviceLower = service.toLowerCase(); + if (!collection[serviceLower]) collection[serviceLower] = {}; + + async.eachOfLimit(serviceObj, 1, function(callObj, callKey, callCb) { + if (settings.api_calls && settings.api_calls.indexOf(service + ':' + callKey) === -1) return callCb(); + if (!collection[serviceLower][callKey]) collection[serviceLower][callKey] = {}; + + var requestOption = { method: callObj.method || 'POST' }; + async.eachLimit(regions[serviceLower], helpers.MAX_REGIONS_AT_A_TIME, function(region, regionCb) { + if (settings.skip_regions && + settings.skip_regions.indexOf(region) > -1 && + globalServices.indexOf(service) === -1) return regionCb(); + if (!collection[serviceLower][callKey][region]) collection[serviceLower][callKey][region] = {}; + + if (callObj.reliesOnService && !collection[callObj.reliesOnService]) return regionCb(); + + if (callObj.reliesOnCall && + (!collection[callObj.reliesOnService] || + !collection[callObj.reliesOnService][callObj.reliesOnCall] || + !collection[callObj.reliesOnService][callObj.reliesOnCall][region] || + !collection[callObj.reliesOnService][callObj.reliesOnCall][region].data || + !collection[callObj.reliesOnService][callObj.reliesOnCall][region].data.length)) + return regionCb(); + + let LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); + + LocalAlibabaConfig['endpoint'] = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? + `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; + LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; + let client = new alicloud(LocalAlibabaConfig); + + async.eachLimit(collection[callObj.reliesOnService][callObj.reliesOnCall][region].data, 10, function(val, valCb) { + let resultKey = callObj.filterValue[0]; + collection[serviceLower][callKey][region][val[resultKey]] = {}; + + let params = {}; + if (callObj.params) params = JSON.parse(JSON.stringify(callObj.params)); + + for (let key in callObj.filterKey) { + params[callObj.filterKey[key]] = val[callObj.filterValue[key]]; + } + + params['RegionId'] = region; + + var requestCb = function(err, data) { + if (err) collection[serviceLower][callKey][region][val[resultKey]].err = err; + if (!data) return valCb(); + + collection[serviceLower][callKey][region][val[resultKey]].data = (callObj.resultFilter && data[callObj.resultFilter]) ? + data[callObj.resultFilter] : data; + + if (callObj.rateLimit) { + setTimeout(function() { + valCb(); + }, callObj.rateLimit); + } else { + valCb(); + } + }; + + var execute = function() { + client.request(callKey, params, requestOption).then((result) => { + requestCb(null, result); + }, (err) => { + requestCb(err); + }); + }; + + execute(); + }, function() { + if (callObj.rateLimit) { + setTimeout(function() { + regionCb(); + }, callObj.rateLimit); + } else { + regionCb(); + } + }); + }, function() { + callCb(); + }); + }, function() { + serviceCb(); + }); + }, function() { + postcallCb(); + }); + }, function() { + callback(null, collection); + }); + }); +}; + +module.exports = collect; diff --git a/collectors/alibaba/index.js b/collectors/alibaba/index.js new file mode 100644 index 0000000000..b5824d95cf --- /dev/null +++ b/collectors/alibaba/index.js @@ -0,0 +1,26 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var collectors = {}; + +var directories = fs.readdirSync(__dirname).filter(function(file) { + return fs.statSync(path.join(__dirname, file)).isDirectory(); +}); + +directories.forEach(function(directory) { + collectors[directory] = {}; + + fs + .readdirSync(__dirname + '/' + directory) + .filter(function(file) { + return (file.indexOf('.') !== 0); + }) + .forEach(function(file) { + var collector = require(path.join(__dirname + '/' + directory, file)); + var name = file.substring(0, file.indexOf('.js')); + collectors[directory][name] = collector; + }); +}); + +module.exports = collectors; \ No newline at end of file diff --git a/helpers/alibaba/functions.js b/helpers/alibaba/functions.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/helpers/alibaba/index.js b/helpers/alibaba/index.js new file mode 100644 index 0000000000..a45cd0aa9a --- /dev/null +++ b/helpers/alibaba/index.js @@ -0,0 +1,17 @@ +var shared = require(__dirname + '/../shared.js'); +var functions = require('./functions.js'); +var regRegions = require('./regions.js'); + +var regions = function() { + return regRegions; +}; + +var helpers = { + regions: regions, + MAX_REGIONS_AT_A_TIME: 6 +}; + +for (var s in shared) helpers[s] = shared[s]; +for (var f in functions) helpers[f] = functions[f]; + +module.exports = helpers; diff --git a/helpers/alibaba/regions.js b/helpers/alibaba/regions.js new file mode 100644 index 0000000000..14f95532b4 --- /dev/null +++ b/helpers/alibaba/regions.js @@ -0,0 +1,37 @@ +// Source: https://www.alibabacloud.com/global-locations + +var regions = [ + 'cn-hangzhou', // China (Hangzhou) + 'cn-shanghai', // China (Shanghai) + 'cn-qingdao', // China (Qingdao) + 'cn-beijing', // China (Beijing) + 'cn-zhangjiakou', // China (Zhangjiakou) + 'cn-huhehaote', // China (Hohhot) + 'cn-wulanchabu', // China (Ulanqab) + 'cn-shenzhen', // China (Shenzhen) + 'cn-heyuan', // China (Heyuan) + 'cn-chengdu', // China (Chengdu) + 'cn-hongkong', // China(Hong Kong) + 'cn-guangzhou', // China (Guangzhou) + 'ap-southeast-1', // Singapore + 'ap-southeast-2', // Australia (Sydney) + 'ap-southeast-3', // Malaysia (Kuala Lumpur) + 'ap-southeast-5', // Indonesia (Jakarta) + 'ap-northeast-1', // Japan (Tokyo) + 'ap-south-1', // India (Mumbai) + 'eu-central-1', // Germany (Frankfurt) + 'eu-west-1', // UK(London) + 'us-west-1', // US (Silicon Valley) + 'us-east-1', // US (Virginia) + 'me-east-1', // UAE (Dubai) +]; + +module.exports = { + default: ['cn-hangzhou'], + all: regions, + ecs: regions, + polardb: regions, + ram: ['cn-hangzhou'], + vpc: regions, + rds: regions, +}; diff --git a/index.js b/index.js index 7b5d564fad..b6a457049d 100755 --- a/index.js +++ b/index.js @@ -21,7 +21,8 @@ console.log(` const parser = new ArgumentParser({}); parser.add_argument('--config', { - help: 'The path to a CloudSploit config file containing cloud credentials. See config_example.js' + help: 'The path to a CloudSploit config file containing cloud credentials. See config_example.js. ' + + 'If not provided, logic will use default AWS credential chain and will also override provided cloud' }); parser.add_argument('--compliance', { @@ -69,14 +70,18 @@ parser.add_argument('--remediate', { help: 'Run remediation the provided plugin', action: 'append' }); +parser.add_argument('--cloud', { + help: 'The name of cloud to run plugins for. If not provided, logic will assume cloud from config.js file based on provided credetials', + choices: ['aws', 'azure', 'github', 'google', 'oracle'], + action: 'append' +}); let settings = parser.parse_args(); let cloudConfig = {}; -settings.cloud = 'aws'; - // Now execute the scans using the defined configuration information. if (!settings.config) { + settings.cloud = 'aws'; // AWS will handle the default credential chain without needing a credential file console.log('INFO: No config file provided, using default AWS credential chain.'); return engine(cloudConfig, settings); @@ -122,13 +127,15 @@ function checkRequiredKeys(obj, keys) { }); } -if (config.credentials.aws.credential_file) { +if (config.credentials.aws.credential_file && (!settings.cloud || (settings.cloud == 'aws'))) { + settings.cloud = 'aws'; cloudConfig = loadHelperFile(config.credentials.aws.credential_file); if (!cloudConfig || !cloudConfig.accessKeyId || !cloudConfig.secretAccessKey) { console.error('ERROR: AWS credential file does not have accessKeyId or secretAccessKey properties'); process.exit(1); } -} else if (config.credentials.aws.access_key) { +} else if (config.credentials.aws.access_key && (!settings.cloud || (settings.cloud == 'aws'))) { + settings.cloud = 'aws'; checkRequiredKeys(config.credentials.aws, ['secret_access_key']); cloudConfig = { accessKeyId: config.credentials.aws.access_key, @@ -136,7 +143,7 @@ if (config.credentials.aws.credential_file) { sessionToken: config.credentials.aws.session_token, region: 'us-east-1' }; -} else if (config.credentials.azure.credential_file) { +} else if (config.credentials.azure.credential_file && (!settings.cloud || (settings.cloud == 'azure'))) { settings.cloud = 'azure'; cloudConfig = loadHelperFile(config.credentials.azure.credential_file); if (!cloudConfig || !cloudConfig.ApplicationID || !cloudConfig.KeyValue || !cloudConfig.DirectoryID || !cloudConfig.SubscriptionID) { @@ -144,7 +151,7 @@ if (config.credentials.aws.credential_file) { process.exit(1); } cloudConfig.location = 'East US'; -} else if (config.credentials.azure.application_id) { +} else if (config.credentials.azure.application_id && (!settings.cloud || (settings.cloud == 'azure'))) { settings.cloud = 'azure'; checkRequiredKeys(config.credentials.azure, ['key_value', 'directory_id', 'subscription_id']); cloudConfig = { @@ -154,11 +161,11 @@ if (config.credentials.aws.credential_file) { SubscriptionID: config.credentials.azure.subscription_id, location: 'East US' }; -} else if (config.credentials.google.credential_file) { +} else if (config.credentials.google.credential_file && (!settings.cloud || (settings.cloud == 'google'))) { settings.cloud = 'google'; cloudConfig = loadHelperFile(config.credentials.google.credential_file); cloudConfig.project = cloudConfig.project_id; -} else if (config.credentials.google.project) { +} else if (config.credentials.google.project && (!settings.cloud || (settings.cloud == 'google'))) { settings.cloud = 'google'; checkRequiredKeys(config.credentials.google, ['client_email', 'private_key']); cloudConfig = { @@ -167,7 +174,7 @@ if (config.credentials.aws.credential_file) { client_email: config.credentials.google.client_email, private_key: config.credentials.google.private_key, }; -} else if (config.credentials.oracle.credential_file) { +} else if (config.credentials.oracle.credential_file && (!settings.cloud || (settings.cloud == 'oracle'))) { settings.cloud = 'oracle'; cloudConfig = loadHelperFile(config.credentials.oracle.credential_file); if (!cloudConfig || !cloudConfig.tenancyId || !cloudConfig.compartmentId || !cloudConfig.userId || !cloudConfig.keyValue || !cloudConfig.region) { @@ -176,7 +183,7 @@ if (config.credentials.aws.credential_file) { } cloudConfig.RESTversion = '/20160918'; -} else if (config.credentials.oracle.tenancy_id) { +} else if (config.credentials.oracle.tenancy_id && (!settings.cloud || (settings.cloud == 'oracle'))) { settings.cloud = 'oracle'; checkRequiredKeys(config.credentials.oracle, ['compartment_id', 'user_id', 'key_fingerprint', 'key_value']); cloudConfig = { @@ -188,10 +195,10 @@ if (config.credentials.aws.credential_file) { keyValue: config.credentials.oracle.key_value, region: config.credentials.oracle.region, }; -} else if (config.credentials.github.credential_file) { +} else if (config.credentials.github.credential_file && (!settings.cloud || (settings.cloud == 'github'))) { settings.cloud = 'github'; cloudConfig = loadHelperFile(config.credentials.github.credential_file); -} else if (config.credentials.github.token) { +} else if (config.credentials.github.token && (!settings.cloud || (settings.cloud == 'github'))) { settings.cloud = 'github'; checkRequiredKeys(config.credentials.github, ['url', 'login']); cloudConfig = { @@ -200,6 +207,16 @@ if (config.credentials.aws.credential_file) { organization: config.credentials.github.organization, login: config.credentials.github.login }; +} else if (config.credentials.alibaba.credential_file && (!settings.cloud || (settings.cloud == 'alibaba'))) { + settings.cloud = 'alibaba'; + cloudConfig = loadHelperFile(config.credentials.alibaba.credential_file); +} else if (config.credentials.alibaba.access_key_id && (!settings.cloud || (settings.cloud == 'alibaba'))) { + settings.cloud = 'alibaba'; + checkRequiredKeys(config.credentials.alibaba, ['access_key_secret']); + cloudConfig = { + accessKeyId: config.credentials.alibaba.access_key_id, + accessKeySecret: config.credentials.alibaba.access_key_secret + }; } else { console.error('ERROR: Config file does not contain any valid credential configs.'); process.exit(1); From b954f6a46011731b3d05aa677f214f54e3cd2ba5 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Mon, 17 May 2021 21:55:58 +0500 Subject: [PATCH 03/11] Fixed undefined resource issue (#674) --- plugins/aws/es/esExposedDomain.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/aws/es/esExposedDomain.js b/plugins/aws/es/esExposedDomain.js index df85f8c883..8085e078fe 100644 --- a/plugins/aws/es/esExposedDomain.js +++ b/plugins/aws/es/esExposedDomain.js @@ -8,13 +8,18 @@ module.exports = { more_info: 'ElasticSearch domains should not be publicly exposed to all AWS accounts.', link: 'https://aws.amazon.com/blogs/database/set-access-control-for-amazon-elasticsearch-service/', recommended_action: 'Update elasticsearch domain to set access control.', - apis: ['ES:listDomainNames', 'ES:describeElasticsearchDomain'], + apis: ['ES:listDomainNames', 'ES:describeElasticsearchDomain', 'STS:getCallerIdentity'], run: function(cache, settings, callback) { var results = []; var source = {}; var regions = helpers.regions(settings); + var acctRegion = helpers.defaultRegion(settings); + var accountId = helpers.addSource(cache, source, + ['sts', 'getCallerIdentity', acctRegion, 'data']); + var awsOrGov = helpers.defaultPartition(settings); + async.each(regions.es, function(region, rcb) { var listDomainNames = helpers.addSource(cache, source, ['es', 'listDomainNames', region]); @@ -37,7 +42,7 @@ module.exports = { var describeElasticsearchDomain = helpers.addSource(cache, source, ['es', 'describeElasticsearchDomain', region, domain.DomainName]); - var resource = domain.ARN; + var resource = `arn:${awsOrGov}:es:${region}:${accountId}:domain/${domain.DomainName}`; if (!describeElasticsearchDomain || describeElasticsearchDomain.err || From eafec7bbbefb3d2b3702d675db8ca2f2e862a51c Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 May 2021 18:34:45 +0500 Subject: [PATCH 04/11] feature/AKD-191: Added Alibaba RAM Users MFA Enabled plugin and test cases (#684) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector * Added Alibaba RAM Users MFA Enabled plugin and test cases * Modified collector * fix Co-authored-by: Gio Rodriguez --- collectors/alibaba/collector.js | 74 +++++++------- exports.js | 3 + helpers/alibaba/functions.js | 14 +++ helpers/alibaba/regions.js | 1 + index.js | 1 + plugins/alibaba/ram/usersMfaEnabled.js | 67 +++++++++++++ plugins/alibaba/ram/usersMfaEnabled.spec.js | 103 ++++++++++++++++++++ 7 files changed, 230 insertions(+), 33 deletions(-) mode change 100755 => 100644 index.js create mode 100644 plugins/alibaba/ram/usersMfaEnabled.js create mode 100644 plugins/alibaba/ram/usersMfaEnabled.spec.js diff --git a/collectors/alibaba/collector.js b/collectors/alibaba/collector.js index d2454340c1..996111fe6e 100644 --- a/collectors/alibaba/collector.js +++ b/collectors/alibaba/collector.js @@ -20,9 +20,7 @@ var alicloud = require('@alicloud/pop-core'); var async = require('async'); var helpers = require(__dirname + '/../../helpers/alibaba'); -var regions = helpers.regions(); - -var regionEndpointMap = {}; +var apiVersion = '2015-05-01'; var globalServices = [ 'RAM' @@ -41,13 +39,11 @@ var calls = { ListPolicies: { property: 'Policies', subProperty: 'Policy', - apiVersion: '2015-05-01', paginate: 'Marker' }, ListUsers: { property: 'Users', subProperty: 'User', - apiVersion: '2015-05-01', paginate: 'Marker' } }, @@ -55,7 +51,6 @@ var calls = { DescribeDBInstances: { property: 'Items', subProperty: 'DBInstance', - apiVersion: '2015-05-01', paginate: 'Pages' } }, @@ -88,6 +83,12 @@ var calls = { apiVersion: '2017-08-01', paginate: 'Pages' } + }, + STS: { + GetCallerIdentity: { + property: 'AccountId', + apiVersion: '2015-04-01' + } } }; @@ -99,6 +100,7 @@ var postcalls = [ reliesOnCall: 'DescribeInstances', filterKey: ['InstanceId'], filterValue: ['InstanceId'], + resultKey: 'InstanceId', apiVersion: '2014-05-26' } }, @@ -108,16 +110,23 @@ var postcalls = [ reliesOnCall: 'ListPolicies', filterKey: ['PolicyName', 'PolicyType'], filterValue: ['PolicyName', 'PolicyType'], - resultFilter: 'DefaultPolicyVersion', - apiVersion: '2015-05-01' + resultKey: 'PolicyName', + resultFilter: 'DefaultPolicyVersion' }, GetUser: { reliesOnService: 'ram', reliesOnCall: 'ListUsers', filterKey: ['UserName'], filterValue: ['UserName'], - resultFilter: 'User', - apiVersion: '2015-05-01' + resultKey: 'UserName', + resultFilter: 'User' + }, + GetUserMFAInfo: { + reliesOnService: 'ram', + reliesOnCall: 'ListUsers', + filterKey: ['UserName'], + filterValue: ['UserName'], + resultKey: 'UserName' } } } @@ -128,18 +137,20 @@ var collect = function(AlibabaConfig, settings, callback) { return callback(null, calls, postcalls); } + var regions = helpers.regions(settings); + var collection = {}; async.eachOfLimit(calls, 10, function(call, service, serviceCb) { - let serviceLower = service.toLowerCase(); + var serviceLower = service.toLowerCase(); if (!collection[serviceLower]) collection[serviceLower] = {}; async.eachOfLimit(call, 15, function(callObj, callKey, callCb) { if (settings.api_calls && settings.api_calls.indexOf(service + ':' + callKey) === -1) return callCb(); if (!collection[serviceLower][callKey]) collection[serviceLower][callKey] = {}; - let callRegions = regions[serviceLower]; - let requestOption = { method: callObj.method || 'POST' }; + var callRegions = regions[serviceLower]; + var requestOption = { method: callObj.method || 'POST' }; async.eachLimit(callRegions, helpers.MAX_REGIONS_AT_A_TIME, function(region, regionCb) { if (settings.skip_regions && @@ -147,15 +158,14 @@ var collect = function(AlibabaConfig, settings, callback) { globalServices.indexOf(service) === -1) return regionCb(); if (!collection[serviceLower][callKey][region]) collection[serviceLower][callKey][region] = {}; - let LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); + var LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); - let endpoint = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? - `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; + var endpoint = `https://${serviceLower}.aliyuncs.com`; LocalAlibabaConfig['endpoint'] = endpoint; - LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; - let client = new alicloud(LocalAlibabaConfig); - let paginating = false; - let pageNumber = 1; + LocalAlibabaConfig['apiVersion'] = callObj.apiVersion || apiVersion; + var client = new alicloud(LocalAlibabaConfig); + var paginating = false; + var pageNumber = 1; var clientCb = function(err, data) { if (err) collection[serviceLower][callKey][region].err = err; if (!data) return regionCb(); @@ -200,10 +210,10 @@ var collect = function(AlibabaConfig, settings, callback) { } }; - function execute(nextToken, paginateParams) { - let localParams = JSON.parse(JSON.stringify(callObj.params || {})); + function execute(nextTokens, paginateParams) { + var localParams = JSON.parse(JSON.stringify(callObj.params || {})); localParams['RegionId'] = region; - if (nextToken) localParams[nextToken[0]] = nextToken[1]; + if (nextTokens) localParams[nextTokens[0]] = nextTokens[1]; else if (paginateParams) localParams = {...localParams, ...paginateParams}; client.request(callKey, localParams, requestOption).then((result) => { @@ -247,18 +257,16 @@ var collect = function(AlibabaConfig, settings, callback) { !collection[callObj.reliesOnService][callObj.reliesOnCall][region].data.length)) return regionCb(); - let LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); + var LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); - LocalAlibabaConfig['endpoint'] = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? - `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; - LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; - let client = new alicloud(LocalAlibabaConfig); + LocalAlibabaConfig['endpoint'] = `https://${serviceLower}.aliyuncs.com`; + LocalAlibabaConfig['apiVersion'] = callObj.apiVersion || apiVersion; + var client = new alicloud(LocalAlibabaConfig); async.eachLimit(collection[callObj.reliesOnService][callObj.reliesOnCall][region].data, 10, function(val, valCb) { - let resultKey = callObj.filterValue[0]; - collection[serviceLower][callKey][region][val[resultKey]] = {}; + collection[serviceLower][callKey][region][val[callObj.resultKey]] = {}; - let params = {}; + var params = {}; if (callObj.params) params = JSON.parse(JSON.stringify(callObj.params)); for (let key in callObj.filterKey) { @@ -268,10 +276,10 @@ var collect = function(AlibabaConfig, settings, callback) { params['RegionId'] = region; var requestCb = function(err, data) { - if (err) collection[serviceLower][callKey][region][val[resultKey]].err = err; + if (err) collection[serviceLower][callKey][region][val[callObj.resultKey]].err = err; if (!data) return valCb(); - collection[serviceLower][callKey][region][val[resultKey]].data = (callObj.resultFilter && data[callObj.resultFilter]) ? + collection[serviceLower][callKey][region][val[callObj.resultKey]].data = (callObj.resultFilter && data[callObj.resultFilter]) ? data[callObj.resultFilter] : data; if (callObj.rateLimit) { diff --git a/exports.js b/exports.js index 5f7e16e77d..5e931643ad 100644 --- a/exports.js +++ b/exports.js @@ -725,5 +725,8 @@ module.exports = { 'vpcNetworkRouteLogging' : require(__dirname + '/plugins/google/logging/vpcNetworkRouteLogging.js'), 'vpcNetworkLogging' : require(__dirname + '/plugins/google/logging/vpcNetworkLogging.js'), 'logSinksEnabled' : require(__dirname + '/plugins/google/logging/logSinksEnabled.js'), + }, + alibaba: { + 'usersMfaEnabled' : require(__dirname + '/plugins/alibaba/ram/usersMfaEnabled.js') } }; diff --git a/helpers/alibaba/functions.js b/helpers/alibaba/functions.js index e69de29bb2..96e06bacfe 100644 --- a/helpers/alibaba/functions.js +++ b/helpers/alibaba/functions.js @@ -0,0 +1,14 @@ +function defaultRegion(settings) { + if (settings.defaultRegion) return settings.defaultRegion; + return 'cn-hangzhou'; +} + +function createArn(service, account, resourceType, resourceId, region) { + if (!region) region = ''; + return `arn:acs:${service}:${region}:${account}:${resourceType}/${resourceId}`; +} + +module.exports = { + defaultRegion: defaultRegion, + createArn: createArn +}; \ No newline at end of file diff --git a/helpers/alibaba/regions.js b/helpers/alibaba/regions.js index 14f95532b4..1583ca018c 100644 --- a/helpers/alibaba/regions.js +++ b/helpers/alibaba/regions.js @@ -34,4 +34,5 @@ module.exports = { ram: ['cn-hangzhou'], vpc: regions, rds: regions, + sts: ['cn-hangzhou'] }; diff --git a/index.js b/index.js old mode 100755 new mode 100644 index b6a457049d..f80c8f4f17 --- a/index.js +++ b/index.js @@ -259,5 +259,6 @@ if (settings.remediate && settings.remediate.length) { process.exit(1); } } + // Now execute the scans using the defined configuration information. engine(cloudConfig, settings); diff --git a/plugins/alibaba/ram/usersMfaEnabled.js b/plugins/alibaba/ram/usersMfaEnabled.js new file mode 100644 index 0000000000..30a4380224 --- /dev/null +++ b/plugins/alibaba/ram/usersMfaEnabled.js @@ -0,0 +1,67 @@ +var helpers = require('../../../helpers/alibaba'); + +module.exports = { + title: 'Users MFA Enabled', + category: 'RAM', + description: 'Ensures a multi-factor authentication device is enabled for all RAM users within the account', + more_info: 'RAM User should have an MFA device setup to enable two-factor authentication.', + link: 'https://partners-intl.aliyun.com/help/doc-detail/119555.htm', + recommended_action: 'Enable an MFA device for the RAM users', + apis: ['RAM:ListUsers', 'RAM:GetUserMFAInfo', 'STS:GetCallerIdentity'], + compliance: { + hipaa: 'MFA helps provide additional assurance that the user accessing ' + + 'the Alibaba environment has been identified. HIPAA requires ' + + 'strong controls around entity authentication which can be ' + + 'enhanced through the use of MFA.', + pci: 'PCI requires MFA for all access to cardholder environments. ' + + 'Create an MFA key for RAM users.', + cis: '1.4 Ensure that multi-factor authentication is enabled for all RAM users that have a console password' + }, + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + + var region = helpers.defaultRegion(settings); + + var accountId = helpers.addSource(cache, source, ['sts', 'GetCallerIdentity', region, 'data']); + + var listUsers = helpers.addSource(cache, source, + ['ram', 'ListUsers', region]); + + if (!listUsers) return callback(null, results, source); + + if (listUsers.err || !listUsers.data) { + helpers.addResult(results, 3, + 'Unable to query RAM users' + helpers.addError(listUsers), region); + return callback(null, results, source); + } + + if (!listUsers.data.length) { + helpers.addResult(results, 0, 'No RAM users found', region); + return callback(null, results, source); + } + + for (var user of listUsers.data) { + if (!user.UserName) continue; + + var getUserMfa = helpers.addSource(cache, source, + ['ram', 'GetUserMFAInfo', region, user.UserName]); + + var resource = helpers.createArn('ram', accountId, 'user', user.UserName); + + if (getUserMfa && getUserMfa.err && getUserMfa.err.code && getUserMfa.err.code === 'EntityNotExist.User.MFADevice') { + helpers.addResult(results, 2, + 'RAM user does not have MFA device configured', region, resource); + } else if (!getUserMfa || getUserMfa.err || !getUserMfa.data) { + helpers.addResult(results, 3, + 'Unable to query RAM user MFA info', region, resource); + } else { + helpers.addResult(results, 0, + 'RAM user has MFA device configured', region, resource); + } + } + + callback(null, results, source); + } +}; \ No newline at end of file diff --git a/plugins/alibaba/ram/usersMfaEnabled.spec.js b/plugins/alibaba/ram/usersMfaEnabled.spec.js new file mode 100644 index 0000000000..99479309f8 --- /dev/null +++ b/plugins/alibaba/ram/usersMfaEnabled.spec.js @@ -0,0 +1,103 @@ +var expect = require('chai').expect; +var usersMfaEnabled = require('./usersMfaEnabled') + +const listUsers = [ + { + "UpdateDate": "2021-05-04T12:03:49Z", + "UserName": "aqua", + "Comments": "", + "UserId": "254529020129829608", + "DisplayName": "aqua", + "CreateDate": "2021-05-04T12:03:49Z" + }, + { + "UpdateDate": "2021-05-04T09:54:39Z", + "UserName": "cloudsploit", + "Comments": "", + "UserId": "283806919721151694", + "DisplayName": "cloudsploit", + "CreateDate": "2021-04-29T18:32:31Z" + } +]; + +const getUserMfa = [ + { + "MFADevice": { + "Type": "VMFA", + "SerialNumber": "acs:ram::5103119194921620:mfa/aqua" + } + }, + { + code: "EntityNotExist.User.MFADevice" + } +]; + +const createCache = (users, userMfaInfo, usersErr, userMfaInfoErr) => { + let userName = (users && users.length) ? users[0].UserName : null; + return { + ram: { + ListUsers: { + 'cn-hangzhou': { + data: users, + err: usersErr + } + }, + GetUserMFAInfo: { + 'cn-hangzhou': { + [userName]: { + data: userMfaInfo, + err: userMfaInfoErr + } + } + } + } + } +} + +describe('usersMfaEnabled', function () { + describe('run', function () { + it('should FAIL if RAM user does not have MFA device configured', function (done) { + const cache = createCache([listUsers[1]], null, null, getUserMfa[1]); + usersMfaEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('RAM user does not have MFA device configured'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if RAM user has MFA device configured', function (done) { + const cache = createCache([listUsers[0]], getUserMfa[0]); + usersMfaEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('RAM user has MFA device configured'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if No RAM users found', function (done) { + const cache = createCache([]); + usersMfaEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No RAM users found'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if Unable to query RAM users', function (done) { + const cache = createCache([], null, { err: 'Unable to query RAM users' }); + usersMfaEnabled.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 RAM users'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + }) +}) From 8bbd96903ecaf31076cdb70b289285b7a6d27b09 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 May 2021 22:51:14 +0500 Subject: [PATCH 05/11] feature/AKD-195: Added Alibaba RAM Password Requires Uppercase plugin and test cases (#685) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector * Added Alibaba RAM Users MFA Enabled plugin and test cases * Modified collector * Added Alibaba RAM Password Requires Uppercase plugin and test cases * fix Co-authored-by: Gio Rodriguez Co-authored-by: Gio Rodriguez --- collectors/alibaba/collector.js | 4 +- exports.js | 1 + .../alibaba/ram/passwordRequiresUppercase.js | 41 ++++++++++ .../ram/passwordRequiresUppercase.spec.js | 77 +++++++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 plugins/alibaba/ram/passwordRequiresUppercase.js create mode 100644 plugins/alibaba/ram/passwordRequiresUppercase.spec.js diff --git a/collectors/alibaba/collector.js b/collectors/alibaba/collector.js index 996111fe6e..7109a4221a 100644 --- a/collectors/alibaba/collector.js +++ b/collectors/alibaba/collector.js @@ -21,7 +21,6 @@ var async = require('async'); var helpers = require(__dirname + '/../../helpers/alibaba'); var apiVersion = '2015-05-01'; - var globalServices = [ 'RAM' ]; @@ -45,6 +44,9 @@ var calls = { property: 'Users', subProperty: 'User', paginate: 'Marker' + }, + GetPasswordPolicy: { + property: 'PasswordPolicy', } }, GBDB: { diff --git a/exports.js b/exports.js index 5e931643ad..b6f54a90aa 100644 --- a/exports.js +++ b/exports.js @@ -727,6 +727,7 @@ module.exports = { 'logSinksEnabled' : require(__dirname + '/plugins/google/logging/logSinksEnabled.js'), }, alibaba: { + 'passwordRequiresUppercase' : require(__dirname + '/plugins/alibaba/ram/passwordRequiresUppercase.js'), 'usersMfaEnabled' : require(__dirname + '/plugins/alibaba/ram/usersMfaEnabled.js') } }; diff --git a/plugins/alibaba/ram/passwordRequiresUppercase.js b/plugins/alibaba/ram/passwordRequiresUppercase.js new file mode 100644 index 0000000000..ed0f130892 --- /dev/null +++ b/plugins/alibaba/ram/passwordRequiresUppercase.js @@ -0,0 +1,41 @@ +var helpers = require('../../../helpers/alibaba'); + +module.exports = { + title: 'Password Requires Uppercase', + category: 'RAM', + description: 'Ensure that RAM password security settings require at least one uppercase character.', + more_info: 'A strong password policy enforces minimum length, expiration, reuse, and symbol usage.', + link: 'https://www.alibabacloud.com/help/doc-detail/116413.htm', + recommended_action: 'Update the password security settings to require the use of uppercase characters.', + apis: ['RAM:GetPasswordPolicy'], + compliance: { + pci: 'PCI requires a strong password policy. Setting Identity password ' + + 'requirements enforces this policy.' + }, + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var region = helpers.defaultRegion(settings); + + var getPasswordPolicy = helpers.addSource(cache, source, + ['ram', 'GetPasswordPolicy', region]); + + if (!getPasswordPolicy) return callback(null, results, source); + + if (getPasswordPolicy.err || !getPasswordPolicy.data || !Object.keys(getPasswordPolicy.data).length) { + helpers.addResult(results, 3, + 'Unable to query RAM password policy: ' + helpers.addError(getPasswordPolicy), region); + return callback(null, results, source); + } + + if (getPasswordPolicy.data.RequireUppercaseCharacters) { + helpers.addResult(results, 0, + 'RAM password security policy requires uppercase characters', region); + } else { + helpers.addResult(results, 2, + 'RAM password security policy does not require uppercase characters', region); + } + + callback(null, results, source); + } +}; \ No newline at end of file diff --git a/plugins/alibaba/ram/passwordRequiresUppercase.spec.js b/plugins/alibaba/ram/passwordRequiresUppercase.spec.js new file mode 100644 index 0000000000..c3645b5d3d --- /dev/null +++ b/plugins/alibaba/ram/passwordRequiresUppercase.spec.js @@ -0,0 +1,77 @@ +var expect = require('chai').expect; +var passwordRequiresUppercase = require('./passwordRequiresUppercase') + +const getPasswordPolicy = [ + { + MinimumPasswordLength:8, + RequireLowercaseCharacters:false, + RequireNumbers:false, + MaxLoginAttemps:0, + MaxPasswordAge:0, + PasswordReusePrevention:0, + HardExpiry:false, + RequireUppercaseCharacters:false, + RequireSymbols:false + }, + { + MinimumPasswordLength:8, + RequireLowercaseCharacters:false, + RequireNumbers:false, + MaxLoginAttemps:0, + MaxPasswordAge:0, + PasswordReusePrevention:0, + HardExpiry:false, + RequireUppercaseCharacters:true, + RequireSymbols:false + } +]; + +const createCache = (data, err) => { + return { + ram: { + GetPasswordPolicy: { + 'cn-hangzhou': { + data: data, + err: err + } + } + } + } +} + +describe('passwordRequiresUppercase', function () { + describe('run', function () { + it('should FAIL if RAM password security policy does not require uppercase characters', function (done) { + const cache = createCache(getPasswordPolicy[0]); + passwordRequiresUppercase.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('RAM password security policy does not require uppercase characters'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if RAM password security policy requires uppercase characters', function (done) { + const cache = createCache(getPasswordPolicy[1]); + passwordRequiresUppercase.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('RAM password security policy requires uppercase characters'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query RAM password policy', function (done) { + const cache = createCache({}); + passwordRequiresUppercase.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 RAM password policy'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + }) +}) \ No newline at end of file From 766e7d6534d5f8e06e221302af90a64d60f42a47 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 May 2021 22:58:25 +0500 Subject: [PATCH 06/11] feature/AKD-215: Added Alibaba RDS SSL Encryption Enabled plugin and test cases (#686) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector * Added Alibaba RAM Users MFA Enabled plugin and test cases * Modified collector * Added Alibaba RDS SSL Encryption Enabled plugin and test cases Co-authored-by: Gio Rodriguez --- collectors/alibaba/collector.js | 10 ++ exports.js | 1 + .../alibaba/rds/rdsSslEncryptionEnabled.js | 78 +++++++++++ .../rds/rdsSslEncryptionEnabled.spec.js | 128 ++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 plugins/alibaba/rds/rdsSslEncryptionEnabled.js create mode 100644 plugins/alibaba/rds/rdsSslEncryptionEnabled.spec.js diff --git a/collectors/alibaba/collector.js b/collectors/alibaba/collector.js index 7109a4221a..585081dbba 100644 --- a/collectors/alibaba/collector.js +++ b/collectors/alibaba/collector.js @@ -130,6 +130,16 @@ var postcalls = [ filterValue: ['UserName'], resultKey: 'UserName' } + }, + RDS: { + DescribeDBInstanceSSL: { + reliesOnService: 'rds', + reliesOnCall: 'DescribeDBInstances', + filterKey: ['DBInstanceId'], + filterValue: ['DBInstanceId'], + resultKey: 'DBInstanceId', + apiVersion: '2014-08-15' + } } } ]; diff --git a/exports.js b/exports.js index b6f54a90aa..9d23ffee0d 100644 --- a/exports.js +++ b/exports.js @@ -727,6 +727,7 @@ module.exports = { 'logSinksEnabled' : require(__dirname + '/plugins/google/logging/logSinksEnabled.js'), }, alibaba: { + 'rdsSslEncryptionEnabled' : require(__dirname + '/plugins/alibaba/rds/rdsSslEncryptionEnabled.js'), 'passwordRequiresUppercase' : require(__dirname + '/plugins/alibaba/ram/passwordRequiresUppercase.js'), 'usersMfaEnabled' : require(__dirname + '/plugins/alibaba/ram/usersMfaEnabled.js') } diff --git a/plugins/alibaba/rds/rdsSslEncryptionEnabled.js b/plugins/alibaba/rds/rdsSslEncryptionEnabled.js new file mode 100644 index 0000000000..8666003c63 --- /dev/null +++ b/plugins/alibaba/rds/rdsSslEncryptionEnabled.js @@ -0,0 +1,78 @@ +var async = require('async'); +var helpers = require('../../../helpers/alibaba'); + +module.exports = { + title: 'RDS SSL Encryption Enabled', + category: 'RDS', + description: 'Ensure that RDS instances enforce all incoming connections to use SSL.', + more_info: 'To enhance link security, you should enable Secure Sockets Layer (SSL) encryption for RDS instances. ' + + 'SSL is used on the transport layer to encrypt network connections. SSL not only increases the security and integrity of communication data, but also increases the response time for network connection.', + link: 'https://partners-intl.aliyun.com/help/doc-detail/32474.htm', + recommended_action: 'Enable SSL ecnryption for RDS instances', + apis: ['RDS:DescribeDBInstances', 'RDS:DescribeDBInstanceSSL', 'STS:GetCallerIdentity'], + compliance: { + hipaa: 'HIPAA requires all data to be transmitted over secure channels. ' + + 'RDS SSL connection should be used to ensure internal ' + + 'services are always connecting over a secure channel.', + }, + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(); + var defaultRegion = helpers.defaultRegion(settings); + + var accountId = helpers.addSource(cache, source, ['sts', 'GetCallerIdentity', defaultRegion, 'data']); + + async.each(regions.rds, function(region, rcb) { + var describeDBInstances = helpers.addSource(cache, source, + ['rds', 'DescribeDBInstances', region]); + + if (!describeDBInstances) { + return rcb(); + } + + if (describeDBInstances.err || !describeDBInstances.data) { + helpers.addResult(results, 3, + `Unable to query RDS DB instances: ${helpers.addError(describeDBInstances)}`, + region); + return rcb(); + } + + if (!describeDBInstances.data.length) { + helpers.addResult(results, 0, 'No RDS DB instances found', region); + return rcb(); + } + + async.each(describeDBInstances.data, function(instance, cb){ + if (!instance.DBInstanceId) return cb(); + + var instanceSslInfo = helpers.addSource(cache, source, + ['rds', 'DescribeDBInstanceSSL', region, instance.DBInstanceId]); + + var resource = helpers.createArn('rds', accountId, 'instance', instance.DBInstanceId, region); + + if (!instanceSslInfo || instanceSslInfo.err || !instanceSslInfo.data) { + helpers.addResult(results, 3, + `Unable to query RDS instance SSL info: ${helpers.addError(instanceSslInfo)}`, + region, resource); + return cb(); + } + + if (instanceSslInfo.data.RequireUpdate && instanceSslInfo.data.RequireUpdate.toLowerCase() == 'yes') { + helpers.addResult(results, 0, + 'RDS instance has SSL encryption enabled', region, resource); + } else { + helpers.addResult(results, 2, + 'RDS instance does not have SSL encryption enabled', region, resource); + } + + cb(); + }, function(){ + rcb(); + }); + }, function(){ + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/alibaba/rds/rdsSslEncryptionEnabled.spec.js b/plugins/alibaba/rds/rdsSslEncryptionEnabled.spec.js new file mode 100644 index 0000000000..d978d1cf9d --- /dev/null +++ b/plugins/alibaba/rds/rdsSslEncryptionEnabled.spec.js @@ -0,0 +1,128 @@ +var expect = require('chai').expect; +var rdsSslEncryptionEnabled = require('./rdsSslEncryptionEnabled.js'); + +const describeDBInstances = [ + { + "EngineVersion": "13.0", + "DBInstanceStatus": "Running", + "ResourceGroupId": "rg-aekzsj44b4lt5fa", + "DBInstanceNetType": "Intranet", + "DBInstanceClass": "pg.n2.small.2c", + "CreateTime": "2021-05-04T17:13:45Z", + "VSwitchId": "vsw-rj94uhhrj5qz5008lwi1x", + "DBInstanceType": "Primary", + "PayType": "Postpaid", + "LockMode": "Unlock", + "MutriORsignle": false, + "InstanceNetworkType": "VPC", + "InsId": 1, + "VpcId": "vpc-rj9vu86hdve3qr173ew17", + "DBInstanceId": "pgm-2ev213kfnogf7mfi", + "ConnectionMode": "Standard", + "ReadOnlyDBInstanceIds": { + "ReadOnlyDBInstanceId": [] + }, + "VpcCloudInstanceId": "pgm-2ev213kfnogf7mfi", + "ExpireTime": "", + "LockReason": "", + "Engine": "PostgreSQL" + } +]; + +const describeDBInstanceSSL = [ + { + "SSLExpireTime": "", + "RequestId": "B61DFDF9-627C-41BD-81C6-5DF77D2A63ED", + "RequireUpdateReason": "", + "ConnectionString": "", + "RequireUpdate": "Yes" + }, + { + "SSLExpireTime": "", + "RequestId": "B61DFDF9-627C-41BD-81C6-5DF77D2A63ED", + "RequireUpdateReason": "", + "ConnectionString": "", + "RequireUpdate": "No" + } +]; + +const createCache = (dbInstances, dbSslData, dbInstancesErr, dbSslErr) => { + let instanceId = (dbInstances && dbInstances.length) ? dbInstances[0].DBInstanceId : null; + return { + rds: { + DescribeDBInstances: { + 'cn-hangzhou': { + data: dbInstances, + err: dbInstancesErr + }, + }, + DescribeDBInstanceSSL: { + 'cn-hangzhou': { + [instanceId]: { + data: dbSslData, + err: dbSslErr + } + } + } + }, + }; +}; + +describe('rdsSslEncryptionEnabled', function () { + describe('run', function () { + it('should FAIL if RDS instance does not have SSL encryption enabled', function (done) { + const cache = createCache([describeDBInstances[0]], describeDBInstanceSSL[1]); + rdsSslEncryptionEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('RDS instance does not have SSL encryption enabled'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if RDS instance has SSL encryption enabled', function (done) { + const cache = createCache([describeDBInstances[0]], describeDBInstanceSSL[0]); + rdsSslEncryptionEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('RDS instance has SSL encryption enabled'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if no RDS DB instances found', function (done) { + const cache = createCache([]); + rdsSslEncryptionEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No RDS DB instances found'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query RDS DB instances', function (done) { + const cache = createCache([], null, { err: 'Unable to query RDS DB instances' }); + rdsSslEncryptionEnabled.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 RDS DB instances'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query RDS instance SSL info', function (done) { + const cache = createCache([describeDBInstances[0]], {}, null, { err: 'Unable to query RDS instance SSL info' }); + rdsSslEncryptionEnabled.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 RDS instance SSL info'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + }) +}) \ No newline at end of file From 47ec522ca5c7ea9c4e09a046dd3834e630dacd40 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 May 2021 23:08:33 +0500 Subject: [PATCH 07/11] feature/AKD-208: Added ECS Data Disks Encyrpted plugin and test cases (#688) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector * Added Alibaba RAM Users MFA Enabled plugin and test cases * Modified collector * Added ECS Data Disks Encyrpted plugin and test cases Co-authored-by: Gio Rodriguez --- collectors/alibaba/collector.js | 91 +++-- exports.js | 1 + helpers/alibaba/regions.js | 3 +- plugins/alibaba/ecs/dataDisksEncrypted.js | 161 +++++++++ .../alibaba/ecs/dataDisksEncrypted.spec.js | 327 ++++++++++++++++++ 5 files changed, 556 insertions(+), 27 deletions(-) create mode 100644 plugins/alibaba/ecs/dataDisksEncrypted.js create mode 100644 plugins/alibaba/ecs/dataDisksEncrypted.spec.js diff --git a/collectors/alibaba/collector.js b/collectors/alibaba/collector.js index 585081dbba..1f8bbd04dd 100644 --- a/collectors/alibaba/collector.js +++ b/collectors/alibaba/collector.js @@ -20,9 +20,17 @@ var alicloud = require('@alicloud/pop-core'); var async = require('async'); var helpers = require(__dirname + '/../../helpers/alibaba'); -var apiVersion = '2015-05-01'; +var regions = helpers.regions(); + +var regionEndpointMap = { + ecs: ['cn-wulanchabu', 'cn-zhangjiak', 'cn-huhehaote', 'cn-heyuan', 'cn-chengdu', 'ap-southeast-2', 'cn-guangzhou', + 'ap-southeast-3', 'ap-southeast-5', 'ap-northeast-1', 'ap-south-1', 'eu-central-1', 'eu-west-1', 'me-east-1'], + kms: regions['kms'] +}; + var globalServices = [ - 'RAM' + 'RAM', + 'OSS' ]; var calls = { @@ -32,17 +40,25 @@ var calls = { subProperty: 'Instance', paginate: 'NextToken', apiVersion: '2014-05-26' + }, + DescribeDisks: { + property: 'Disks', + subProperty: 'Disk', + paginate: 'NextToken', + apiVersion: '2014-05-26' } }, RAM: { ListPolicies: { property: 'Policies', subProperty: 'Policy', + apiVersion: '2015-05-01', paginate: 'Marker' }, ListUsers: { property: 'Users', subProperty: 'User', + apiVersion: '2015-05-01', paginate: 'Marker' }, GetPasswordPolicy: { @@ -53,6 +69,7 @@ var calls = { DescribeDBInstances: { property: 'Items', subProperty: 'DBInstance', + apiVersion: '2015-05-01', paginate: 'Pages' } }, @@ -91,6 +108,15 @@ var calls = { property: 'AccountId', apiVersion: '2015-04-01' } + }, + KMS: { + ListKeys: { + property: 'Keys', + subProperty: 'Key', + apiVersion: '2016-01-20', + paginate: 'Pages', + regionalEndpoint: true + } } }; @@ -112,6 +138,7 @@ var postcalls = [ reliesOnCall: 'ListPolicies', filterKey: ['PolicyName', 'PolicyType'], filterValue: ['PolicyName', 'PolicyType'], + apiVersion: '2015-05-01', resultKey: 'PolicyName', resultFilter: 'DefaultPolicyVersion' }, @@ -120,8 +147,19 @@ var postcalls = [ reliesOnCall: 'ListUsers', filterKey: ['UserName'], filterValue: ['UserName'], + resultFilter: 'User', + apiVersion: '2015-05-01' + } + }, + KMS: { + DescribeKey: { + reliesOnService: 'kms', + reliesOnCall: 'ListKeys', + filterKey: ['KeyId'], + filterValue: ['KeyId'], + resultFilter: 'KeyMetadata', + apiVersion: '2016-01-20', resultKey: 'UserName', - resultFilter: 'User' }, GetUserMFAInfo: { reliesOnService: 'ram', @@ -149,20 +187,18 @@ var collect = function(AlibabaConfig, settings, callback) { return callback(null, calls, postcalls); } - var regions = helpers.regions(settings); - var collection = {}; async.eachOfLimit(calls, 10, function(call, service, serviceCb) { - var serviceLower = service.toLowerCase(); + let serviceLower = service.toLowerCase(); if (!collection[serviceLower]) collection[serviceLower] = {}; async.eachOfLimit(call, 15, function(callObj, callKey, callCb) { if (settings.api_calls && settings.api_calls.indexOf(service + ':' + callKey) === -1) return callCb(); if (!collection[serviceLower][callKey]) collection[serviceLower][callKey] = {}; - var callRegions = regions[serviceLower]; - var requestOption = { method: callObj.method || 'POST' }; + let callRegions = regions[serviceLower]; + let requestOption = { method: callObj.method || 'POST' }; async.eachLimit(callRegions, helpers.MAX_REGIONS_AT_A_TIME, function(region, regionCb) { if (settings.skip_regions && @@ -170,14 +206,15 @@ var collect = function(AlibabaConfig, settings, callback) { globalServices.indexOf(service) === -1) return regionCb(); if (!collection[serviceLower][callKey][region]) collection[serviceLower][callKey][region] = {}; - var LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); + let LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); - var endpoint = `https://${serviceLower}.aliyuncs.com`; + let endpoint = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? + `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; LocalAlibabaConfig['endpoint'] = endpoint; - LocalAlibabaConfig['apiVersion'] = callObj.apiVersion || apiVersion; - var client = new alicloud(LocalAlibabaConfig); - var paginating = false; - var pageNumber = 1; + LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; + let client = new alicloud(LocalAlibabaConfig); + let paginating = false; + let pageNumber = 1; var clientCb = function(err, data) { if (err) collection[serviceLower][callKey][region].err = err; if (!data) return regionCb(); @@ -222,10 +259,10 @@ var collect = function(AlibabaConfig, settings, callback) { } }; - function execute(nextTokens, paginateParams) { + function execute(nextToken, paginateParams) { var localParams = JSON.parse(JSON.stringify(callObj.params || {})); localParams['RegionId'] = region; - if (nextTokens) localParams[nextTokens[0]] = nextTokens[1]; + if (nextToken) localParams[nextToken[0]] = nextToken[1]; else if (paginateParams) localParams = {...localParams, ...paginateParams}; client.request(callKey, localParams, requestOption).then((result) => { @@ -245,14 +282,14 @@ var collect = function(AlibabaConfig, settings, callback) { }, function() { async.eachSeries(postcalls, function(postcallObj, postcallCb) { async.eachOfLimit(postcallObj, 10, function(serviceObj, service, serviceCb) { - var serviceLower = service.toLowerCase(); + let serviceLower = service.toLowerCase(); if (!collection[serviceLower]) collection[serviceLower] = {}; async.eachOfLimit(serviceObj, 1, function(callObj, callKey, callCb) { if (settings.api_calls && settings.api_calls.indexOf(service + ':' + callKey) === -1) return callCb(); if (!collection[serviceLower][callKey]) collection[serviceLower][callKey] = {}; - var requestOption = { method: callObj.method || 'POST' }; + let requestOption = { method: callObj.method || 'POST' }; async.eachLimit(regions[serviceLower], helpers.MAX_REGIONS_AT_A_TIME, function(region, regionCb) { if (settings.skip_regions && settings.skip_regions.indexOf(region) > -1 && @@ -269,16 +306,18 @@ var collect = function(AlibabaConfig, settings, callback) { !collection[callObj.reliesOnService][callObj.reliesOnCall][region].data.length)) return regionCb(); - var LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); + let LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); - LocalAlibabaConfig['endpoint'] = `https://${serviceLower}.aliyuncs.com`; - LocalAlibabaConfig['apiVersion'] = callObj.apiVersion || apiVersion; - var client = new alicloud(LocalAlibabaConfig); + LocalAlibabaConfig['endpoint'] = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? + `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; + LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; + let client = new alicloud(LocalAlibabaConfig); async.eachLimit(collection[callObj.reliesOnService][callObj.reliesOnCall][region].data, 10, function(val, valCb) { - collection[serviceLower][callKey][region][val[callObj.resultKey]] = {}; + let resultKey = callObj.filterValue[0]; + collection[serviceLower][callKey][region][val[resultKey]] = {}; - var params = {}; + let params = {}; if (callObj.params) params = JSON.parse(JSON.stringify(callObj.params)); for (let key in callObj.filterKey) { @@ -288,10 +327,10 @@ var collect = function(AlibabaConfig, settings, callback) { params['RegionId'] = region; var requestCb = function(err, data) { - if (err) collection[serviceLower][callKey][region][val[callObj.resultKey]].err = err; + if (err) collection[serviceLower][callKey][region][val[resultKey]].err = err; if (!data) return valCb(); - collection[serviceLower][callKey][region][val[callObj.resultKey]].data = (callObj.resultFilter && data[callObj.resultFilter]) ? + collection[serviceLower][callKey][region][val[resultKey]].data = (callObj.resultFilter && data[callObj.resultFilter]) ? data[callObj.resultFilter] : data; if (callObj.rateLimit) { diff --git a/exports.js b/exports.js index 9d23ffee0d..b6c3d66f65 100644 --- a/exports.js +++ b/exports.js @@ -727,6 +727,7 @@ module.exports = { 'logSinksEnabled' : require(__dirname + '/plugins/google/logging/logSinksEnabled.js'), }, alibaba: { + 'dataDisksEncrypted' : require(__dirname + '/plugins/alibaba/ecs/dataDisksEncrypted.js'), 'rdsSslEncryptionEnabled' : require(__dirname + '/plugins/alibaba/rds/rdsSslEncryptionEnabled.js'), 'passwordRequiresUppercase' : require(__dirname + '/plugins/alibaba/ram/passwordRequiresUppercase.js'), 'usersMfaEnabled' : require(__dirname + '/plugins/alibaba/ram/usersMfaEnabled.js') diff --git a/helpers/alibaba/regions.js b/helpers/alibaba/regions.js index 1583ca018c..42f49ad156 100644 --- a/helpers/alibaba/regions.js +++ b/helpers/alibaba/regions.js @@ -34,5 +34,6 @@ module.exports = { ram: ['cn-hangzhou'], vpc: regions, rds: regions, - sts: ['cn-hangzhou'] + sts: ['cn-hangzhou'], + kms: regions }; diff --git a/plugins/alibaba/ecs/dataDisksEncrypted.js b/plugins/alibaba/ecs/dataDisksEncrypted.js new file mode 100644 index 0000000000..c20d0b401c --- /dev/null +++ b/plugins/alibaba/ecs/dataDisksEncrypted.js @@ -0,0 +1,161 @@ +var async = require('async'); +var helpers = require('../../../helpers/alibaba'); + +var encryptionLevels = ['none', 'sse', 'cloudkms', 'alibabacmk', 'externalcmk', 'cloudhsm']; + +function getEncryptionLevel(kmsKey) { + if (kmsKey.Origin) { + if (kmsKey.Origin === 'Aliyun_KMS') { + if (kmsKey.ProtectionLevel) { + if (kmsKey.ProtectionLevel.toUpperCase() == 'SOFTWARE') return 3; + if (kmsKey.ProtectionLevel.toUpperCase() == 'HSM') return 5; + } + } + if (kmsKey.Origin === 'EXTERNAL') return 4; + } + + return 0; +} + +module.exports = { + title: 'Data Disks Encrypted', + category: 'ECS', + description: 'Ensure that encryption is enabled for ECS data disk volumes.', + more_info: 'Encryption can help you secure your data stored in Alibaba Cloud ECS and comply with security standards.', + link: 'https://www.alibabacloud.com/help/doc-detail/59643.htm', + recommended_action: 'Enable encryption for ECS data disk volumes.', + apis: ['ECS:DescribeDisks', 'KMS:ListKeys', 'KMS:DescribeKey', 'STS:GetCallerIdentity'], + compliance: { + hipaa: 'HIPAA requires that all data is encrypted, including data at rest. ' + + 'ECS disk is a HIPAA-compliant solution that provides automated encryption ' + + 'of ECS instance data at rest.', + pci: 'PCI requires proper encryption of cardholder data at rest. Encryption ' + + 'should be enabled for all disk volumes storing this type of data.' + }, + settings: { + data_disks_encryption_level: { + name: 'ECS Data Disks Encryption Level', + description: 'In order (lowest to highest) cloudkms=Alibaba managed default service KMS; alibabacmk=Customer managed KMS; externalcmk=Customer imported key; cloudhsm=Customer managed CloudHSM sourced Key', + regex: '^(cloudkms|alibabacmk|externalcmk|cloudhsm)$', + default: 'cloudkms', + }, + data_disks_result_limit: { + name: 'Data Disks Result Limit', + description: 'If the number of results is greater than this value, combine them into one result', + regex: '^[0-9]*$', + default: '20', + }, + }, + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(settings); + var defaultRegion = helpers.defaultRegion(settings); + + var accountId = helpers.addSource(cache, source, ['sts', 'GetCallerIdentity', defaultRegion, 'data']); + + var targetEncryptionLevel = encryptionLevels.indexOf(settings.data_disks_encryption_level || this.settings.data_disks_encryption_level.default); + var disksResultLimit = parseInt(settings.data_disks_result_limit || this.settings.data_disks_result_limit.default); + + async.each(regions.ecs, function(region, rcb) { + var describeDisks = helpers.addSource(cache, source, ['ecs', 'DescribeDisks', region]); + if (!describeDisks) return rcb(); + + if (describeDisks.err || !describeDisks.data) { + helpers.addResult(results, 3, 'Unable to query ECS disks: ' + helpers.addError(describeDisks), region); + return rcb(); + } + + if (!describeDisks.data.length) { + helpers.addResult(results, 0, 'No ECS disks found', region); + return rcb(); + } + + var listKeys = helpers.addSource(cache, source, ['kms', 'ListKeys', region]); + + if (!listKeys || listKeys.err || !listKeys.data) { + helpers.addResult(results, 3, 'Unable to query KMS keys: ' + helpers.addError(listKeys), region); + return rcb(); + } + + var encryptionFailing = []; + var encryptionPassing = []; + var keyErrors = []; + var found = false; + + async.each(describeDisks.data, (disk, dcb) => { + if (!disk.DiskId || disk.Type.toLowerCase() !== 'data') return dcb(); + + found = true; + if (!disk.Encrypted) { + encryptionFailing.push(disk.DiskId); + return dcb(); + } + + if (!disk.KMSKeyId || !disk.KMSKeyId.length) { + if (targetEncryptionLevel > 2) { + encryptionFailing.push(disk.DiskId); + } else encryptionPassing.push(disk.DiskId); + return dcb(); + } + + var describeKey = helpers.addSource(cache, source, ['kms', 'DescribeKey', region, disk.KMSKeyId]); + + if (!describeKey || describeKey.err || !describeKey.data) { + keyErrors.push({ kmsKeyId: disk.KMSKeyId, err: helpers.addError(describeKey) }); + return dcb(); + } + + var currentEncryptionLevel = getEncryptionLevel(describeKey.data); + + if (currentEncryptionLevel < targetEncryptionLevel) { + encryptionFailing.push(disk.DiskId); + } else encryptionPassing.push(disk.DiskId); + dcb(); + }, function() { + if (encryptionFailing.length) { + if (encryptionFailing.length > disksResultLimit) { + helpers.addResult(results, 2, `More than ${disksResultLimit} data disks are not encrypted to ${encryptionLevels[targetEncryptionLevel]}`, region); + } else { + encryptionFailing.forEach(diskId => { + let resource = helpers.createArn('ecs', accountId, 'disk', diskId, region); + helpers.addResult(results, 2, `Data disk is not encrypted to ${encryptionLevels[targetEncryptionLevel]}`, region, resource); + }); + } + } + + if (keyErrors.length) { + if (keyErrors.length > disksResultLimit) { + helpers.addResult(results, 3, `More than ${disksResultLimit} errors describing kms keys happened`, region); + } else { + keyErrors.forEach(({err, kmsKeyId}) => { + let resource = helpers.createArn('kms', accountId, 'key', kmsKeyId, region); + helpers.addResult(results, 3, `Unable to describe KMS key: ${err}`, region, resource); + }); + } + } + + if (encryptionPassing.length) { + if (encryptionPassing.length > disksResultLimit) { + helpers.addResult(results, 0, `More than ${disksResultLimit} data disks are encrypted to at least ${encryptionLevels[targetEncryptionLevel]}`, + region); + } else { + encryptionPassing.forEach(diskId => { + let resource = helpers.createArn('ecs', accountId, 'disk', diskId, region); + helpers.addResult(results, 0, `Data disk is encrypted to at least ${encryptionLevels[targetEncryptionLevel]}`, + region, resource); + }); + } + } + + if (!found) { + helpers.addResult(results, 0, 'No ECS data disks found', region); + } + rcb(); + }); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/alibaba/ecs/dataDisksEncrypted.spec.js b/plugins/alibaba/ecs/dataDisksEncrypted.spec.js new file mode 100644 index 0000000000..2abd5a6de6 --- /dev/null +++ b/plugins/alibaba/ecs/dataDisksEncrypted.spec.js @@ -0,0 +1,327 @@ +var expect = require('chai').expect; +var dataDisksEncrypted = require('./dataDisksEncrypted.js'); + +const describeDisks = [ + { + "DetachedTime": "", + "Category": "cloud_essd", + "Description": "", + "KMSKeyId": "", + "ResourceGroupId": "rg-aekzsj44b4lt5fa", + "DedicatedBlockStorageClusterId": "", + "Size": 40, + "Encrypted": true, + "DeleteAutoSnapshot": false, + "DiskChargeType": "PostPaid", + "ExpiredTime": "2999-09-08T16:00Z", + "ImageId": "", + "StorageSetId": "", + "Tags": { + "Tag": [] + }, + "Status": "Available", + "AttachedTime": "", + "ZoneId": "us-west-1b", + "SourceSnapshotId": "", + "ProductCode": "", + "InstanceId": "", + "PerformanceLevel": "PL1", + "Device": "", + "DeleteWithInstance": false, + "EnableAutomatedSnapshotPolicy": false, + "EnableAutoSnapshot": true, + "AutoSnapshotPolicyId": "", + "DiskName": "", + "OperationLocks": { + "OperationLock": [] + }, + "BdfId": "", + "Portable": true, + "Type": "data", + "SerialNumber": "rj956ec1t8qrh97kei6k", + "MountInstances": { + "MountInstance": [] + }, + "CreationTime": "2021-05-06T10:23:18Z", + "RegionId": "us-west-1", + "DiskId": "d-rj956ec1t8qrh97kei6k" + }, + { + "DetachedTime": "", + "Category": "cloud_essd", + "KMSKeyId": "ed204e08-f814-4788-8406-3dc19c8e5260", + "Description": "", + "ResourceGroupId": "rg-aekzsj44b4lt5fa", + "DedicatedBlockStorageClusterId": "", + "Size": 20, + "Encrypted": true, + "DeleteAutoSnapshot": false, + "DiskChargeType": "PostPaid", + "ExpiredTime": "2999-09-08T16:00Z", + "ImageId": "", + "StorageSetId": "", + "Tags": { + "Tag": [] + }, + "Status": "Available", + "AttachedTime": "", + "ZoneId": "us-west-1b", + "SourceSnapshotId": "", + "ProductCode": "", + "InstanceId": "", + "PerformanceLevel": "PL1", + "Device": "", + "DeleteWithInstance": false, + "EnableAutomatedSnapshotPolicy": false, + "EnableAutoSnapshot": true, + "AutoSnapshotPolicyId": "", + "DiskName": "akhtar-made", + "OperationLocks": { + "OperationLock": [] + }, + "BdfId": "", + "Portable": true, + "Type": "data", + "SerialNumber": "rj94pupgygkqr4y3rr59", + "MountInstances": { + "MountInstance": [] + }, + "CreationTime": "2021-05-05T18:31:03Z", + "RegionId": "us-west-1", + "DiskId": "d-rj94pupgygkqr4y3rr59" + }, + { + "DetachedTime": "", + "Category": "cloud_efficiency", + "Description": "", + "KMSKeyId": "", + "ResourceGroupId": "rg-aekzsj44b4lt5fa", + "DedicatedBlockStorageClusterId": "", + "Size": 20, + "Encrypted": true, + "DeleteAutoSnapshot": true, + "DiskChargeType": "PostPaid", + "ExpiredTime": "2999-09-08T16:00Z", + "ImageId": "", + "StorageSetId": "", + "Tags": { + "Tag": [] + }, + "Status": "In_use", + "AttachedTime": "2021-05-05T11:56:22Z", + "ZoneId": "us-west-1b", + "InstanceId": "i-rj9cexclrthxbysg4w5x", + "SourceSnapshotId": "", + "ProductCode": "", + "Device": "/dev/xvdb", + "PerformanceLevel": "", + "DeleteWithInstance": false, + "EnableAutomatedSnapshotPolicy": false, + "EnableAutoSnapshot": true, + "AutoSnapshotPolicyId": "", + "DiskName": "akhtar-made", + "OperationLocks": { + "OperationLock": [] + }, + "BdfId": "", + "Portable": true, + "Type": "data", + "SerialNumber": "rj9jcm5n3s695mng74et", + "MountInstances": { + "MountInstance": [] + }, + "CreationTime": "2021-05-03T11:11:53Z", + "RegionId": "us-west-1", + "DiskId": "d-rj9jcm5n3s695mng74et" + }, + { + "DetachedTime": "2021-05-06T10:20:59Z", + "Category": "cloud_efficiency", + "Description": "", + "KMSKeyId": "", + "ResourceGroupId": "rg-aekzsj44b4lt5fa", + "DedicatedBlockStorageClusterId": "", + "Size": 20, + "Encrypted": false, + "DeleteAutoSnapshot": false, + "DiskChargeType": "PostPaid", + "ExpiredTime": "", + "ImageId": "aliyun_2_1903_x64_20G_alibase_20210325.vhd", + "StorageSetId": "", + "Tags": { + "Tag": [ + { + "TagKey": "acs:ecs:sourceInstanceId", + "TagValue": "i-rj9cexclrthxbysg4w5x" + }, + { + "TagKey": "acs:ecs:diskDeleteProtection", + "TagValue": "true" + }, + { + "TagKey": "acs:ecs:diskPayType", + "TagValue": "AfterPay" + } + ] + }, + "Status": "Available", + "AttachedTime": "2021-04-30T09:57:27Z", + "ZoneId": "us-west-1b", + "SourceSnapshotId": "", + "ProductCode": "", + "InstanceId": "", + "Device": "", + "PerformanceLevel": "", + "DeleteWithInstance": true, + "EnableAutomatedSnapshotPolicy": false, + "EnableAutoSnapshot": true, + "AutoSnapshotPolicyId": "", + "DiskName": "", + "OperationLocks": { + "OperationLock": [ + { + "LockReason": "detached-system-disk" + } + ] + }, + "BdfId": "", + "Portable": true, + "Type": "data", + "SerialNumber": "rj947ftycv8s1xxvghaa", + "MountInstances": { + "MountInstance": [] + }, + "CreationTime": "2021-04-30T09:57:14Z", + "RegionId": "us-west-1", + "DiskId": "d-rj947ftycv8s1xxvghaa" + } +]; + +const listKeys = [ + { + "KeyId": "ed204e08-f814-4788-8406-3dc19c8e5260", + "KeyArn": "acs:kms:us-west-1:0000111122223333:key/ed204e08-f814-4788-8406-3dc19c8e5260" + } +]; + +const describeKey = [ + { + "data": { + "CreationDate": "2021-05-03T11:11:47Z", + "Description": "", + "KeyId": "ed204e08-f814-4788-8406-3dc19c8e5260", + "KeySpec": "Aliyun_AES_256", + "KeyState": "Enabled", + "KeyUsage": "ENCRYPT/DECRYPT", + "PrimaryKeyVersion": "9e42450c-fe3a-4bc0-93b5-e8074aa4b4c9", + "DeleteDate": "", + "Creator": "Ecs", + "Arn": "acs:kms:us-west-1:0000111122223333:key/ed204e08-f814-4788-8406-3dc19c8e5260", + "Origin": "Aliyun_KMS", + "MaterialExpireTime": "", + "ProtectionLevel": "SOFTWARE", + "LastRotationDate": "2021-05-03T11:11:47Z", + "AutomaticRotation": "Disabled" + } + } +]; + +const createCache = (disksData, listKeys, describeKeyData, disksErr, listKeysErr, describeKeyErr) => { + let keyId = (listKeys && listKeys.length) ? listKeys[0].KeyId : null; + return { + ecs: { + DescribeDisks: { + 'cn-hangzhou': { + data: disksData, + err: disksErr + }, + } + }, + kms: { + ListKeys: { + 'cn-hangzhou': { + data: listKeys, + err: listKeysErr + } + }, + DescribeKey: { + 'cn-hangzhou': { + [keyId]: { + data: describeKeyData, + err: describeKeyErr + } + } + } + } + }; +}; + +describe('dataDisksEncrypted', function () { + describe('run', function () { + it('should FAIL if disk is not encrypted', function (done) { + const cache = createCache([describeDisks[3]], listKeys); + dataDisksEncrypted.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Data disk is not encrypted'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should FAIL if Data disk is not encrypted to target encryption level', function (done) { + const cache = createCache([describeDisks[0]], listKeys); + dataDisksEncrypted.run(cache, { data_disks_encryption_level: 'alibabacmk' }, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Data disk is not encrypted'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if data disks are encrypted', function (done) { + const cache = createCache([describeDisks[0], describeDisks[2]], listKeys); + dataDisksEncrypted.run(cache, {}, (err, results) => { + expect(results.length).to.equal(2); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Data disk is encrypted'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should FAIL if Data disk is not encrypted to target level and combine results when reach results limit', function (done) { + const cache = createCache([describeDisks[0], describeDisks[2]], listKeys); + dataDisksEncrypted.run(cache, { data_disks_encryption_level: 'alibabacmk', data_disks_result_limit: '1' }, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('data disks are not encrypted'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if no ECS disks found', function (done) { + const cache = createCache([]); + dataDisksEncrypted.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No ECS disks found'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query ECS disks', function (done) { + const cache = createCache([], null, null, { err: 'Unable to query ECS disks' }); + dataDisksEncrypted.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 ECS disks'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + }) +}) \ No newline at end of file From 429dd89312f7f9cec8a3ce2ca0224e1cae3670ea Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 May 2021 23:15:20 +0500 Subject: [PATCH 08/11] feature/AKD-224: Added Alibaba RDS Log Duration plugin and test cases (#689) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector * Added Alibaba RAM Users MFA Enabled plugin and test cases * Modified collector * Added Alibaba RDS SSL Encryption Enabled plugin and test cases * Added Alibaba RDS Log Duration plugin and test cases Co-authored-by: Gio Rodriguez --- collectors/alibaba/collector.js | 12 +- exports.js | 1 + plugins/alibaba/rds/rdsLogDuration.js | 93 ++++++++++ plugins/alibaba/rds/rdsLogDuration.spec.js | 193 +++++++++++++++++++++ 4 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 plugins/alibaba/rds/rdsLogDuration.js create mode 100644 plugins/alibaba/rds/rdsLogDuration.spec.js diff --git a/collectors/alibaba/collector.js b/collectors/alibaba/collector.js index 1f8bbd04dd..c931e994fd 100644 --- a/collectors/alibaba/collector.js +++ b/collectors/alibaba/collector.js @@ -25,7 +25,9 @@ var regions = helpers.regions(); var regionEndpointMap = { ecs: ['cn-wulanchabu', 'cn-zhangjiak', 'cn-huhehaote', 'cn-heyuan', 'cn-chengdu', 'ap-southeast-2', 'cn-guangzhou', 'ap-southeast-3', 'ap-southeast-5', 'ap-northeast-1', 'ap-south-1', 'eu-central-1', 'eu-west-1', 'me-east-1'], - kms: regions['kms'] + kms: regions['kms'], + rds: ['cn-zhangjiakou', 'cn-huhehaote', 'cn-chengdu', 'ap-southeast-2', 'ap-southeast-3', 'ap-southeast-5', + 'ap-northeast-1', 'ap-south-1', 'eu-central-1', 'eu-west-1', 'me-east-1'] }; var globalServices = [ @@ -170,6 +172,14 @@ var postcalls = [ } }, RDS: { + DescribeParameters: { + reliesOnService: 'rds', + reliesOnCall: 'DescribeDBInstances', + filterKey: ['DBInstanceId'], + filterValue: ['DBInstanceId'], + resultKey: 'DBInstanceId', + apiVersion: '2014-08-15' + }, DescribeDBInstanceSSL: { reliesOnService: 'rds', reliesOnCall: 'DescribeDBInstances', diff --git a/exports.js b/exports.js index b6c3d66f65..977cb0e636 100644 --- a/exports.js +++ b/exports.js @@ -727,6 +727,7 @@ module.exports = { 'logSinksEnabled' : require(__dirname + '/plugins/google/logging/logSinksEnabled.js'), }, alibaba: { + 'rdsLogDuration' : require(__dirname + '/plugins/alibaba/rds/rdsLogDuration.js'), 'dataDisksEncrypted' : require(__dirname + '/plugins/alibaba/ecs/dataDisksEncrypted.js'), 'rdsSslEncryptionEnabled' : require(__dirname + '/plugins/alibaba/rds/rdsSslEncryptionEnabled.js'), 'passwordRequiresUppercase' : require(__dirname + '/plugins/alibaba/ram/passwordRequiresUppercase.js'), diff --git a/plugins/alibaba/rds/rdsLogDuration.js b/plugins/alibaba/rds/rdsLogDuration.js new file mode 100644 index 0000000000..0cb5feacb3 --- /dev/null +++ b/plugins/alibaba/rds/rdsLogDuration.js @@ -0,0 +1,93 @@ +var async = require('async'); +var helpers = require('../../../helpers/alibaba'); + +module.exports = { + title: 'RDS Log Duration', + category: 'RDS', + description: 'Ensure that RDS DB instances have log_duration paraemeter enabled.', + more_info: 'Enabling log_duration parameter logs the duration of each completed SQL statement generating query and error logs ' + + 'which can be used to identify, troubleshoot, and repair configuration errors and sub-optimal performance.', + link: 'https://partners-intl.aliyun.com/help/doc-detail/26179.htm', + recommended_action: 'Modify RDS DB instances to set value for log_duration parameter to ON', + apis: ['RDS:DescribeDBInstances', 'RDS:DescribeParameters', 'STS:GetCallerIdentity'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(); + var defaultRegion = helpers.defaultRegion(settings); + + var accountId = helpers.addSource(cache, source, ['sts', 'GetCallerIdentity', defaultRegion, 'data']); + + async.each(regions.rds, function(region, rcb) { + var describeDBInstances = helpers.addSource(cache, source, + ['rds', 'DescribeDBInstances', region]); + + if (!describeDBInstances) { + return rcb(); + } + + if (describeDBInstances.err || !describeDBInstances.data) { + helpers.addResult(results, 3, + `Unable to query RDS DB instances: ${helpers.addError(describeDBInstances)}`, + region); + return rcb(); + } + + if (!describeDBInstances.data.length) { + helpers.addResult(results, 0, 'No RDS DB instances found', region); + return rcb(); + } + + async.each(describeDBInstances.data, function(instance, cb){ + if (!instance.DBInstanceId) return cb(); + + var resource = helpers.createArn('rds', accountId, 'instance', instance.DBInstanceId, region); + + if (instance.Engine && instance.Engine.toUpperCase() !== 'POSTGRESQL') { + helpers.addResult(results, 0, + `Log Duration is not supported for ${instance.Engine} engine type`, + region, resource); + return cb(); + } + + var describeParameters = helpers.addSource(cache, source, + ['rds', 'DescribeParameters', region, instance.DBInstanceId]); + + if (!describeParameters || describeParameters.err || !describeParameters.data) { + helpers.addResult(results, 3, + `Unable to query DB parameters: ${helpers.addError(describeParameters)}`, + region, resource); + return cb(); + } + + if (describeParameters.data.RunningParameters && + describeParameters.data.RunningParameters.DBInstanceParameter && + describeParameters.data.RunningParameters.DBInstanceParameter.length) { + let parameters = describeParameters.data.RunningParameters.DBInstanceParameter; + let found = parameters.find(parameter => parameter.ParameterName == 'log_duration' && parameter.ParameterValue.toLowerCase() == 'on'); + + if (found) { + helpers.addResult(results, 0, + 'RDS DB instance has log_duration parameter enabled', + region, resource); + } else { + helpers.addResult(results, 2, + 'RDS DB instance does not have log_duration parameter enabled', + region, resource); + } + } else { + helpers.addResult(results, 2, + 'RDS DB instance does not have log_duration parameter enabled', + region, resource); + } + + cb(); + }, function(){ + rcb(); + }); + }, function(){ + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/alibaba/rds/rdsLogDuration.spec.js b/plugins/alibaba/rds/rdsLogDuration.spec.js new file mode 100644 index 0000000000..7bed43c4e4 --- /dev/null +++ b/plugins/alibaba/rds/rdsLogDuration.spec.js @@ -0,0 +1,193 @@ +var expect = require('chai').expect; +var rdsLogDuration = require('./rdsLogDuration.js'); + +const describeDBInstances = [ + { + "EngineVersion": "13.0", + "DBInstanceStatus": "Running", + "ResourceGroupId": "rg-aekzsj44b4lt5fa", + "DBInstanceNetType": "Intranet", + "DBInstanceClass": "pg.n2.small.2c", + "CreateTime": "2021-05-04T17:13:45Z", + "VSwitchId": "vsw-rj94uhhrj5qz5008lwi1x", + "DBInstanceType": "Primary", + "PayType": "Postpaid", + "LockMode": "Unlock", + "MutriORsignle": false, + "InstanceNetworkType": "VPC", + "InsId": 1, + "VpcId": "vpc-rj9vu86hdve3qr173ew17", + "DBInstanceId": "pgm-2ev213kfnogf7mfi", + "ConnectionMode": "Standard", + "ReadOnlyDBInstanceIds": { + "ReadOnlyDBInstanceId": [] + }, + "VpcCloudInstanceId": "pgm-2ev213kfnogf7mfi", + "ExpireTime": "", + "LockReason": "", + "Engine": "PostgreSQL" + } +]; + +const describeParameters = [ + { + "RunningParameters": { + "DBInstanceParameter": [ + { + "ParameterValue": "on", + "ParameterName": "log_duration", + "ParameterDescription": "Logs the duration of each completed SQL statement." + }, + { + "ParameterValue": "off", + "ParameterName": "jit", + "ParameterDescription": "allow JIT compilation" + }, + { + "ParameterValue": "0", + "ParameterName": "lock_timeout", + "ParameterDescription": "Sets the maximum allowed duration of any wait for a lock. A value of 0 turns off the timeout." + }, + { + "ParameterValue": "off", + "ParameterName": "log_connections", + "ParameterDescription": "Logs each successful connection." + }, + { + "ParameterValue": "off", + "ParameterName": "log_disconnections", + "ParameterDescription": "Logs end of a session, including duration." + }, + { + "ParameterValue": "1000", + "ParameterName": "log_min_duration_statement", + "ParameterDescription": "SQL with execution time exceeding this value will be logged. Note that a too small value may cause performance degradation and increase the amount of logs." + }, + { + "ParameterValue": "ddl", + "ParameterName": "log_statement", + "ParameterDescription": "Sets the type of statements logged. Setting it to all or mod will cause performance degradation and increase the amount of logs." + }, + { + "ParameterValue": "-1", + "ParameterName": "old_snapshot_threshold", + "ParameterDescription": "Time before a snapshot is too old to read pages changed after the snapshot was taken." + }, + { + "ParameterValue": "disable", + "ParameterName": "sql_firewall.firewall", + "ParameterDescription": "The parameter is to detemine running mode of sql_firewall extension." + }, + { + "ParameterValue": "off", + "ParameterName": "ssl", + "ParameterDescription": "Enables SSL connections." + }, + { + "ParameterValue": "TLSv1", + "ParameterName": "ssl_min_protocol_version", + "ParameterDescription": "Sets the minimum SSL/TLS protocol version to use" + }, + { + "ParameterValue": "0", + "ParameterName": "statement_timeout", + "ParameterDescription": "Sets the maximum allowed duration of any statement. A value of 0 turns off the timeout." + }, + ] + }, + "EngineVersion": "13.0", + "Engine": "PostgreSQL" + }, + { + "RunningParameters": { + "DBInstanceParameter": [ + { + "ParameterValue": "off", + "ParameterName": "log_duration", + "ParameterDescription": "Logs the duration of each completed SQL statement." + }, + ] + } + } +]; + +const createCache = (dbInstances, describeParameters, dbInstancesErr, describeParametersErr) => { + let instanceId = (dbInstances && dbInstances.length) ? dbInstances[0].DBInstanceId : null; + return { + rds: { + DescribeDBInstances: { + 'cn-hangzhou': { + data: dbInstances, + err: dbInstancesErr + }, + }, + DescribeParameters: { + 'cn-hangzhou': { + [instanceId]: { + data: describeParameters, + err: describeParametersErr + } + } + } + }, + }; +}; + +describe('rdsLogDuration', function () { + describe('run', function () { + it('should FAIL if RDS DB instance does not have log_duration parameter enabled', function (done) { + const cache = createCache(describeDBInstances, describeParameters[1]); + rdsLogDuration.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('RDS DB instance does not have log_duration parameter enabled'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if RDS DB instance has log_duration parameter enabled', function (done) { + const cache = createCache(describeDBInstances, describeParameters[0]); + rdsLogDuration.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('RDS DB instance has log_duration parameter enabled'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if no RDS DB instances found', function (done) { + const cache = createCache([]); + rdsLogDuration.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No RDS DB instances found'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query RDS DB instances', function (done) { + const cache = createCache([], null, { err: 'Unable to query RDS DB instances' }); + rdsLogDuration.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 RDS DB instances'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query DB parameters', function (done) { + const cache = createCache([describeDBInstances[0]], {}, null, { err: 'Unable to query DB parameters' }); + rdsLogDuration.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 DB parameters'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + }) +}) \ No newline at end of file From 8e09ee535702e3d85532e2fe6ed2fb577f484f11 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 May 2021 23:31:48 +0500 Subject: [PATCH 09/11] feature/AKD-212: Added Alibaba OSS Bucket Private plugin and test cases (#690) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector * Added Alibaba RAM Users MFA Enabled plugin and test cases * Modified collector * Added Alibaba RDS SSL Encryption Enabled plugin and test cases * Added Alibaba RDS Log Duration plugin and test cases * Added Alibaba OSS Bucket Private plugin and test cases * Update collectors/alibaba/collector.js * Update collectors/alibaba/collector.js * lint Co-authored-by: Gio Rodriguez --- collectors/alibaba/collector.js | 239 +++++++++++-------- collectors/alibaba/oss/getBucketInfo.js | 5 + collectors/alibaba/oss/index.js | 21 ++ collectors/alibaba/oss/listBuckets.js | 29 +++ exports.js | 1 + helpers/alibaba/regions.js | 1 + plugins/alibaba/oss/ossBucketPrivate.js | 69 ++++++ plugins/alibaba/oss/ossBucketPrivate.spec.js | 148 ++++++++++++ 8 files changed, 412 insertions(+), 101 deletions(-) create mode 100644 collectors/alibaba/oss/getBucketInfo.js create mode 100644 collectors/alibaba/oss/index.js create mode 100644 collectors/alibaba/oss/listBuckets.js create mode 100644 plugins/alibaba/oss/ossBucketPrivate.js create mode 100644 plugins/alibaba/oss/ossBucketPrivate.spec.js diff --git a/collectors/alibaba/collector.js b/collectors/alibaba/collector.js index c931e994fd..49ea8d000c 100644 --- a/collectors/alibaba/collector.js +++ b/collectors/alibaba/collector.js @@ -19,6 +19,7 @@ Arguments: var alicloud = require('@alicloud/pop-core'); var async = require('async'); var helpers = require(__dirname + '/../../helpers/alibaba'); +var collectors = require(__dirname + '/../../collectors/alibaba'); var regions = helpers.regions(); @@ -31,11 +32,15 @@ var regionEndpointMap = { }; var globalServices = [ - 'RAM', - 'OSS' + 'RAM' ]; var calls = { + OSS: { + listBuckets: { + override: true + } + }, ECS: { DescribeInstances: { property: 'Instances', @@ -150,6 +155,7 @@ var postcalls = [ filterKey: ['UserName'], filterValue: ['UserName'], resultFilter: 'User', + resultKey: 'UserName', apiVersion: '2015-05-01' } }, @@ -188,6 +194,13 @@ var postcalls = [ resultKey: 'DBInstanceId', apiVersion: '2014-08-15' } + }, + OSS: { + getBucketInfo: { + reliesOnService: 'oss', + reliesOnCall: 'listBuckets', + override: true + } } } ]; @@ -218,71 +231,83 @@ var collect = function(AlibabaConfig, settings, callback) { let LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); - let endpoint = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? - `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; - LocalAlibabaConfig['endpoint'] = endpoint; - LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; - let client = new alicloud(LocalAlibabaConfig); - let paginating = false; - let pageNumber = 1; - var clientCb = function(err, data) { - if (err) collection[serviceLower][callKey][region].err = err; - if (!data) return regionCb(); - if (callObj.property && !data[callObj.property]) return regionCb(); - if (callObj.subProperty && !data[callObj.property][callObj.subProperty]) return regionCb(); - - var dataToAdd = callObj.subProperty ? data[callObj.property][callObj.subProperty] : data[callObj.property]; - - if (paginating) { - collection[serviceLower][callKey][region].data = collection[serviceLower][callKey][region].data.concat(dataToAdd); - } else { - collection[serviceLower][callKey][region].data = dataToAdd; - } + if (callObj.override) { + collectors[serviceLower][callKey](LocalAlibabaConfig, collection, region, function() { + if (callObj.rateLimit) { + setTimeout(function() { + regionCb(); + }, callObj.rateLimit); + } else { + regionCb(); + } + }); + } else { + let endpoint = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? + `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; + LocalAlibabaConfig['endpoint'] = endpoint; + LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; + let client = new alicloud(LocalAlibabaConfig); + let paginating = false; + let pageNumber = 1; + var clientCb = function(err, data) { + if (err) collection[serviceLower][callKey][region].err = err; + if (!data) return regionCb(); + if (callObj.property && !data[callObj.property]) return regionCb(); + if (callObj.subProperty && !data[callObj.property][callObj.subProperty]) return regionCb(); + + var dataToAdd = callObj.subProperty ? data[callObj.property][callObj.subProperty] : data[callObj.property]; + + if (paginating) { + collection[serviceLower][callKey][region].data = collection[serviceLower][callKey][region].data.concat(dataToAdd); + } else { + collection[serviceLower][callKey][region].data = dataToAdd; + } - if (callObj.paginate && callObj.paginate == 'Pages' && settings.paginate) { - if (data['PageNumber'] && data['PageSize'] && data['TotalCount']) { - let pageSize = callObj.pageSize || parseInt(data['PageSize']); - let totalCount = parseInt(data['TotalCount']); + if (callObj.paginate && callObj.paginate == 'Pages' && settings.paginate) { + if (data['PageNumber'] && data['PageSize'] && data['TotalCount']) { + let pageSize = callObj.pageSize || parseInt(data['PageSize']); + let totalCount = parseInt(data['TotalCount']); - if ((pageNumber*pageSize) < totalCount) { - paginating = true; - pageNumber += 1; - let paginateParams = { PageNumber: pageNumber, PageSize: pageSize}; - return execute(null, paginateParams); + if ((pageNumber*pageSize) < totalCount) { + paginating = true; + pageNumber += 1; + let paginateParams = { PageNumber: pageNumber, PageSize: pageSize}; + return execute(null, paginateParams); + } } } - } - var nextToken = callObj.paginate; - if (settings.paginate && nextToken && data[nextToken]) { - paginating = true; - var paginateProp = callObj.paginateReqProp ? callObj.paginateReqProp : nextToken; - return execute([paginateProp, data[nextToken]]); - } + var nextToken = callObj.paginate; + if (settings.paginate && nextToken && data[nextToken]) { + paginating = true; + var paginateProp = callObj.paginateReqProp ? callObj.paginateReqProp : nextToken; + return execute([paginateProp, data[nextToken]]); + } - if (callObj.rateLimit) { - setTimeout(function() { + if (callObj.rateLimit) { + setTimeout(function() { + regionCb(); + }, callObj.rateLimit); + } else { regionCb(); - }, callObj.rateLimit); - } else { - regionCb(); + } + }; + + function execute(nextToken, paginateParams) { // eslint-disable-line no-inner-declarations + var localParams = JSON.parse(JSON.stringify(callObj.params || {})); + localParams['RegionId'] = region; + if (nextToken) localParams[nextToken[0]] = nextToken[1]; + else if (paginateParams) localParams = {...localParams, ...paginateParams}; + + client.request(callKey, localParams, requestOption).then((result) => { + clientCb(null, result); + }, (err) => { + clientCb(err); + }); } - }; - - function execute(nextToken, paginateParams) { - var localParams = JSON.parse(JSON.stringify(callObj.params || {})); - localParams['RegionId'] = region; - if (nextToken) localParams[nextToken[0]] = nextToken[1]; - else if (paginateParams) localParams = {...localParams, ...paginateParams}; - - client.request(callKey, localParams, requestOption).then((result) => { - clientCb(null, result); - }, (err) => { - clientCb(err); - }); - } - execute(); + execute(); + } }, function() { callCb(); }); @@ -318,58 +343,70 @@ var collect = function(AlibabaConfig, settings, callback) { let LocalAlibabaConfig = JSON.parse(JSON.stringify(AlibabaConfig)); - LocalAlibabaConfig['endpoint'] = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? - `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; - LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; - let client = new alicloud(LocalAlibabaConfig); - - async.eachLimit(collection[callObj.reliesOnService][callObj.reliesOnCall][region].data, 10, function(val, valCb) { - let resultKey = callObj.filterValue[0]; - collection[serviceLower][callKey][region][val[resultKey]] = {}; - - let params = {}; - if (callObj.params) params = JSON.parse(JSON.stringify(callObj.params)); - - for (let key in callObj.filterKey) { - params[callObj.filterKey[key]] = val[callObj.filterValue[key]]; - } + if (callObj.override) { + collectors[serviceLower][callKey](LocalAlibabaConfig, collection, region, function() { + if (callObj.rateLimit) { + setTimeout(function() { + regionCb(); + }, callObj.rateLimit); + } else { + regionCb(); + } + }); + } else { + LocalAlibabaConfig['endpoint'] = (regionEndpointMap[serviceLower] && regionEndpointMap[serviceLower].includes(region)) ? + `https://${serviceLower}.${region}.aliyuncs.com` : `https://${serviceLower}.aliyuncs.com`; + LocalAlibabaConfig['apiVersion'] = callObj.apiVersion; + let client = new alicloud(LocalAlibabaConfig); + + async.eachLimit(collection[callObj.reliesOnService][callObj.reliesOnCall][region].data, 10, function(val, valCb) { + let resultKey = callObj.filterValue[0]; + collection[serviceLower][callKey][region][val[resultKey]] = {}; + + let params = {}; + if (callObj.params) params = JSON.parse(JSON.stringify(callObj.params)); + + for (let key in callObj.filterKey) { + params[callObj.filterKey[key]] = val[callObj.filterValue[key]]; + } - params['RegionId'] = region; + params['RegionId'] = region; - var requestCb = function(err, data) { - if (err) collection[serviceLower][callKey][region][val[resultKey]].err = err; - if (!data) return valCb(); + var requestCb = function(err, data) { + if (err) collection[serviceLower][callKey][region][val[resultKey]].err = err; + if (!data) return valCb(); - collection[serviceLower][callKey][region][val[resultKey]].data = (callObj.resultFilter && data[callObj.resultFilter]) ? - data[callObj.resultFilter] : data; + collection[serviceLower][callKey][region][val[resultKey]].data = (callObj.resultFilter && data[callObj.resultFilter]) ? + data[callObj.resultFilter] : data; + if (callObj.rateLimit) { + setTimeout(function() { + valCb(); + }, callObj.rateLimit); + } else { + valCb(); + } + }; + + var execute = function() { + client.request(callKey, params, requestOption).then((result) => { + requestCb(null, result); + }, (err) => { + requestCb(err); + }); + }; + + execute(); + }, function() { if (callObj.rateLimit) { setTimeout(function() { - valCb(); + regionCb(); }, callObj.rateLimit); } else { - valCb(); - } - }; - - var execute = function() { - client.request(callKey, params, requestOption).then((result) => { - requestCb(null, result); - }, (err) => { - requestCb(err); - }); - }; - - execute(); - }, function() { - if (callObj.rateLimit) { - setTimeout(function() { regionCb(); - }, callObj.rateLimit); - } else { - regionCb(); - } - }); + } + }); + } }, function() { callCb(); }); diff --git a/collectors/alibaba/oss/getBucketInfo.js b/collectors/alibaba/oss/getBucketInfo.js new file mode 100644 index 0000000000..8c4a537d5f --- /dev/null +++ b/collectors/alibaba/oss/getBucketInfo.js @@ -0,0 +1,5 @@ +var index = require(__dirname + '/index.js'); + +module.exports = function(AlibabaConfig, collection, region, callback) { + index('getBucketInfo', AlibabaConfig, collection, region, callback); +}; \ No newline at end of file diff --git a/collectors/alibaba/oss/index.js b/collectors/alibaba/oss/index.js new file mode 100644 index 0000000000..90fc63ba0e --- /dev/null +++ b/collectors/alibaba/oss/index.js @@ -0,0 +1,21 @@ +var async = require('async'); +const OSS = require('ali-oss'); + +module.exports = function(callKey, AlibabaConfig, collection, region, callback) { + var store = new OSS(AlibabaConfig); + + async.eachLimit(collection.oss.listBuckets[region].data, 10, function(bucket, bcb){ + let bucketName = bucket.name; + collection.oss[callKey][region][bucketName] = {}; + + store[callKey](bucketName).then((result) => { + collection.oss[callKey][region][bucketName].data = result.bucket; + bcb(); + }, (err) => { + collection.oss[callKey][region][bucketName].err = err; + bcb(); + }); + }, function(){ + callback(); + }); +}; \ No newline at end of file diff --git a/collectors/alibaba/oss/listBuckets.js b/collectors/alibaba/oss/listBuckets.js new file mode 100644 index 0000000000..83c5b88446 --- /dev/null +++ b/collectors/alibaba/oss/listBuckets.js @@ -0,0 +1,29 @@ +const OSS = require('ali-oss'); + +module.exports = function(AlibabaConfig, collection, region, callback) { + const store = new OSS(AlibabaConfig); + collection.oss.listBuckets[region].data = []; + + var execute = function(nextToken) { + store.listBuckets({ + 'max-keys': 1, + 'marker': nextToken + }).then((result) => { + callCB(null, result); + }, (err) => { + callCB(err); + }); + }; + + var callCB = function(err, data) { + if (err) { + collection.oss.listBuckets[region].err = err; + callback(); + } + collection.oss.listBuckets[region].data = collection.oss.listBuckets[region].data.concat(data.buckets); + if (data.nextMarker) execute(data.nextMarker); + else callback(); + }; + + execute(); +}; \ No newline at end of file diff --git a/exports.js b/exports.js index 977cb0e636..3c9f90cb06 100644 --- a/exports.js +++ b/exports.js @@ -727,6 +727,7 @@ module.exports = { 'logSinksEnabled' : require(__dirname + '/plugins/google/logging/logSinksEnabled.js'), }, alibaba: { + 'ossBucketPrivate' : require(__dirname + '/plugins/alibaba/oss/ossBucketPrivate.js'), 'rdsLogDuration' : require(__dirname + '/plugins/alibaba/rds/rdsLogDuration.js'), 'dataDisksEncrypted' : require(__dirname + '/plugins/alibaba/ecs/dataDisksEncrypted.js'), 'rdsSslEncryptionEnabled' : require(__dirname + '/plugins/alibaba/rds/rdsSslEncryptionEnabled.js'), diff --git a/helpers/alibaba/regions.js b/helpers/alibaba/regions.js index 42f49ad156..43d343f6f7 100644 --- a/helpers/alibaba/regions.js +++ b/helpers/alibaba/regions.js @@ -35,5 +35,6 @@ module.exports = { vpc: regions, rds: regions, sts: ['cn-hangzhou'], + oss: ['cn-hangzhou'], kms: regions }; diff --git a/plugins/alibaba/oss/ossBucketPrivate.js b/plugins/alibaba/oss/ossBucketPrivate.js new file mode 100644 index 0000000000..f393b85052 --- /dev/null +++ b/plugins/alibaba/oss/ossBucketPrivate.js @@ -0,0 +1,69 @@ +var async = require('async'); +var helpers = require('../../../helpers/alibaba'); + +module.exports = { + title: 'OSS Bucket Private', + category: 'OSS', + description: 'Ensure that OSS bucket is not publicly accessible.', + more_info: 'When you allow public-access on an OSS bucket, all Internet users can access the objects in the bucket ' + + 'and write data to the bucket. This may cause unexpected access to the data in your bucket, and cause an increase in your fees. ' + + 'If a user uploads prohibited data or information, it may affect your legitimate interests and rights. ', + recommended_action: 'Modify bucket ACL to restrict access to be private.', + link: 'https://www.alibabacloud.com/help/doc-detail/31843.htm', + apis: ['OSS:listBuckets', 'OSS:getBucketInfo', 'STS:GetCallerIdentity'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + + var region = helpers.defaultRegion(settings); + + var accountId = helpers.addSource(cache, source, ['sts', 'GetCallerIdentity', region, 'data']); + + var listBuckets = helpers.addSource(cache, source, ['oss', 'listBuckets', region]); + + if (!listBuckets) return callback(null, results, source); + if (listBuckets.err || !listBuckets.data) { + helpers.addResult(results, 3, `Unable to query for OSS buckets: ${helpers.addError(listBuckets)}`, region); + return callback(null, results, source); + } + + if (!listBuckets.data.length) { + helpers.addResult(results, 0, 'No OSS buckets found', region); + return callback(null, results, source); + } + + async.each(listBuckets.data, (bucket, cb) => { + if (!bucket.name) return cb(); + + var getBucketInfo = helpers.addSource(cache, source, + ['oss', 'getBucketInfo', region, bucket.name]); + var bucketLocation = bucket.region || region; + bucketLocation = bucketLocation.replace('oss-', ''); + + var resource = helpers.createArn('oss', accountId, 'bucket', bucket.name, bucketLocation); + + if (!getBucketInfo || getBucketInfo.err || !getBucketInfo.data) { + helpers.addResult(results, 3, + `Unable to query OSS bucket info: ${helpers.addError(getBucketInfo)}`, bucketLocation, resource); + return cb(); + } + + if (getBucketInfo.data.AccessControlList && + getBucketInfo.data.AccessControlList.Grant && + getBucketInfo.data.AccessControlList.Grant == 'private') { + helpers.addResult(results, 0, + `Bucket ACL allows ${getBucketInfo.data.AccessControlList.Grant} access`, + bucketLocation, resource); + } else { + helpers.addResult(results, 2, + `Bucket ACL allows ${getBucketInfo.data.AccessControlList.Grant} access`, + bucketLocation, resource); + } + + cb(); + }, function() { + callback(null, results, source); + }); + } +}; diff --git a/plugins/alibaba/oss/ossBucketPrivate.spec.js b/plugins/alibaba/oss/ossBucketPrivate.spec.js new file mode 100644 index 0000000000..0c2342b4e5 --- /dev/null +++ b/plugins/alibaba/oss/ossBucketPrivate.spec.js @@ -0,0 +1,148 @@ +var expect = require('chai').expect; +var ossBucketPrivate = require('./ossBucketPrivate.js'); + +const listBuckets = [ + { + "name": 'test-bucket', + "region": 'oss-cn-hangzhou', + "creationDate": '2021-05-08T10:35:06.000Z', + "storageClass": 'Standard', + "StorageClass": 'Standard', + } +]; + +const getBucketInfo = [ + { + "Comment": "", + "CreationDate": "2021-05-08T18:42:34.000Z", + "CrossRegionReplication": "Disabled", + "DataRedundancyType": "LRS", + "ExtranetEndpoint": "oss-cn-hangzhou.aliyuncs.com", + "IntranetEndpoint": "oss-cn-hangzhou-internal.aliyuncs.com", + "Location": "oss-cn-hangzhou", + "Name": "test-bucket", + "StorageClass": "Standard", + "TransferAcceleration": "Disabled", + "Owner": { + "DisplayName": "5103119194921620", + "ID": "5103119194921620" + }, + "AccessControlList": { + "Grant": "private" + }, + "ServerSideEncryptionRule": { + "SSEAlgorithm": "None" + }, + "BucketPolicy": { + "LogBucket": "", + "LogPrefix": "" + } + }, + { + "Comment": "", + "CreationDate": "2021-05-08T18:42:34.000Z", + "CrossRegionReplication": "Disabled", + "DataRedundancyType": "LRS", + "ExtranetEndpoint": "oss-cn-hangzhou.aliyuncs.com", + "IntranetEndpoint": "oss-cn-hangzhou-internal.aliyuncs.com", + "Location": "oss-cn-hangzhou", + "Name": "test-bucket", + "StorageClass": "Standard", + "TransferAcceleration": "Disabled", + "Owner": { + "DisplayName": "5103119194921620", + "ID": "5103119194921620" + }, + "AccessControlList": { + "Grant": "public-read-write" + }, + "ServerSideEncryptionRule": { + "SSEAlgorithm": "None" + }, + "BucketPolicy": { + "LogBucket": "", + "LogPrefix": "" + } + } +]; + +const createCache = (listBuckets, getBucketInfo, listBucketsErr, getBucketInfoErr) => { + let bucketName = (listBuckets && listBuckets.length) ? listBuckets[0].name : null; + return { + oss: { + listBuckets: { + 'cn-hangzhou': { + data: listBuckets, + err: listBucketsErr + }, + }, + getBucketInfo: { + 'cn-hangzhou': { + [bucketName]: { + data: getBucketInfo, + err: getBucketInfoErr + } + } + } + }, + }; +}; + +describe('ossBucketPrivate', function () { + describe('run', function () { + it('should FAIL if bucket ACL allows public-read-write access', function (done) { + const cache = createCache(listBuckets, getBucketInfo[1]); + ossBucketPrivate.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Bucket ACL allows public-read-write access'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if bucket ACL allows private access', function (done) { + const cache = createCache(listBuckets, getBucketInfo[0]); + ossBucketPrivate.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Bucket ACL allows private access'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if no OSS buckets found', function (done) { + const cache = createCache([]); + ossBucketPrivate.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No OSS buckets found'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query for OSS buckets', function (done) { + const cache = createCache([], null, { err: 'Unable to query for OSS buckets' }); + ossBucketPrivate.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 OSS buckets'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query OSS bucket info', function (done) { + const cache = createCache(listBuckets, {}, null, { err: 'Unable to query OSS bucket info' }); + ossBucketPrivate.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 OSS bucket info'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + }) +}) \ No newline at end of file From 9598df04b41f0c9d7eefa49d032f930d8b56bfcd Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 May 2021 23:37:30 +0500 Subject: [PATCH 10/11] feature/AKD-213: Added Alibaba OSS Bucket Logging Enabled plugin and test cases (#691) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector * Added Alibaba RAM Users MFA Enabled plugin and test cases * Modified collector * Added Alibaba RDS SSL Encryption Enabled plugin and test cases * Added Alibaba RDS Log Duration plugin and test cases * Added Alibaba OSS Bucket Private plugin and test cases * Added Alibaba OSS Bucket Logging Enabled plugin and test cases Co-authored-by: Gio Rodriguez --- exports.js | 1 + plugins/alibaba/oss/bucketLoggingEnabled.js | 67 ++++++++ .../alibaba/oss/bucketLoggingEnabled.spec.js | 148 ++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 plugins/alibaba/oss/bucketLoggingEnabled.js create mode 100644 plugins/alibaba/oss/bucketLoggingEnabled.spec.js diff --git a/exports.js b/exports.js index 3c9f90cb06..00c28f4bee 100644 --- a/exports.js +++ b/exports.js @@ -727,6 +727,7 @@ module.exports = { 'logSinksEnabled' : require(__dirname + '/plugins/google/logging/logSinksEnabled.js'), }, alibaba: { + 'bucketLoggingEnabled' : require(__dirname + '/plugins/alibaba/oss/bucketLoggingEnabled.js'), 'ossBucketPrivate' : require(__dirname + '/plugins/alibaba/oss/ossBucketPrivate.js'), 'rdsLogDuration' : require(__dirname + '/plugins/alibaba/rds/rdsLogDuration.js'), 'dataDisksEncrypted' : require(__dirname + '/plugins/alibaba/ecs/dataDisksEncrypted.js'), diff --git a/plugins/alibaba/oss/bucketLoggingEnabled.js b/plugins/alibaba/oss/bucketLoggingEnabled.js new file mode 100644 index 0000000000..04682917fd --- /dev/null +++ b/plugins/alibaba/oss/bucketLoggingEnabled.js @@ -0,0 +1,67 @@ +var async = require('async'); +var helpers = require('../../../helpers/alibaba'); + +module.exports = { + title: 'Bucket Logging Enabled', + category: 'OSS', + description: 'Ensure that OSS buckets has logging enabled.', + more_info: 'Enabling logging for OSS buckets provides visibility into request made to access bucket objects which can be useful in auditing and security workflows.', + recommended_action: 'Modify OSS buckets to enable logging.', + link: 'https://www.alibabacloud.com/help/doc-detail/31900.htm', + apis: ['OSS:listBuckets', 'OSS:getBucketInfo', 'STS:GetCallerIdentity'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + + var region = helpers.defaultRegion(settings); + + var accountId = helpers.addSource(cache, source, ['sts', 'GetCallerIdentity', region, 'data']); + + var listBuckets = helpers.addSource(cache, source, ['oss', 'listBuckets', region]); + + if (!listBuckets) return callback(null, results, source); + if (listBuckets.err || !listBuckets.data) { + helpers.addResult(results, 3, `Unable to query for OSS buckets: ${helpers.addError(listBuckets)}`, region); + return callback(null, results, source); + } + + if (!listBuckets.data.length) { + helpers.addResult(results, 0, 'No OSS buckets found', region); + return callback(null, results, source); + } + + async.each(listBuckets.data, (bucket, cb) => { + if (!bucket.name) return cb(); + + var getBucketInfo = helpers.addSource(cache, source, + ['oss', 'getBucketInfo', region, bucket.name]); + var bucketLocation = bucket.region || region; + bucketLocation = bucketLocation.replace('oss-', ''); + + var resource = helpers.createArn('oss', accountId, 'bucket', bucket.name, bucketLocation); + + if (!getBucketInfo || getBucketInfo.err || !getBucketInfo.data) { + helpers.addResult(results, 3, + `Unable to query OSS bucket info: ${helpers.addError(getBucketInfo)}`, bucketLocation, resource); + return cb(); + } + + if (getBucketInfo.data.BucketPolicy && + getBucketInfo.data.BucketPolicy.LogBucket && + getBucketInfo.data.BucketPolicy.LogBucket.length) { + helpers.addResult(results, 0, + 'Bucket has logging enabled', + bucketLocation, resource); + } else { + helpers.addResult(results, 2, + 'Bucket does not have logging enabled', + bucketLocation, resource); + } + + cb(); + }, function() { + callback(null, results, source); + }); + } +}; diff --git a/plugins/alibaba/oss/bucketLoggingEnabled.spec.js b/plugins/alibaba/oss/bucketLoggingEnabled.spec.js new file mode 100644 index 0000000000..7c5cb57e6a --- /dev/null +++ b/plugins/alibaba/oss/bucketLoggingEnabled.spec.js @@ -0,0 +1,148 @@ +var expect = require('chai').expect; +var bucketLoggingEnabled = require('./bucketLoggingEnabled.js'); + +const listBuckets = [ + { + "name": 'test-bucket', + "region": 'oss-cn-hangzhou', + "creationDate": '2021-05-08T10:35:06.000Z', + "storageClass": 'Standard', + "StorageClass": 'Standard', + } +]; + +const getBucketInfo = [ + { + "Comment": "", + "CreationDate": "2021-05-08T18:42:34.000Z", + "CrossRegionReplication": "Disabled", + "DataRedundancyType": "LRS", + "ExtranetEndpoint": "oss-cn-hangzhou.aliyuncs.com", + "IntranetEndpoint": "oss-cn-hangzhou-internal.aliyuncs.com", + "Location": "oss-cn-hangzhou", + "Name": "test-bucket", + "StorageClass": "Standard", + "TransferAcceleration": "Disabled", + "Owner": { + "DisplayName": "5103119194921620", + "ID": "5103119194921620" + }, + "AccessControlList": { + "Grant": "private" + }, + "ServerSideEncryptionRule": { + "SSEAlgorithm": "None" + }, + "BucketPolicy": { + "LogBucket": "", + "LogPrefix": "" + } + }, + { + "Comment": "", + "CreationDate": "2021-05-08T18:42:34.000Z", + "CrossRegionReplication": "Disabled", + "DataRedundancyType": "LRS", + "ExtranetEndpoint": "oss-cn-hangzhou.aliyuncs.com", + "IntranetEndpoint": "oss-cn-hangzhou-internal.aliyuncs.com", + "Location": "oss-cn-hangzhou", + "Name": "test-bucket", + "StorageClass": "Standard", + "TransferAcceleration": "Disabled", + "Owner": { + "DisplayName": "5103119194921620", + "ID": "5103119194921620" + }, + "AccessControlList": { + "Grant": "public-read-write" + }, + "ServerSideEncryptionRule": { + "SSEAlgorithm": "None" + }, + "BucketPolicy": { + "LogBucket": "log-bucket", + "LogPrefix": "" + } + } +]; + +const createCache = (listBuckets, getBucketInfo, listBucketsErr, getBucketInfoErr) => { + let bucketName = (listBuckets && listBuckets.length) ? listBuckets[0].name : null; + return { + oss: { + listBuckets: { + 'cn-hangzhou': { + data: listBuckets, + err: listBucketsErr + }, + }, + getBucketInfo: { + 'cn-hangzhou': { + [bucketName]: { + data: getBucketInfo, + err: getBucketInfoErr + } + } + } + }, + }; +}; + +describe('bucketLoggingEnabled', function () { + describe('run', function () { + it('should FAIL if bucket does not have logging enabled', function (done) { + const cache = createCache(listBuckets, getBucketInfo[0]); + bucketLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Bucket does not have logging enabled'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if bucket has logging enabled', function (done) { + const cache = createCache(listBuckets, getBucketInfo[1]); + bucketLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Bucket has logging enabled'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if no OSS buckets found', function (done) { + const cache = createCache([]); + bucketLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No OSS buckets found'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query for OSS buckets', function (done) { + const cache = createCache([], null, { err: 'Unable to query for OSS buckets' }); + bucketLoggingEnabled.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 OSS buckets'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNOWN if unable to query OSS bucket info', function (done) { + const cache = createCache(listBuckets, {}, null, { err: 'Unable to query OSS bucket info' }); + bucketLoggingEnabled.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 OSS bucket info'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + }) +}) \ No newline at end of file From 8e6e7058ad90b62df9aa0e0632121b9241b34084 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 May 2021 23:45:18 +0500 Subject: [PATCH 11/11] feature/AKD-221: Added Alibaba ECS Open SSH plugin and test cases (#695) * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added vpcEndpointAcceptance plugin and spec file * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Fixed eslint issues * Update index.js * Update index.js * Added cloudformation in china and gov regions * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation * Added plugin and spec file for launch wizard security groups * Added vpcEndpointAcceptance plugin and spec file * Refactored code in plaintextParameters plugin and spec file * SPLOIT-113: Updated custom settings * Made PR requested changes * SPLOIT-113: Added regex to check if NoEcho is enabled * Accommodated PR changes * Fixed eslint issues * Update exports.js * Update index.js * Update index.js * Accomodated PR changes * Updated status in result of failure * Removed unnecesary rebase changes * Added superlinter * Added scans ci * Updated Ci file * Updated Node version in CI file * removed spech check command * Delete scan_ci.yml * Added spellcheck * Azure MySQL Enforce SSL Connection remediated * Modified index and engine files to fix azure remediation invalid token issue * Update engine.js * Added new argument for cloud * Modified index for Alibaba cloud * Added logic for Alibaba collector * Modified collector * Added Alibaba RAM Users MFA Enabled plugin and test cases * Modified collector * Added ECS Data Disks Encyrpted plugin and test cases * Added Alibaba ECS Open SSH plugin and test cases Co-authored-by: Gio Rodriguez --- collectors/alibaba/collector.js | 19 +++- exports.js | 1 + helpers/alibaba/functions.js | 81 +++++++++++++++- plugins/alibaba/ecs/openSSH.js | 48 +++++++++ plugins/alibaba/ecs/openSSH.spec.js | 145 ++++++++++++++++++++++++++++ 5 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 plugins/alibaba/ecs/openSSH.js create mode 100644 plugins/alibaba/ecs/openSSH.spec.js diff --git a/collectors/alibaba/collector.js b/collectors/alibaba/collector.js index 49ea8d000c..7b15dcdb52 100644 --- a/collectors/alibaba/collector.js +++ b/collectors/alibaba/collector.js @@ -42,6 +42,12 @@ var calls = { } }, ECS: { + DescribeSecurityGroups: { + property: 'SecurityGroups', + subProperty: 'SecurityGroup', + paginate: 'Pages', + apiVersion: '2014-05-26' + }, DescribeInstances: { property: 'Instances', subProperty: 'Instance', @@ -137,6 +143,14 @@ var postcalls = [ filterValue: ['InstanceId'], resultKey: 'InstanceId', apiVersion: '2014-05-26' + }, + DescribeSecurityGroupAttribute: { + reliesOnService: 'ecs', + reliesOnCall: 'DescribeSecurityGroups', + filterKey: ['SecurityGroupId'], + filterValue: ['SecurityGroupId'], + resultKey: 'SecurityGroupId', + apiVersion: '2014-05-26' } }, RAM: { @@ -221,7 +235,10 @@ var collect = function(AlibabaConfig, settings, callback) { if (!collection[serviceLower][callKey]) collection[serviceLower][callKey] = {}; let callRegions = regions[serviceLower]; - let requestOption = { method: callObj.method || 'POST' }; + let requestOption = { + timeout: 5000, //default 3000 ms + method: callObj.method || 'POST' + }; async.eachLimit(callRegions, helpers.MAX_REGIONS_AT_A_TIME, function(region, regionCb) { if (settings.skip_regions && diff --git a/exports.js b/exports.js index 00c28f4bee..0964c93240 100644 --- a/exports.js +++ b/exports.js @@ -727,6 +727,7 @@ module.exports = { 'logSinksEnabled' : require(__dirname + '/plugins/google/logging/logSinksEnabled.js'), }, alibaba: { + 'openSSH' : require(__dirname + '/plugins/alibaba/ecs/openSSH.js'), 'bucketLoggingEnabled' : require(__dirname + '/plugins/alibaba/oss/bucketLoggingEnabled.js'), 'ossBucketPrivate' : require(__dirname + '/plugins/alibaba/oss/ossBucketPrivate.js'), 'rdsLogDuration' : require(__dirname + '/plugins/alibaba/rds/rdsLogDuration.js'), diff --git a/helpers/alibaba/functions.js b/helpers/alibaba/functions.js index 96e06bacfe..494218f54b 100644 --- a/helpers/alibaba/functions.js +++ b/helpers/alibaba/functions.js @@ -1,3 +1,5 @@ +var helpers = require('../shared.js'); + function defaultRegion(settings) { if (settings.defaultRegion) return settings.defaultRegion; return 'cn-hangzhou'; @@ -8,7 +10,84 @@ function createArn(service, account, resourceType, resourceId, region) { return `arn:acs:${service}:${region}:${account}:${resourceType}/${resourceId}`; } +function findOpenPorts(cache, groups, ports, service, region, results) { + // console.log(JSON.stringify(cache, null, 2)); + var found = false; + + for (var group of groups) { + if (!group.SecurityGroupId) continue; + + var accountId = helpers.addSource(cache, {}, ['sts', 'GetCallerIdentity', defaultRegion, 'data']); + + var resource = createArn('ecs', accountId, 'securitygroup', group.SecurityGroupId, region); + + var describeSecurityGroupAttribute = helpers.addSource(cache, {}, + ['ecs', 'DescribeSecurityGroupAttribute', region, group.SecurityGroupId]); + + if (!describeSecurityGroupAttribute || describeSecurityGroupAttribute.err || !describeSecurityGroupAttribute.data) { + helpers.addResult(results, 3, + `Unable to query security group attributes: ${describeSecurityGroupAttribute}`, region, resource); + continue; + } + + var string; + var openV4Ports = []; + + if (describeSecurityGroupAttribute.data.Permissions && describeSecurityGroupAttribute.data.Permissions.Permission && describeSecurityGroupAttribute.data.Permissions.Permission.length){ + for (var permission of describeSecurityGroupAttribute.data.Permissions.Permission) { + if (permission.Direction && permission.Direction !== 'ingress') continue; + let protocol = permission.IpProtocol.toLowerCase(); + if (permission.SourceCidrIp === '0.0.0.0/0' && ports[protocol]) { + for (var port of ports[protocol]) { + let fromPort = (Number(permission.PortRange.split('/')[0])) ? + Number(permission.PortRange.split('/')[0]) : Number(permission.PortRange); + let toPort = (Number(permission.PortRange.split('/')[1])) ? + Number(permission.PortRange.split('/')[1]) : Number(permission.PortRange); + + if (port.toString().indexOf('-') > -1) { + var rangeFrom = Number(port.split('-')[0]); + var rangeTo = Number(port.split('-')[1]); + + for (let i = rangeFrom; i <= rangeTo; i++) { + if (fromPort<= i && toPort >= i) { + string = `some of ${permission.IpProtocol}:${port}`; + openV4Ports.push(string); + found = true; + break; + } + } + } else { + port = Number(port); + if (fromPort <= port && toPort >= port) { + string = `${permission.IpProtocol}:${port}`; + if (openV4Ports.indexOf(string) === -1) openV4Ports.push(string); + found = true; + } + } + } + } + } + } + + if (openV4Ports.length) { + var resultsString = ''; + if (openV4Ports.length) { + resultsString = `Security group: ${group.SecurityGroupId} has ${service}:${openV4Ports.join(', ')} open to 0.0.0.0/0`; + } + + helpers.addResult(results, 2, resultsString, region, resource); + } + } + + if (!found) { + helpers.addResult(results, 0, 'No public open ports found', region); + } + + return; +} + module.exports = { defaultRegion: defaultRegion, - createArn: createArn + createArn: createArn, + findOpenPorts: findOpenPorts }; \ No newline at end of file diff --git a/plugins/alibaba/ecs/openSSH.js b/plugins/alibaba/ecs/openSSH.js new file mode 100644 index 0000000000..6c8a025558 --- /dev/null +++ b/plugins/alibaba/ecs/openSSH.js @@ -0,0 +1,48 @@ +var async = require('async'); +var helpers = require('../../../helpers/alibaba'); + +module.exports = { + title: 'Open SSH', + category: 'ECS', + description: 'Ensure that security groups does not have TCP port 22 for SSH open to the public.', + more_info: 'While some ports such as HTTP and HTTPS are required to be open to the public to function properly, more sensitive services such as SSH should be restricted to known IP addresses.', + link: 'https://www.alibabacloud.com/help/doc-detail/25471.htm', + recommended_action: 'Restrict TCP port 22 to known IP addresses', + apis: ['ECS:DescribeSecurityGroups', 'ECS:DescribeSecurityGroupAttribute', 'STS:GetCallerIdentity'], + + run: function(cache, settings, callback) { + // console.log(JSON.stringify(cache, null, 2)); + var results = []; + var source = {}; + var regions = helpers.regions(settings); + + var ports = { + 'tcp': [22] + }; + + var service = 'SSH'; + + async.each(regions.ecs, function(region, rcb){ + var describeSecurityGroups = helpers.addSource(cache, source, + ['ecs', 'DescribeSecurityGroups', region]); + + if (!describeSecurityGroups) return rcb(); + + if (describeSecurityGroups.err || !describeSecurityGroups.data) { + helpers.addResult(results, 3, + `Unable to describe security groups: ${helpers.addError(describeSecurityGroups)}`, region); + return rcb(); + } + + if (!describeSecurityGroups.data.length) { + helpers.addResult(results, 0, 'No security groups found', region); + return rcb(); + } + + helpers.findOpenPorts(cache, describeSecurityGroups.data, ports, service, region, results); + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/alibaba/ecs/openSSH.spec.js b/plugins/alibaba/ecs/openSSH.spec.js new file mode 100644 index 0000000000..8c3db06367 --- /dev/null +++ b/plugins/alibaba/ecs/openSSH.spec.js @@ -0,0 +1,145 @@ +var expect = require('chai').expect; +const openSSH = require('./openSSH'); + +const describeSecurityGroups = [ + { + "Description": "System created security group.", + "SecurityGroupName": "sg-rj998kwpxbxh3muao6nx", + "VpcId": "vpc-rj9vu86hdve3qr173ew17", + "ServiceManaged": false, + "ResourceGroupId": "", + "SecurityGroupId": "sg-rj998kwpxbxh3muao6nx", + "CreationTime": "2021-04-30T09:57:23Z", + "SecurityGroupType": "normal", + "Tags": { + "Tag": [] + } + } +]; + +const describeSecurityGroupAttribute = [ + { + "Description": "System created security group.", + "RequestId": "B417712F-F2D9-4D84-9E14-53642866EC41", + "SecurityGroupName": "sg-rj998kwpxbxh3muao6nx", + "VpcId": "vpc-rj9vu86hdve3qr173ew17", + "SecurityGroupId": "sg-rj998kwpxbxh3muao6nx", + "Permissions": { + "Permission": [ + { + "Direction": "ingress", + "SourceGroupName": "", + "PortRange": "443/443", + "SourceCidrIp": "0.0.0.0/0", + "IpProtocol": "TCP" + } + ] + } + }, + { + "Description": "System created security group.", + "RequestId": "BCC3A7D9-93A5-44AA-85C1-A0C94A53DDBD", + "SecurityGroupName": "sg-0xijcm5n3s67cgnlklmi", + "VpcId": "vpc-0xitjib9awrnrv6i3sk9y", + "SecurityGroupId": "sg-0xijcm5n3s67cgnlklmi", + "Permissions": { + "Permission": [ + { + "SourceGroupId": "", + "Policy": "Accept", + "Description": "System created rule.", + "SourcePortRange": "", + "Priority": 100, + "CreateTime": "2021-04-29T22:40:41Z", + "DestPrefixListName": "", + "Ipv6SourceCidrIp": "", + "NicType": "intranet", + "DestGroupId": "", + "Direction": "ingress", + "SourceGroupName": "", + "PortRange": "22/22", + "DestGroupOwnerAccount": "", + "DestPrefixListId": "", + "SourceCidrIp": "0.0.0.0/0", + "SourcePrefixListName": "", + "IpProtocol": "TCP", + "DestCidrIp": "", + "DestGroupName": "", + "SourceGroupOwnerAccount": "", + "Ipv6DestCidrIp": "", + "SourcePrefixListId": "" + }, + ] + } + } +]; + +const createCache = (securityGroups, describeSecurityGroupAttribute, securityGroupsErr, describeSecurityGroupAttributeErr) => { + const securityGroupId = (securityGroups && securityGroups.length) ? securityGroups[0].SecurityGroupId : null; + return { + ecs:{ + DescribeSecurityGroups: { + 'cn-hangzhou': { + err: securityGroupsErr, + data: securityGroups + } + }, + DescribeSecurityGroupAttribute: { + 'cn-hangzhou': { + [securityGroupId]: { + err: describeSecurityGroupAttributeErr, + data: describeSecurityGroupAttribute + } + } + } + } + }; +}; + +describe('openSSH', function () { + describe('run', function () { + it('should PASS if no public open ports found', function (done) { + const cache = createCache(describeSecurityGroups, describeSecurityGroupAttribute[0]); + openSSH.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No public open ports found'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should FAIL if security group has SSH TCP 22 port open to public', function (done) { + const cache = createCache(describeSecurityGroups, describeSecurityGroupAttribute[1]); + openSSH.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('has SSH:TCP:22 open to 0.0.0.0/0'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should PASS if no security groups found', function (done) { + const cache = createCache([]); + openSSH.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No security groups found'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + + it('should UNKNWON unable to describe security groups', function (done) { + const cache = createCache(null, { message: 'Unable to describe security groups'}); + openSSH.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 describe security groups'); + expect(results[0].region).to.equal('cn-hangzhou'); + done(); + }); + }); + }); +});