From e1708af1bb8d44dbef05b9f495c4e276158238fb Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 13 Aug 2020 00:34:00 +0500 Subject: [PATCH 01/71] SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation --- collectors/aws/collector.js | 6 + exports.js | 388 +++++++++--------- .../aws/cloudformation/plainTextParameters.js | 70 ++++ 3 files changed, 270 insertions(+), 194 deletions(-) create mode 100644 plugins/aws/cloudformation/plainTextParameters.js diff --git a/collectors/aws/collector.js b/collectors/aws/collector.js index be291cf944..fe386d0b27 100644 --- a/collectors/aws/collector.js +++ b/collectors/aws/collector.js @@ -60,6 +60,12 @@ var calls = { } } }, + CloudFormation: { + describeStacks: { + property: 'Stacks', + paginate: 'NextToken' + } + }, CloudFront: { // TODO: Pagination is using an older format listDistributions: { diff --git a/exports.js b/exports.js index 912d562838..f8dac891a1 100644 --- a/exports.js +++ b/exports.js @@ -2,200 +2,200 @@ module.exports = { aws : { - 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), - 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), - 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), - 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), - 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), - 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), - 'secureOrigin' : require(__dirname + '/plugins/aws/cloudfront/secureOrigin.js'), - 'insecureProtocols' : require(__dirname + '/plugins/aws/cloudfront/insecureProtocols.js'), - 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), - 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), - 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), - - 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), - 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), - 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), - 'cloudtrailEncryption' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEncryption.js'), - 'cloudtrailFileValidation' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailFileValidation.js'), - 'cloudtrailToCloudwatch' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailToCloudwatch.js'), - 'cloudtrailBucketPrivate' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketPrivate.js'), - - 'configServiceEnabled' : require(__dirname + '/plugins/aws/configservice/configServiceEnabled.js'), - - 'dmsEncryptionEnabled' : require(__dirname + '/plugins/aws/dms/dmsEncryptionEnabled.js'), - - 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), - - 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), - 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), - 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), - 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), - 'instanceLimit' : require(__dirname + '/plugins/aws/ec2/instanceLimit.js'), - 'instanceVcpusLimit' : require(__dirname + '/plugins/aws/ec2/instanceVcpusLimit.js'), - 'instanceMaxCount' : require(__dirname + '/plugins/aws/ec2/instanceMaxCount.js'), - 'instanceKeyBasedLogin' : require(__dirname + '/plugins/aws/ec2/instanceKeyBasedLogin.js'), - 'openAllPortsProtocols' : require(__dirname + '/plugins/aws/ec2/openAllPortsProtocols.js'), - 'openCIFS' : require(__dirname + '/plugins/aws/ec2/openCIFS.js'), - 'openDNS' : require(__dirname + '/plugins/aws/ec2/openDNS.js'), - 'openDocker' : require(__dirname + '/plugins/aws/ec2/openDocker.js'), - 'openFTP' : require(__dirname + '/plugins/aws/ec2/openFTP.js'), - 'openHadoopNameNode' : require(__dirname + '/plugins/aws/ec2/openHadoopNameNode.js'), - 'openHadoopNameNodeWebUI' : require(__dirname + '/plugins/aws/ec2/openHadoopNameNodeWebUI.js'), - 'openKibana' : require(__dirname + '/plugins/aws/ec2/openKibana.js'), - 'openMySQL' : require(__dirname + '/plugins/aws/ec2/openMySQL.js'), - 'openOracle' : require(__dirname + '/plugins/aws/ec2/openOracle.js'), - 'openNetBIOS' : require(__dirname + '/plugins/aws/ec2/openNetBIOS.js'), - 'openPostgreSQL' : require(__dirname + '/plugins/aws/ec2/openPostgreSQL.js'), - 'openRDP' : require(__dirname + '/plugins/aws/ec2/openRDP.js'), - 'openRPC' : require(__dirname + '/plugins/aws/ec2/openRPC.js'), - 'openSalt' : require(__dirname + '/plugins/aws/ec2/openSalt.js'), - 'openSMBoTCP' : require(__dirname + '/plugins/aws/ec2/openSMBoTCP.js'), - 'openSMTP' : require(__dirname + '/plugins/aws/ec2/openSMTP.js'), - 'openSQLServer' : require(__dirname + '/plugins/aws/ec2/openSQLServer.js'), - 'openSSH' : require(__dirname + '/plugins/aws/ec2/openSSH.js'), - 'openTelnet' : require(__dirname + '/plugins/aws/ec2/openTelnet.js'), - 'openVNCClient' : require(__dirname + '/plugins/aws/ec2/openVNCClient.js'), - 'openVNCServer' : require(__dirname + '/plugins/aws/ec2/openVNCServer.js'), - 'openElasticsearch' : require(__dirname + '/plugins/aws/ec2/openElasticsearch.js'), - 'vpcElasticIpLimit' : require(__dirname + '/plugins/aws/ec2/vpcElasticIpLimit.js'), - 'classicInstances' : require(__dirname + '/plugins/aws/ec2/classicInstances.js'), - 'flowLogsEnabled' : require(__dirname + '/plugins/aws/ec2/flowLogsEnabled.js'), - 'vpcMultipleSubnets' : require(__dirname + '/plugins/aws/ec2/multipleSubnets.js'), - 'overlappingSecurityGroups' : require(__dirname + '/plugins/aws/ec2/overlappingSecurityGroups.js'), - 'publicAmi' : require(__dirname + '/plugins/aws/ec2/publicAmi.js'), - 'encryptedAmi' : require(__dirname + '/plugins/aws/ec2/encryptedAmi.js'), - 'instanceIamRole' : require(__dirname + '/plugins/aws/ec2/instanceIamRole.js'), - 'ebsEncryptionEnabled' : require(__dirname + '/plugins/aws/ec2/ebsEncryptionEnabled.js'), - 'ebsSnapshotPrivate' : require(__dirname + '/plugins/aws/ec2/ebsSnapshotPrivate.js'), - 'natMultiAz' : require(__dirname + '/plugins/aws/ec2/natMultiAz.js'), - 'defaultVpcInUse' : require(__dirname + '/plugins/aws/ec2/defaultVpcInUse.js'), - 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), - 'crossVpcPublicPrivate' : require(__dirname + '/plugins/aws/ec2/crossVpcPublicPrivate.js'), - 'ebsEncryptedSnapshots' : require(__dirname + '/plugins/aws/ec2/ebsEncryptedSnapshots.js'), - 'ec2MetadataOptions' : require(__dirname + '/plugins/aws/ec2/ec2MetadataOptions.js'), - - 'efsEncryptionEnabled' : require(__dirname + '/plugins/aws/efs/efsEncryptionEnabled.js'), - - 'ecrRepositoryPolicy' : require(__dirname + '/plugins/aws/ecr/ecrRepositoryPolicy.js'), - 'ecrRepositoryTagImmutability' : require(__dirname + '/plugins/aws/ecr/ecrRepositoryTagImmutability.js'), - - 'eksKubernetesVersion' : require(__dirname + '/plugins/aws/eks/eksKubernetesVersion.js'), - 'eksLoggingEnabled' : require(__dirname + '/plugins/aws/eks/eksLoggingEnabled.js'), - 'eksPrivateEndpoint' : require(__dirname + '/plugins/aws/eks/eksPrivateEndpoint.js'), - 'eksSecurityGroups' : require(__dirname + '/plugins/aws/eks/eksSecurityGroups.js'), - - 'insecureCiphers' : require(__dirname + '/plugins/aws/elb/insecureCiphers.js'), - 'elbHttpsOnly' : require(__dirname + '/plugins/aws/elb/elbHttpsOnly.js'), - 'elbLoggingEnabled' : require(__dirname + '/plugins/aws/elb/elbLoggingEnabled.js'), - 'elbNoInstances' : require(__dirname + '/plugins/aws/elb/elbNoInstances.js'), - - 'elbv2LoggingEnabled' : require(__dirname + '/plugins/aws/elbv2/elbv2LoggingEnabled.js'), - 'elbv2HttpsOnly' : require(__dirname + '/plugins/aws/elbv2/elbv2HttpsOnly.js'), - 'elbv2NoInstances' : require(__dirname + '/plugins/aws/elbv2/elbv2NoInstances.js'), - 'elbv2WafEnabled' : require(__dirname + '/plugins/aws/elbv2/elbv2WafEnabled.js'), - - 'esPublicEndpoint' : require(__dirname + '/plugins/aws/es/esPublicEndpoint.js'), - 'esRequireIAMAuth' : require(__dirname + '/plugins/aws/es/esRequireIAMAuth.js'), - 'esEncryptedDomain' : require(__dirname + '/plugins/aws/es/esEncryptedDomain.js'), - 'esNodeToNodeEncryption' : require(__dirname + '/plugins/aws/es/esNodeToNodeEncryption.js'), - 'esLoggingEnabled' : require(__dirname + '/plugins/aws/es/esLoggingEnabled.js'), - 'esUpgradeAvailable' : require(__dirname + '/plugins/aws/es/esUpgradeAvailable.js'), - 'esHttpsOnly' : require(__dirname + '/plugins/aws/es/esHttpsOnly.js'), - - 'accessKeysExtra' : require(__dirname + '/plugins/aws/iam/accessKeysExtra.js'), - 'accessKeysLastUsed' : require(__dirname + '/plugins/aws/iam/accessKeysLastUsed.js'), - 'accessKeysRotated' : require(__dirname + '/plugins/aws/iam/accessKeysRotated.js'), - 'certificateExpiry' : require(__dirname + '/plugins/aws/iam/certificateExpiry.js'), - 'emptyGroups' : require(__dirname + '/plugins/aws/iam/emptyGroups.js'), - 'iamUserAdmins' : require(__dirname + '/plugins/aws/iam/iamUserAdmins.js'), - 'iamUserNameRegex' : require(__dirname + '/plugins/aws/iam/iamUserNameRegex.js'), - 'iamRolePolicies' : require(__dirname + '/plugins/aws/iam/iamRolePolicies.js'), - 'maxPasswordAge' : require(__dirname + '/plugins/aws/iam/maxPasswordAge.js'), - 'minPasswordLength' : require(__dirname + '/plugins/aws/iam/minPasswordLength.js'), - 'noUserIamPolicies' : require(__dirname + '/plugins/aws/iam/noUserIamPolicies.js'), - 'passwordExpiration' : require(__dirname + '/plugins/aws/iam/passwordExpiration.js'), - 'passwordRequiresLowercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresLowercase.js'), - 'passwordRequiresNumbers' : require(__dirname + '/plugins/aws/iam/passwordRequiresNumbers.js'), - 'passwordRequiresSymbols' : require(__dirname + '/plugins/aws/iam/passwordRequiresSymbols.js'), - 'passwordRequiresUppercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresUppercase.js'), - 'passwordReusePrevention' : require(__dirname + '/plugins/aws/iam/passwordReusePrevention.js'), - 'rootAccessKeys' : require(__dirname + '/plugins/aws/iam/rootAccessKeys.js'), - 'rootAccountInUse' : require(__dirname + '/plugins/aws/iam/rootAccountInUse.js'), - 'rootHardwareMfa' : require(__dirname + '/plugins/aws/iam/rootHardwareMfa.js'), - 'rootMfaEnabled' : require(__dirname + '/plugins/aws/iam/rootMfaEnabled.js'), - 'sshKeysRotated' : require(__dirname + '/plugins/aws/iam/sshKeysRotated.js'), - 'usersMfaEnabled' : require(__dirname + '/plugins/aws/iam/usersMfaEnabled.js'), - 'usersPasswordAndKeys' : require(__dirname + '/plugins/aws/iam/usersPasswordAndKeys.js'), - 'usersPasswordLastUsed' : require(__dirname + '/plugins/aws/iam/usersPasswordLastUsed.js'), - 'canaryKeysUsed' : require(__dirname + '/plugins/aws/iam/canaryKeysUsed.js'), - 'kinesisEncrypted' : require(__dirname + '/plugins/aws/kinesis/kinesisEncrypted.js'), - 'firehoseEncrypted' : require(__dirname + '/plugins/aws/firehose/firehoseEncrypted.js'), - 'kmsKeyRotation' : require(__dirname + '/plugins/aws/kms/kmsKeyRotation.js'), - 'kmsScheduledDeletion' : require(__dirname + '/plugins/aws/kms/kmsScheduledDeletion.js'), - 'kmsKeyPolicy' : require(__dirname + '/plugins/aws/kms/kmsKeyPolicy.js'), - 'kmsDefaultKeyUsage' : require(__dirname + '/plugins/aws/kms/kmsDefaultKeyUsage.js'), - - 'rdsAutomatedBackups' : require(__dirname + '/plugins/aws/rds/rdsAutomatedBackups.js'), - 'rdsEncryptionEnabled' : require(__dirname + '/plugins/aws/rds/rdsEncryptionEnabled.js'), - 'rdsLoggingEnabled' : require(__dirname + '/plugins/aws/rds/rdsLoggingEnabled.js'), - 'rdsPubliclyAccessible' : require(__dirname + '/plugins/aws/rds/rdsPubliclyAccessible.js'), - 'rdsRestorable' : require(__dirname + '/plugins/aws/rds/rdsRestorable.js'), - 'rdsMultiAz' : require(__dirname + '/plugins/aws/rds/rdsMultiAz.js'), - 'rdsSnapshotEncryption' : require(__dirname + '/plugins/aws/rds/rdsSnapshotEncryption.js'), - 'rdsMinorVersionUpgrade' : require(__dirname + '/plugins/aws/rds/rdsMinorVersionUpgrade.js'), - - 'domainAutoRenew' : require(__dirname + '/plugins/aws/route53/domainAutoRenew.js'), - 'domainExpiry' : require(__dirname + '/plugins/aws/route53/domainExpiry.js'), - 'domainTransferLock' : require(__dirname + '/plugins/aws/route53/domainTransferLock.js'), - - 'bucketEncryptionInTransit' : require(__dirname + '/plugins/aws/s3/bucketEncryptionInTransit.js'), - 'bucketAllUsersPolicy' : require(__dirname + '/plugins/aws/s3/bucketAllUsersPolicy.js'), - 'bucketAllUsersAcl' : require(__dirname + '/plugins/aws/s3/bucketAllUsersAcl.js'), - 'bucketVersioning' : require(__dirname + '/plugins/aws/s3/bucketVersioning.js'), - 'bucketLogging' : require(__dirname + '/plugins/aws/s3/bucketLogging.js'), - 's3Encryption' : require(__dirname + '/plugins/aws/s3/s3Encryption.js'), - 'bucketPublicAccessBlock' : require(__dirname + '/plugins/aws/s3/bucketPublicAccessBlock.js'), - 'bucketEncryption' : require(__dirname + '/plugins/aws/s3/bucketEncryption.js'), - 'bucketWebsiteEnabled' : require(__dirname + '/plugins/aws/s3/bucketWebsiteEnabled.js'), - 'bucketEnforceEncryption' : require(__dirname + '/plugins/aws/s3/bucketEnforceEncryption.js'), - - 'notebookDataEncrypted' : require(__dirname + '/plugins/aws/sagemaker/notebookDataEncrypted.js'), - 'notebookDirectInternetAccess' : require(__dirname + '/plugins/aws/sagemaker/notebookDirectInternetAccess.js'), - - 'dkimEnabled' : require(__dirname + '/plugins/aws/ses/dkimEnabled.js'), - - 'topicPolicies' : require(__dirname + '/plugins/aws/sns/topicPolicies.js'), - 'sqsCrossAccount' : require(__dirname + '/plugins/aws/sqs/sqsCrossAccount.js'), - 'sqsEncrypted' : require(__dirname + '/plugins/aws/sqs/sqsEncrypted.js'), - - 'ssmEncryptedParameters' : require(__dirname + '/plugins/aws/ssm/ssmEncryptedParameters.js'), - 'ssmActiveOnAllInstances' : require(__dirname + '/plugins/aws/ssm/ssmActiveOnAllInstances.js'), - 'ssmAgentLatestVersion' : require(__dirname + '/plugins/aws/ssm/ssmAgentLatestVersion.js'), - - 'lambdaOldRuntimes' : require(__dirname + '/plugins/aws/lambda/lambdaOldRuntimes.js'), - 'lambdaVpcConfig' : require(__dirname + '/plugins/aws/lambda/lambdaVpcConfig.js'), - 'lambdaPublicAccess' : require(__dirname + '/plugins/aws/lambda/lambdaPublicAccess.js'), - 'lambdaLogGroups' : require(__dirname + '/plugins/aws/lambda/lambdaLogGroups.js'), - - 'monitoringMetrics' : require(__dirname + '/plugins/aws/cloudwatchlogs/monitoringMetrics.js'), - - 'redshiftEncryptionEnabled' : require(__dirname + '/plugins/aws/redshift/redshiftEncryptionEnabled.js'), - 'redshiftPubliclyAccessible' : require(__dirname + '/plugins/aws/redshift/redshiftPubliclyAccessible.js'), - - 'transferLoggingEnabled' : require(__dirname + '/plugins/aws/transfer/transferLoggingEnabled.js'), - - 'shieldAdvancedEnabled' : require(__dirname + '/plugins/aws/shield/shieldAdvancedEnabled.js'), - 'shieldEmergencyContacts' : require(__dirname + '/plugins/aws/shield/shieldEmergencyContacts.js'), - 'shieldProtections' : require(__dirname + '/plugins/aws/shield/shieldProtections.js'), - - 'enableAllFeatures' : require(__dirname + '/plugins/aws/organizations/enableAllFeatures.js'), - 'organizationInvite' : require(__dirname + '/plugins/aws/organizations/organizationInvite.js'), - 'guardDutyEnabled' : require(__dirname + '/plugins/aws/guardduty/guarddutyEnabled.js'), - 'guardDutyMaster' : require(__dirname + '/plugins/aws/guardduty/guarddutyMaster.js'), - - 'xrayEncryptionEnabled' : require(__dirname + '/plugins/aws/xray/xrayEncryptionEnabled.js') + // 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), + // 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), + // 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), + // 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), + // 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), + // 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), + // 'secureOrigin' : require(__dirname + '/plugins/aws/cloudfront/secureOrigin.js'), + // 'insecureProtocols' : require(__dirname + '/plugins/aws/cloudfront/insecureProtocols.js'), + // 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), + // 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), + // 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), + 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), + // 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), + // 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), + // 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), + // 'cloudtrailEncryption' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEncryption.js'), + // 'cloudtrailFileValidation' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailFileValidation.js'), + // 'cloudtrailToCloudwatch' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailToCloudwatch.js'), + // 'cloudtrailBucketPrivate' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketPrivate.js'), + + // 'configServiceEnabled' : require(__dirname + '/plugins/aws/configservice/configServiceEnabled.js'), + + // 'dmsEncryptionEnabled' : require(__dirname + '/plugins/aws/dms/dmsEncryptionEnabled.js'), + + // 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), + + // 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), + // 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), + // 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), + // 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), + // 'instanceLimit' : require(__dirname + '/plugins/aws/ec2/instanceLimit.js'), + // 'instanceVcpusLimit' : require(__dirname + '/plugins/aws/ec2/instanceVcpusLimit.js'), + // 'instanceMaxCount' : require(__dirname + '/plugins/aws/ec2/instanceMaxCount.js'), + // 'instanceKeyBasedLogin' : require(__dirname + '/plugins/aws/ec2/instanceKeyBasedLogin.js'), + // 'openAllPortsProtocols' : require(__dirname + '/plugins/aws/ec2/openAllPortsProtocols.js'), + // 'openCIFS' : require(__dirname + '/plugins/aws/ec2/openCIFS.js'), + // 'openDNS' : require(__dirname + '/plugins/aws/ec2/openDNS.js'), + // 'openDocker' : require(__dirname + '/plugins/aws/ec2/openDocker.js'), + // 'openFTP' : require(__dirname + '/plugins/aws/ec2/openFTP.js'), + // 'openHadoopNameNode' : require(__dirname + '/plugins/aws/ec2/openHadoopNameNode.js'), + // 'openHadoopNameNodeWebUI' : require(__dirname + '/plugins/aws/ec2/openHadoopNameNodeWebUI.js'), + // 'openKibana' : require(__dirname + '/plugins/aws/ec2/openKibana.js'), + // 'openMySQL' : require(__dirname + '/plugins/aws/ec2/openMySQL.js'), + // 'openOracle' : require(__dirname + '/plugins/aws/ec2/openOracle.js'), + // 'openNetBIOS' : require(__dirname + '/plugins/aws/ec2/openNetBIOS.js'), + // 'openPostgreSQL' : require(__dirname + '/plugins/aws/ec2/openPostgreSQL.js'), + // 'openRDP' : require(__dirname + '/plugins/aws/ec2/openRDP.js'), + // 'openRPC' : require(__dirname + '/plugins/aws/ec2/openRPC.js'), + // 'openSalt' : require(__dirname + '/plugins/aws/ec2/openSalt.js'), + // 'openSMBoTCP' : require(__dirname + '/plugins/aws/ec2/openSMBoTCP.js'), + // 'openSMTP' : require(__dirname + '/plugins/aws/ec2/openSMTP.js'), + // 'openSQLServer' : require(__dirname + '/plugins/aws/ec2/openSQLServer.js'), + // 'openSSH' : require(__dirname + '/plugins/aws/ec2/openSSH.js'), + // 'openTelnet' : require(__dirname + '/plugins/aws/ec2/openTelnet.js'), + // 'openVNCClient' : require(__dirname + '/plugins/aws/ec2/openVNCClient.js'), + // 'openVNCServer' : require(__dirname + '/plugins/aws/ec2/openVNCServer.js'), + // 'openElasticsearch' : require(__dirname + '/plugins/aws/ec2/openElasticsearch.js'), + // 'vpcElasticIpLimit' : require(__dirname + '/plugins/aws/ec2/vpcElasticIpLimit.js'), + // 'classicInstances' : require(__dirname + '/plugins/aws/ec2/classicInstances.js'), + // 'flowLogsEnabled' : require(__dirname + '/plugins/aws/ec2/flowLogsEnabled.js'), + // 'vpcMultipleSubnets' : require(__dirname + '/plugins/aws/ec2/multipleSubnets.js'), + // 'overlappingSecurityGroups' : require(__dirname + '/plugins/aws/ec2/overlappingSecurityGroups.js'), + // 'publicAmi' : require(__dirname + '/plugins/aws/ec2/publicAmi.js'), + // 'encryptedAmi' : require(__dirname + '/plugins/aws/ec2/encryptedAmi.js'), + // 'instanceIamRole' : require(__dirname + '/plugins/aws/ec2/instanceIamRole.js'), + // 'ebsEncryptionEnabled' : require(__dirname + '/plugins/aws/ec2/ebsEncryptionEnabled.js'), + // 'ebsSnapshotPrivate' : require(__dirname + '/plugins/aws/ec2/ebsSnapshotPrivate.js'), + // 'natMultiAz' : require(__dirname + '/plugins/aws/ec2/natMultiAz.js'), + // 'defaultVpcInUse' : require(__dirname + '/plugins/aws/ec2/defaultVpcInUse.js'), + // 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), + // 'crossVpcPublicPrivate' : require(__dirname + '/plugins/aws/ec2/crossVpcPublicPrivate.js'), + // 'ebsEncryptedSnapshots' : require(__dirname + '/plugins/aws/ec2/ebsEncryptedSnapshots.js'), + // 'ec2MetadataOptions' : require(__dirname + '/plugins/aws/ec2/ec2MetadataOptions.js'), + + // 'efsEncryptionEnabled' : require(__dirname + '/plugins/aws/efs/efsEncryptionEnabled.js'), + + // 'ecrRepositoryPolicy' : require(__dirname + '/plugins/aws/ecr/ecrRepositoryPolicy.js'), + // 'ecrRepositoryTagImmutability' : require(__dirname + '/plugins/aws/ecr/ecrRepositoryTagImmutability.js'), + + // 'eksKubernetesVersion' : require(__dirname + '/plugins/aws/eks/eksKubernetesVersion.js'), + // 'eksLoggingEnabled' : require(__dirname + '/plugins/aws/eks/eksLoggingEnabled.js'), + // 'eksPrivateEndpoint' : require(__dirname + '/plugins/aws/eks/eksPrivateEndpoint.js'), + // 'eksSecurityGroups' : require(__dirname + '/plugins/aws/eks/eksSecurityGroups.js'), + + // 'insecureCiphers' : require(__dirname + '/plugins/aws/elb/insecureCiphers.js'), + // 'elbHttpsOnly' : require(__dirname + '/plugins/aws/elb/elbHttpsOnly.js'), + // 'elbLoggingEnabled' : require(__dirname + '/plugins/aws/elb/elbLoggingEnabled.js'), + // 'elbNoInstances' : require(__dirname + '/plugins/aws/elb/elbNoInstances.js'), + + // 'elbv2LoggingEnabled' : require(__dirname + '/plugins/aws/elbv2/elbv2LoggingEnabled.js'), + // 'elbv2HttpsOnly' : require(__dirname + '/plugins/aws/elbv2/elbv2HttpsOnly.js'), + // 'elbv2NoInstances' : require(__dirname + '/plugins/aws/elbv2/elbv2NoInstances.js'), + // 'elbv2WafEnabled' : require(__dirname + '/plugins/aws/elbv2/elbv2WafEnabled.js'), + + // 'esPublicEndpoint' : require(__dirname + '/plugins/aws/es/esPublicEndpoint.js'), + // 'esRequireIAMAuth' : require(__dirname + '/plugins/aws/es/esRequireIAMAuth.js'), + // 'esEncryptedDomain' : require(__dirname + '/plugins/aws/es/esEncryptedDomain.js'), + // 'esNodeToNodeEncryption' : require(__dirname + '/plugins/aws/es/esNodeToNodeEncryption.js'), + // 'esLoggingEnabled' : require(__dirname + '/plugins/aws/es/esLoggingEnabled.js'), + // 'esUpgradeAvailable' : require(__dirname + '/plugins/aws/es/esUpgradeAvailable.js'), + // 'esHttpsOnly' : require(__dirname + '/plugins/aws/es/esHttpsOnly.js'), + + // 'accessKeysExtra' : require(__dirname + '/plugins/aws/iam/accessKeysExtra.js'), + // 'accessKeysLastUsed' : require(__dirname + '/plugins/aws/iam/accessKeysLastUsed.js'), + // 'accessKeysRotated' : require(__dirname + '/plugins/aws/iam/accessKeysRotated.js'), + // 'certificateExpiry' : require(__dirname + '/plugins/aws/iam/certificateExpiry.js'), + // 'emptyGroups' : require(__dirname + '/plugins/aws/iam/emptyGroups.js'), + // 'iamUserAdmins' : require(__dirname + '/plugins/aws/iam/iamUserAdmins.js'), + // 'iamUserNameRegex' : require(__dirname + '/plugins/aws/iam/iamUserNameRegex.js'), + // 'iamRolePolicies' : require(__dirname + '/plugins/aws/iam/iamRolePolicies.js'), + // 'maxPasswordAge' : require(__dirname + '/plugins/aws/iam/maxPasswordAge.js'), + // 'minPasswordLength' : require(__dirname + '/plugins/aws/iam/minPasswordLength.js'), + // 'noUserIamPolicies' : require(__dirname + '/plugins/aws/iam/noUserIamPolicies.js'), + // 'passwordExpiration' : require(__dirname + '/plugins/aws/iam/passwordExpiration.js'), + // 'passwordRequiresLowercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresLowercase.js'), + // 'passwordRequiresNumbers' : require(__dirname + '/plugins/aws/iam/passwordRequiresNumbers.js'), + // 'passwordRequiresSymbols' : require(__dirname + '/plugins/aws/iam/passwordRequiresSymbols.js'), + // 'passwordRequiresUppercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresUppercase.js'), + // 'passwordReusePrevention' : require(__dirname + '/plugins/aws/iam/passwordReusePrevention.js'), + // 'rootAccessKeys' : require(__dirname + '/plugins/aws/iam/rootAccessKeys.js'), + // 'rootAccountInUse' : require(__dirname + '/plugins/aws/iam/rootAccountInUse.js'), + // 'rootHardwareMfa' : require(__dirname + '/plugins/aws/iam/rootHardwareMfa.js'), + // 'rootMfaEnabled' : require(__dirname + '/plugins/aws/iam/rootMfaEnabled.js'), + // 'sshKeysRotated' : require(__dirname + '/plugins/aws/iam/sshKeysRotated.js'), + // 'usersMfaEnabled' : require(__dirname + '/plugins/aws/iam/usersMfaEnabled.js'), + // 'usersPasswordAndKeys' : require(__dirname + '/plugins/aws/iam/usersPasswordAndKeys.js'), + // 'usersPasswordLastUsed' : require(__dirname + '/plugins/aws/iam/usersPasswordLastUsed.js'), + // 'canaryKeysUsed' : require(__dirname + '/plugins/aws/iam/canaryKeysUsed.js'), + // 'kinesisEncrypted' : require(__dirname + '/plugins/aws/kinesis/kinesisEncrypted.js'), + // 'firehoseEncrypted' : require(__dirname + '/plugins/aws/firehose/firehoseEncrypted.js'), + // 'kmsKeyRotation' : require(__dirname + '/plugins/aws/kms/kmsKeyRotation.js'), + // 'kmsScheduledDeletion' : require(__dirname + '/plugins/aws/kms/kmsScheduledDeletion.js'), + // 'kmsKeyPolicy' : require(__dirname + '/plugins/aws/kms/kmsKeyPolicy.js'), + // 'kmsDefaultKeyUsage' : require(__dirname + '/plugins/aws/kms/kmsDefaultKeyUsage.js'), + + // 'rdsAutomatedBackups' : require(__dirname + '/plugins/aws/rds/rdsAutomatedBackups.js'), + // 'rdsEncryptionEnabled' : require(__dirname + '/plugins/aws/rds/rdsEncryptionEnabled.js'), + // 'rdsLoggingEnabled' : require(__dirname + '/plugins/aws/rds/rdsLoggingEnabled.js'), + // 'rdsPubliclyAccessible' : require(__dirname + '/plugins/aws/rds/rdsPubliclyAccessible.js'), + // 'rdsRestorable' : require(__dirname + '/plugins/aws/rds/rdsRestorable.js'), + // 'rdsMultiAz' : require(__dirname + '/plugins/aws/rds/rdsMultiAz.js'), + // 'rdsSnapshotEncryption' : require(__dirname + '/plugins/aws/rds/rdsSnapshotEncryption.js'), + // 'rdsMinorVersionUpgrade' : require(__dirname + '/plugins/aws/rds/rdsMinorVersionUpgrade.js'), + + // 'domainAutoRenew' : require(__dirname + '/plugins/aws/route53/domainAutoRenew.js'), + // 'domainExpiry' : require(__dirname + '/plugins/aws/route53/domainExpiry.js'), + // 'domainTransferLock' : require(__dirname + '/plugins/aws/route53/domainTransferLock.js'), + + // 'bucketEncryptionInTransit' : require(__dirname + '/plugins/aws/s3/bucketEncryptionInTransit.js'), + // 'bucketAllUsersPolicy' : require(__dirname + '/plugins/aws/s3/bucketAllUsersPolicy.js'), + // 'bucketAllUsersAcl' : require(__dirname + '/plugins/aws/s3/bucketAllUsersAcl.js'), + // 'bucketVersioning' : require(__dirname + '/plugins/aws/s3/bucketVersioning.js'), + // 'bucketLogging' : require(__dirname + '/plugins/aws/s3/bucketLogging.js'), + // 's3Encryption' : require(__dirname + '/plugins/aws/s3/s3Encryption.js'), + // 'bucketPublicAccessBlock' : require(__dirname + '/plugins/aws/s3/bucketPublicAccessBlock.js'), + // 'bucketEncryption' : require(__dirname + '/plugins/aws/s3/bucketEncryption.js'), + // 'bucketWebsiteEnabled' : require(__dirname + '/plugins/aws/s3/bucketWebsiteEnabled.js'), + // 'bucketEnforceEncryption' : require(__dirname + '/plugins/aws/s3/bucketEnforceEncryption.js'), + + // 'notebookDataEncrypted' : require(__dirname + '/plugins/aws/sagemaker/notebookDataEncrypted.js'), + // 'notebookDirectInternetAccess' : require(__dirname + '/plugins/aws/sagemaker/notebookDirectInternetAccess.js'), + + // 'dkimEnabled' : require(__dirname + '/plugins/aws/ses/dkimEnabled.js'), + + // 'topicPolicies' : require(__dirname + '/plugins/aws/sns/topicPolicies.js'), + // 'sqsCrossAccount' : require(__dirname + '/plugins/aws/sqs/sqsCrossAccount.js'), + // 'sqsEncrypted' : require(__dirname + '/plugins/aws/sqs/sqsEncrypted.js'), + + // 'ssmEncryptedParameters' : require(__dirname + '/plugins/aws/ssm/ssmEncryptedParameters.js'), + // 'ssmActiveOnAllInstances' : require(__dirname + '/plugins/aws/ssm/ssmActiveOnAllInstances.js'), + // 'ssmAgentLatestVersion' : require(__dirname + '/plugins/aws/ssm/ssmAgentLatestVersion.js'), + + // 'lambdaOldRuntimes' : require(__dirname + '/plugins/aws/lambda/lambdaOldRuntimes.js'), + // 'lambdaVpcConfig' : require(__dirname + '/plugins/aws/lambda/lambdaVpcConfig.js'), + // 'lambdaPublicAccess' : require(__dirname + '/plugins/aws/lambda/lambdaPublicAccess.js'), + // 'lambdaLogGroups' : require(__dirname + '/plugins/aws/lambda/lambdaLogGroups.js'), + + // 'monitoringMetrics' : require(__dirname + '/plugins/aws/cloudwatchlogs/monitoringMetrics.js'), + + // 'redshiftEncryptionEnabled' : require(__dirname + '/plugins/aws/redshift/redshiftEncryptionEnabled.js'), + // 'redshiftPubliclyAccessible' : require(__dirname + '/plugins/aws/redshift/redshiftPubliclyAccessible.js'), + + // 'transferLoggingEnabled' : require(__dirname + '/plugins/aws/transfer/transferLoggingEnabled.js'), + + // 'shieldAdvancedEnabled' : require(__dirname + '/plugins/aws/shield/shieldAdvancedEnabled.js'), + // 'shieldEmergencyContacts' : require(__dirname + '/plugins/aws/shield/shieldEmergencyContacts.js'), + // 'shieldProtections' : require(__dirname + '/plugins/aws/shield/shieldProtections.js'), + + // 'enableAllFeatures' : require(__dirname + '/plugins/aws/organizations/enableAllFeatures.js'), + // 'organizationInvite' : require(__dirname + '/plugins/aws/organizations/organizationInvite.js'), + // 'guardDutyEnabled' : require(__dirname + '/plugins/aws/guardduty/guarddutyEnabled.js'), + // 'guardDutyMaster' : require(__dirname + '/plugins/aws/guardduty/guarddutyMaster.js'), + + // 'xrayEncryptionEnabled' : require(__dirname + '/plugins/aws/xray/xrayEncryptionEnabled.js') }, azure : { 'fileServiceEncryption' : require(__dirname + '/plugins/azure/storageaccounts/fileServiceEncryption.js'), diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js new file mode 100644 index 0000000000..658dc42f3d --- /dev/null +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -0,0 +1,70 @@ +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'CloudFormation Plaintext Parameters', + category: 'CloudFormation', + description: 'Ensures CloudFormation parameters that reference sensitive values are configured to use NoEcho.', + more_info: 'CloudFormation supports the NoEcho property for sensitive values, which should be used to ensure secrets are not exposed in the CloudFormation UI and APIs.', + link: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html', + recommended_action: 'Update the sensitive parameters to use the NoEcho property.', + apis: ['CloudFormation:describeStacks'], + compliance: { + hipaa: 'HIPAA requires all data to be transmitted over secure channels. ' + + 'CloudFront HTTPS redirection should be used to ensure site visitors ' + + 'are always connecting over a secure channel.' + }, + // settings : { secretWords : ["password", "privatekey", "secret"] }, + + run: function(cache, settings, callback) { + + var results = []; + var source = {}; + + var region = helpers.defaultRegion(settings); + + var describeStacks = helpers.addSource(cache, source, + ['cloudformation', 'describeStacks', region]); + + console.log(describeStacks); + console.log("results received"); + // if (!describeStacks) return callback(null, results, source); + + // if (describeStacks.err || !describeStacks.data) { + // helpers.addResult(results, 3, + // 'Unable to describe stacks: ' + helpers.addError(describeStacks)); + // return callback(null, results, source); + // } + + // if (!describeStacks.data.length) { + // helpers.addResult(results, 0, 'No stacks descriptions found'); + // return callback(null, results, source); + // } + // console.log(describeStacks.data); + // loop through stacks for every template retrieval + // describeStacks.data.forEach(function(Distribution){ + // var stackTemplate = helpers.addSource(cache, source, + // ['cloudformation', 'getTemplate', region]); + + // if (!describeStacks) return callback(null, results, source); + + // if (describeStacks.err || !describeStacks.data) { + // helpers.addResult(results, 3, + // 'Unable to describe stacks: ' + helpers.addError(describeStacks)); + // return callback(null, results, source); + // } + + // if (Distribution.DefaultCacheBehavior.ViewerProtocolPolicy == 'redirect-to-https') { + // helpers.addResult(results, 0, 'CloudFront distribution ' + + // 'is configured to redirect non-HTTPS traffic to HTTPS', 'global', Distribution.ARN); + // } else if (Distribution.DefaultCacheBehavior.ViewerProtocolPolicy == 'https-only') { + // helpers.addResult(results, 0, 'The CloudFront ' + + // 'distribution is set to use HTTPS only.', 'global', Distribution.ARN); + // } else { + // helpers.addResult(results, 2, 'CloudFront distribution ' + + // 'is not configured to use HTTPS', 'global', Distribution.ARN); + // } + // }); + + callback(null, results, source); + } +}; \ No newline at end of file From 2122ade6414d7ce065c47a3e5841b9ca454a493a Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Thu, 13 Aug 2020 19:59:59 +0500 Subject: [PATCH 02/71] Added vpcEndpointAcceptance plugin and spec file --- collectors/aws/collector.js | 4 + plugins/aws/ec2/vpcEndpointAcceptance.js | 57 +++++++ plugins/aws/ec2/vpcEndpointAcceptance.spec.js | 141 ++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 plugins/aws/ec2/vpcEndpointAcceptance.js create mode 100644 plugins/aws/ec2/vpcEndpointAcceptance.spec.js diff --git a/collectors/aws/collector.js b/collectors/aws/collector.js index be291cf944..20a75b5269 100644 --- a/collectors/aws/collector.js +++ b/collectors/aws/collector.js @@ -230,6 +230,10 @@ var calls = { ] } }, + describeVpcEndpointServices: { + property: 'ServiceDetails', + paginate: 'NextToken' + }, describeRouteTables: { property: 'RouteTables', paginate: 'NextToken' diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.js b/plugins/aws/ec2/vpcEndpointAcceptance.js new file mode 100644 index 0000000000..9d0530bd7e --- /dev/null +++ b/plugins/aws/ec2/vpcEndpointAcceptance.js @@ -0,0 +1,57 @@ +var async = require('async'); +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'VPC PrivateLink Endpoint Acceptance Required', + category: 'EC2', + description: 'Ensures VPC PrivateLink endpoints require acceptance', + more_info: 'VPC PrivateLink endpoints should be configured to require acceptance so that access to the endpoint is controlled on a case-by-case basis.', + recommended_action: 'Update the VPC PrivateLink endpoint to require acceptance', + link: 'https://docs.aws.amazon.com/vpc/latest/userguide/accept-reject-endpoint-requests.html', + apis: ['EC2:describeVpcEndpointServices'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(settings); + + async.each(regions.ec2, function(region, rcb){ + var describeVpcEndpointServices = helpers.addSource(cache, source, + ['ec2', 'describeVpcEndpointServices', region]); + + if (!describeVpcEndpointServices) return rcb(); + + if (describeVpcEndpointServices.err || !describeVpcEndpointServices.data) { + helpers.addResult(results, 3, + 'Unable to query for VPC endpoint services: ' + helpers.addError(describeVpcEndpointServices), region); + return rcb(); + } + + describeVpcEndpointServices.data = describeVpcEndpointServices.data.filter(service => service.Owner != 'amazon'); + + if (!describeVpcEndpointServices.data.length) { + helpers.addResult(results, 0, + 'No user owned VPC endpoint services present', region); + return rcb(); + } + + for (var s in describeVpcEndpointServices.data) { + var service = describeVpcEndpointServices.data[s]; + var resource = service.ServiceName; + if (service.AcceptanceRequired) { + helpers.addResult(results, 0, + 'VPC endpoint service ' + (service.ServiceId) + ' requires acceptance by the service owner', + region, resource); + } else { + helpers.addResult(results, 2, + 'VPC endpoint service ' + (service.ServiceId) + ' does not require acceptance by the service owner', + region, resource); + } + } + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js new file mode 100644 index 0000000000..0946f5a682 --- /dev/null +++ b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js @@ -0,0 +1,141 @@ +var expect = require('chai').expect; +const vpcEndpointAcceptance = require('./vpcEndpointAcceptance'); + +const vpcEndpointServices = [ + { + "ServiceName": "com.amazonaws.vpce.us-east-1.vpce-svc-09d3a6a098dce6e8c", + "ServiceId": "vpce-svc-09d3a6a098dce6e8c", + "ServiceType": [ + { + "ServiceType": "Interface" + } + ], + "AvailabilityZones": [ + "us-east-1a", + "us-east-1b" + ], + "Owner": "560213429563", + "BaseEndpointDnsNames": [ + "vpce-svc-09d3a6a098dce6e8c.us-east-1.vpce.amazonaws.com" + ], + "VpcEndpointPolicySupported": false, + "AcceptanceRequired": true, + "ManagesVpcEndpoints": false, + "Tags": [] + }, + { + "ServiceName": "com.amazonaws.vpce.us-east-1.vpce-svc-09145867a106679a3", + "ServiceId": "vpce-svc-09145867a106679a3", + "ServiceType": [ + { + "ServiceType": "Interface" + } + ], + "AvailabilityZones": [ + "us-east-1a", + "us-east-1b", + "us-east-1c" + ], + "Owner": "560213429563", + "BaseEndpointDnsNames": [ + "vpce-svc-09145867a106679a3.us-east-1.vpce.amazonaws.com" + ], + "VpcEndpointPolicySupported": false, + "AcceptanceRequired": false, + "ManagesVpcEndpoints": false, + "Tags": [] + }, +] + +const createCache = (ServiceDetails) => { + return { + ec2: { + describeVpcEndpointServices: { + 'us-east-1': { + data: ServiceDetails + }, + }, + }, + }; +}; + +const createErrorCache = () => { + return { + ec2: { + describeVpcEndpointServices: { + 'us-east-1': { + err: { + message: 'error describing VPC endpoint services' + }, + }, + }, + }, + }; +}; + +const createNullCache = () => { + return { + ec2: { + describeVpcEndpointServices: { + 'us-east-1': null, + }, + }, + }; +}; + +describe('vpcEndpointAcceptance', function () { + describe('run', function () { + it('should PASS if VPC endpoint service requires acceptance by the service owner', function (done) { + const cache = createCache([vpcEndpointServices[0]]); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should FAIL if VPC endpoint service does not require acceptance by the service owner', function (done) { + const cache = createCache([vpcEndpointServices[1]]); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + done(); + }); + }); + + it('should PASS if no VPC endpoint service is detected', function (done) { + const cache = createCache([]); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should PASS if no VPC endpoint services are detected', function (done) { + const cache = createCache([]); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should UNKNOWN if there was an error querying for VPC endpoint services', function (done) { + const cache = createErrorCache(); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + it('should not return any results if unable to query for VPC endpoint services', function (done) { + const cache = createNullCache(); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(0); + done(); + }); + }); + }); +}); From 306d7218921e93751176dc09b524f8fa18639608 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Fri, 14 Aug 2020 05:55:55 +0500 Subject: [PATCH 03/71] SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation --- exports.js | 386 +++++++++--------- helpers/aws/regions.js | 1 + index.js | 8 +- .../aws/cloudformation/plainTextParameters.js | 91 ++--- .../plainTextParameters.spec.js | 161 ++++++++ 5 files changed, 406 insertions(+), 241 deletions(-) create mode 100644 plugins/aws/cloudformation/plainTextParameters.spec.js diff --git a/exports.js b/exports.js index f8dac891a1..fa901a2af7 100644 --- a/exports.js +++ b/exports.js @@ -2,200 +2,200 @@ module.exports = { aws : { - // 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), - // 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), - // 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), - // 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), - // 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), - // 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), - // 'secureOrigin' : require(__dirname + '/plugins/aws/cloudfront/secureOrigin.js'), - // 'insecureProtocols' : require(__dirname + '/plugins/aws/cloudfront/insecureProtocols.js'), - // 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), - // 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), - // 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), + 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), + 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), + 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), + 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), + 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), + 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), + 'secureOrigin' : require(__dirname + '/plugins/aws/cloudfront/secureOrigin.js'), + 'insecureProtocols' : require(__dirname + '/plugins/aws/cloudfront/insecureProtocols.js'), + 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), + 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), + 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), - // 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), - // 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), - // 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), - // 'cloudtrailEncryption' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEncryption.js'), - // 'cloudtrailFileValidation' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailFileValidation.js'), - // 'cloudtrailToCloudwatch' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailToCloudwatch.js'), - // 'cloudtrailBucketPrivate' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketPrivate.js'), - - // 'configServiceEnabled' : require(__dirname + '/plugins/aws/configservice/configServiceEnabled.js'), - - // 'dmsEncryptionEnabled' : require(__dirname + '/plugins/aws/dms/dmsEncryptionEnabled.js'), - - // 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), - - // 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), - // 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), - // 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), - // 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), - // 'instanceLimit' : require(__dirname + '/plugins/aws/ec2/instanceLimit.js'), - // 'instanceVcpusLimit' : require(__dirname + '/plugins/aws/ec2/instanceVcpusLimit.js'), - // 'instanceMaxCount' : require(__dirname + '/plugins/aws/ec2/instanceMaxCount.js'), - // 'instanceKeyBasedLogin' : require(__dirname + '/plugins/aws/ec2/instanceKeyBasedLogin.js'), - // 'openAllPortsProtocols' : require(__dirname + '/plugins/aws/ec2/openAllPortsProtocols.js'), - // 'openCIFS' : require(__dirname + '/plugins/aws/ec2/openCIFS.js'), - // 'openDNS' : require(__dirname + '/plugins/aws/ec2/openDNS.js'), - // 'openDocker' : require(__dirname + '/plugins/aws/ec2/openDocker.js'), - // 'openFTP' : require(__dirname + '/plugins/aws/ec2/openFTP.js'), - // 'openHadoopNameNode' : require(__dirname + '/plugins/aws/ec2/openHadoopNameNode.js'), - // 'openHadoopNameNodeWebUI' : require(__dirname + '/plugins/aws/ec2/openHadoopNameNodeWebUI.js'), - // 'openKibana' : require(__dirname + '/plugins/aws/ec2/openKibana.js'), - // 'openMySQL' : require(__dirname + '/plugins/aws/ec2/openMySQL.js'), - // 'openOracle' : require(__dirname + '/plugins/aws/ec2/openOracle.js'), - // 'openNetBIOS' : require(__dirname + '/plugins/aws/ec2/openNetBIOS.js'), - // 'openPostgreSQL' : require(__dirname + '/plugins/aws/ec2/openPostgreSQL.js'), - // 'openRDP' : require(__dirname + '/plugins/aws/ec2/openRDP.js'), - // 'openRPC' : require(__dirname + '/plugins/aws/ec2/openRPC.js'), - // 'openSalt' : require(__dirname + '/plugins/aws/ec2/openSalt.js'), - // 'openSMBoTCP' : require(__dirname + '/plugins/aws/ec2/openSMBoTCP.js'), - // 'openSMTP' : require(__dirname + '/plugins/aws/ec2/openSMTP.js'), - // 'openSQLServer' : require(__dirname + '/plugins/aws/ec2/openSQLServer.js'), - // 'openSSH' : require(__dirname + '/plugins/aws/ec2/openSSH.js'), - // 'openTelnet' : require(__dirname + '/plugins/aws/ec2/openTelnet.js'), - // 'openVNCClient' : require(__dirname + '/plugins/aws/ec2/openVNCClient.js'), - // 'openVNCServer' : require(__dirname + '/plugins/aws/ec2/openVNCServer.js'), - // 'openElasticsearch' : require(__dirname + '/plugins/aws/ec2/openElasticsearch.js'), - // 'vpcElasticIpLimit' : require(__dirname + '/plugins/aws/ec2/vpcElasticIpLimit.js'), - // 'classicInstances' : require(__dirname + '/plugins/aws/ec2/classicInstances.js'), - // 'flowLogsEnabled' : require(__dirname + '/plugins/aws/ec2/flowLogsEnabled.js'), - // 'vpcMultipleSubnets' : require(__dirname + '/plugins/aws/ec2/multipleSubnets.js'), - // 'overlappingSecurityGroups' : require(__dirname + '/plugins/aws/ec2/overlappingSecurityGroups.js'), - // 'publicAmi' : require(__dirname + '/plugins/aws/ec2/publicAmi.js'), - // 'encryptedAmi' : require(__dirname + '/plugins/aws/ec2/encryptedAmi.js'), - // 'instanceIamRole' : require(__dirname + '/plugins/aws/ec2/instanceIamRole.js'), - // 'ebsEncryptionEnabled' : require(__dirname + '/plugins/aws/ec2/ebsEncryptionEnabled.js'), - // 'ebsSnapshotPrivate' : require(__dirname + '/plugins/aws/ec2/ebsSnapshotPrivate.js'), - // 'natMultiAz' : require(__dirname + '/plugins/aws/ec2/natMultiAz.js'), - // 'defaultVpcInUse' : require(__dirname + '/plugins/aws/ec2/defaultVpcInUse.js'), - // 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), - // 'crossVpcPublicPrivate' : require(__dirname + '/plugins/aws/ec2/crossVpcPublicPrivate.js'), - // 'ebsEncryptedSnapshots' : require(__dirname + '/plugins/aws/ec2/ebsEncryptedSnapshots.js'), - // 'ec2MetadataOptions' : require(__dirname + '/plugins/aws/ec2/ec2MetadataOptions.js'), - - // 'efsEncryptionEnabled' : require(__dirname + '/plugins/aws/efs/efsEncryptionEnabled.js'), - - // 'ecrRepositoryPolicy' : require(__dirname + '/plugins/aws/ecr/ecrRepositoryPolicy.js'), - // 'ecrRepositoryTagImmutability' : require(__dirname + '/plugins/aws/ecr/ecrRepositoryTagImmutability.js'), - - // 'eksKubernetesVersion' : require(__dirname + '/plugins/aws/eks/eksKubernetesVersion.js'), - // 'eksLoggingEnabled' : require(__dirname + '/plugins/aws/eks/eksLoggingEnabled.js'), - // 'eksPrivateEndpoint' : require(__dirname + '/plugins/aws/eks/eksPrivateEndpoint.js'), - // 'eksSecurityGroups' : require(__dirname + '/plugins/aws/eks/eksSecurityGroups.js'), - - // 'insecureCiphers' : require(__dirname + '/plugins/aws/elb/insecureCiphers.js'), - // 'elbHttpsOnly' : require(__dirname + '/plugins/aws/elb/elbHttpsOnly.js'), - // 'elbLoggingEnabled' : require(__dirname + '/plugins/aws/elb/elbLoggingEnabled.js'), - // 'elbNoInstances' : require(__dirname + '/plugins/aws/elb/elbNoInstances.js'), - - // 'elbv2LoggingEnabled' : require(__dirname + '/plugins/aws/elbv2/elbv2LoggingEnabled.js'), - // 'elbv2HttpsOnly' : require(__dirname + '/plugins/aws/elbv2/elbv2HttpsOnly.js'), - // 'elbv2NoInstances' : require(__dirname + '/plugins/aws/elbv2/elbv2NoInstances.js'), - // 'elbv2WafEnabled' : require(__dirname + '/plugins/aws/elbv2/elbv2WafEnabled.js'), - - // 'esPublicEndpoint' : require(__dirname + '/plugins/aws/es/esPublicEndpoint.js'), - // 'esRequireIAMAuth' : require(__dirname + '/plugins/aws/es/esRequireIAMAuth.js'), - // 'esEncryptedDomain' : require(__dirname + '/plugins/aws/es/esEncryptedDomain.js'), - // 'esNodeToNodeEncryption' : require(__dirname + '/plugins/aws/es/esNodeToNodeEncryption.js'), - // 'esLoggingEnabled' : require(__dirname + '/plugins/aws/es/esLoggingEnabled.js'), - // 'esUpgradeAvailable' : require(__dirname + '/plugins/aws/es/esUpgradeAvailable.js'), - // 'esHttpsOnly' : require(__dirname + '/plugins/aws/es/esHttpsOnly.js'), - - // 'accessKeysExtra' : require(__dirname + '/plugins/aws/iam/accessKeysExtra.js'), - // 'accessKeysLastUsed' : require(__dirname + '/plugins/aws/iam/accessKeysLastUsed.js'), - // 'accessKeysRotated' : require(__dirname + '/plugins/aws/iam/accessKeysRotated.js'), - // 'certificateExpiry' : require(__dirname + '/plugins/aws/iam/certificateExpiry.js'), - // 'emptyGroups' : require(__dirname + '/plugins/aws/iam/emptyGroups.js'), - // 'iamUserAdmins' : require(__dirname + '/plugins/aws/iam/iamUserAdmins.js'), - // 'iamUserNameRegex' : require(__dirname + '/plugins/aws/iam/iamUserNameRegex.js'), - // 'iamRolePolicies' : require(__dirname + '/plugins/aws/iam/iamRolePolicies.js'), - // 'maxPasswordAge' : require(__dirname + '/plugins/aws/iam/maxPasswordAge.js'), - // 'minPasswordLength' : require(__dirname + '/plugins/aws/iam/minPasswordLength.js'), - // 'noUserIamPolicies' : require(__dirname + '/plugins/aws/iam/noUserIamPolicies.js'), - // 'passwordExpiration' : require(__dirname + '/plugins/aws/iam/passwordExpiration.js'), - // 'passwordRequiresLowercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresLowercase.js'), - // 'passwordRequiresNumbers' : require(__dirname + '/plugins/aws/iam/passwordRequiresNumbers.js'), - // 'passwordRequiresSymbols' : require(__dirname + '/plugins/aws/iam/passwordRequiresSymbols.js'), - // 'passwordRequiresUppercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresUppercase.js'), - // 'passwordReusePrevention' : require(__dirname + '/plugins/aws/iam/passwordReusePrevention.js'), - // 'rootAccessKeys' : require(__dirname + '/plugins/aws/iam/rootAccessKeys.js'), - // 'rootAccountInUse' : require(__dirname + '/plugins/aws/iam/rootAccountInUse.js'), - // 'rootHardwareMfa' : require(__dirname + '/plugins/aws/iam/rootHardwareMfa.js'), - // 'rootMfaEnabled' : require(__dirname + '/plugins/aws/iam/rootMfaEnabled.js'), - // 'sshKeysRotated' : require(__dirname + '/plugins/aws/iam/sshKeysRotated.js'), - // 'usersMfaEnabled' : require(__dirname + '/plugins/aws/iam/usersMfaEnabled.js'), - // 'usersPasswordAndKeys' : require(__dirname + '/plugins/aws/iam/usersPasswordAndKeys.js'), - // 'usersPasswordLastUsed' : require(__dirname + '/plugins/aws/iam/usersPasswordLastUsed.js'), - // 'canaryKeysUsed' : require(__dirname + '/plugins/aws/iam/canaryKeysUsed.js'), - // 'kinesisEncrypted' : require(__dirname + '/plugins/aws/kinesis/kinesisEncrypted.js'), - // 'firehoseEncrypted' : require(__dirname + '/plugins/aws/firehose/firehoseEncrypted.js'), - // 'kmsKeyRotation' : require(__dirname + '/plugins/aws/kms/kmsKeyRotation.js'), - // 'kmsScheduledDeletion' : require(__dirname + '/plugins/aws/kms/kmsScheduledDeletion.js'), - // 'kmsKeyPolicy' : require(__dirname + '/plugins/aws/kms/kmsKeyPolicy.js'), - // 'kmsDefaultKeyUsage' : require(__dirname + '/plugins/aws/kms/kmsDefaultKeyUsage.js'), - - // 'rdsAutomatedBackups' : require(__dirname + '/plugins/aws/rds/rdsAutomatedBackups.js'), - // 'rdsEncryptionEnabled' : require(__dirname + '/plugins/aws/rds/rdsEncryptionEnabled.js'), - // 'rdsLoggingEnabled' : require(__dirname + '/plugins/aws/rds/rdsLoggingEnabled.js'), - // 'rdsPubliclyAccessible' : require(__dirname + '/plugins/aws/rds/rdsPubliclyAccessible.js'), - // 'rdsRestorable' : require(__dirname + '/plugins/aws/rds/rdsRestorable.js'), - // 'rdsMultiAz' : require(__dirname + '/plugins/aws/rds/rdsMultiAz.js'), - // 'rdsSnapshotEncryption' : require(__dirname + '/plugins/aws/rds/rdsSnapshotEncryption.js'), - // 'rdsMinorVersionUpgrade' : require(__dirname + '/plugins/aws/rds/rdsMinorVersionUpgrade.js'), - - // 'domainAutoRenew' : require(__dirname + '/plugins/aws/route53/domainAutoRenew.js'), - // 'domainExpiry' : require(__dirname + '/plugins/aws/route53/domainExpiry.js'), - // 'domainTransferLock' : require(__dirname + '/plugins/aws/route53/domainTransferLock.js'), - - // 'bucketEncryptionInTransit' : require(__dirname + '/plugins/aws/s3/bucketEncryptionInTransit.js'), - // 'bucketAllUsersPolicy' : require(__dirname + '/plugins/aws/s3/bucketAllUsersPolicy.js'), - // 'bucketAllUsersAcl' : require(__dirname + '/plugins/aws/s3/bucketAllUsersAcl.js'), - // 'bucketVersioning' : require(__dirname + '/plugins/aws/s3/bucketVersioning.js'), - // 'bucketLogging' : require(__dirname + '/plugins/aws/s3/bucketLogging.js'), - // 's3Encryption' : require(__dirname + '/plugins/aws/s3/s3Encryption.js'), - // 'bucketPublicAccessBlock' : require(__dirname + '/plugins/aws/s3/bucketPublicAccessBlock.js'), - // 'bucketEncryption' : require(__dirname + '/plugins/aws/s3/bucketEncryption.js'), - // 'bucketWebsiteEnabled' : require(__dirname + '/plugins/aws/s3/bucketWebsiteEnabled.js'), - // 'bucketEnforceEncryption' : require(__dirname + '/plugins/aws/s3/bucketEnforceEncryption.js'), - - // 'notebookDataEncrypted' : require(__dirname + '/plugins/aws/sagemaker/notebookDataEncrypted.js'), - // 'notebookDirectInternetAccess' : require(__dirname + '/plugins/aws/sagemaker/notebookDirectInternetAccess.js'), - - // 'dkimEnabled' : require(__dirname + '/plugins/aws/ses/dkimEnabled.js'), - - // 'topicPolicies' : require(__dirname + '/plugins/aws/sns/topicPolicies.js'), - // 'sqsCrossAccount' : require(__dirname + '/plugins/aws/sqs/sqsCrossAccount.js'), - // 'sqsEncrypted' : require(__dirname + '/plugins/aws/sqs/sqsEncrypted.js'), - - // 'ssmEncryptedParameters' : require(__dirname + '/plugins/aws/ssm/ssmEncryptedParameters.js'), - // 'ssmActiveOnAllInstances' : require(__dirname + '/plugins/aws/ssm/ssmActiveOnAllInstances.js'), - // 'ssmAgentLatestVersion' : require(__dirname + '/plugins/aws/ssm/ssmAgentLatestVersion.js'), - - // 'lambdaOldRuntimes' : require(__dirname + '/plugins/aws/lambda/lambdaOldRuntimes.js'), - // 'lambdaVpcConfig' : require(__dirname + '/plugins/aws/lambda/lambdaVpcConfig.js'), - // 'lambdaPublicAccess' : require(__dirname + '/plugins/aws/lambda/lambdaPublicAccess.js'), - // 'lambdaLogGroups' : require(__dirname + '/plugins/aws/lambda/lambdaLogGroups.js'), - - // 'monitoringMetrics' : require(__dirname + '/plugins/aws/cloudwatchlogs/monitoringMetrics.js'), - - // 'redshiftEncryptionEnabled' : require(__dirname + '/plugins/aws/redshift/redshiftEncryptionEnabled.js'), - // 'redshiftPubliclyAccessible' : require(__dirname + '/plugins/aws/redshift/redshiftPubliclyAccessible.js'), - - // 'transferLoggingEnabled' : require(__dirname + '/plugins/aws/transfer/transferLoggingEnabled.js'), - - // 'shieldAdvancedEnabled' : require(__dirname + '/plugins/aws/shield/shieldAdvancedEnabled.js'), - // 'shieldEmergencyContacts' : require(__dirname + '/plugins/aws/shield/shieldEmergencyContacts.js'), - // 'shieldProtections' : require(__dirname + '/plugins/aws/shield/shieldProtections.js'), - - // 'enableAllFeatures' : require(__dirname + '/plugins/aws/organizations/enableAllFeatures.js'), - // 'organizationInvite' : require(__dirname + '/plugins/aws/organizations/organizationInvite.js'), - // 'guardDutyEnabled' : require(__dirname + '/plugins/aws/guardduty/guarddutyEnabled.js'), - // 'guardDutyMaster' : require(__dirname + '/plugins/aws/guardduty/guarddutyMaster.js'), - - // 'xrayEncryptionEnabled' : require(__dirname + '/plugins/aws/xray/xrayEncryptionEnabled.js') + 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), + 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), + 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), + 'cloudtrailEncryption' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEncryption.js'), + 'cloudtrailFileValidation' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailFileValidation.js'), + 'cloudtrailToCloudwatch' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailToCloudwatch.js'), + 'cloudtrailBucketPrivate' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketPrivate.js'), + + 'configServiceEnabled' : require(__dirname + '/plugins/aws/configservice/configServiceEnabled.js'), + + 'dmsEncryptionEnabled' : require(__dirname + '/plugins/aws/dms/dmsEncryptionEnabled.js'), + + 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), + + 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), + 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), + 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), + 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), + 'instanceLimit' : require(__dirname + '/plugins/aws/ec2/instanceLimit.js'), + 'instanceVcpusLimit' : require(__dirname + '/plugins/aws/ec2/instanceVcpusLimit.js'), + 'instanceMaxCount' : require(__dirname + '/plugins/aws/ec2/instanceMaxCount.js'), + 'instanceKeyBasedLogin' : require(__dirname + '/plugins/aws/ec2/instanceKeyBasedLogin.js'), + 'openAllPortsProtocols' : require(__dirname + '/plugins/aws/ec2/openAllPortsProtocols.js'), + 'openCIFS' : require(__dirname + '/plugins/aws/ec2/openCIFS.js'), + 'openDNS' : require(__dirname + '/plugins/aws/ec2/openDNS.js'), + 'openDocker' : require(__dirname + '/plugins/aws/ec2/openDocker.js'), + 'openFTP' : require(__dirname + '/plugins/aws/ec2/openFTP.js'), + 'openHadoopNameNode' : require(__dirname + '/plugins/aws/ec2/openHadoopNameNode.js'), + 'openHadoopNameNodeWebUI' : require(__dirname + '/plugins/aws/ec2/openHadoopNameNodeWebUI.js'), + 'openKibana' : require(__dirname + '/plugins/aws/ec2/openKibana.js'), + 'openMySQL' : require(__dirname + '/plugins/aws/ec2/openMySQL.js'), + 'openOracle' : require(__dirname + '/plugins/aws/ec2/openOracle.js'), + 'openNetBIOS' : require(__dirname + '/plugins/aws/ec2/openNetBIOS.js'), + 'openPostgreSQL' : require(__dirname + '/plugins/aws/ec2/openPostgreSQL.js'), + 'openRDP' : require(__dirname + '/plugins/aws/ec2/openRDP.js'), + 'openRPC' : require(__dirname + '/plugins/aws/ec2/openRPC.js'), + 'openSalt' : require(__dirname + '/plugins/aws/ec2/openSalt.js'), + 'openSMBoTCP' : require(__dirname + '/plugins/aws/ec2/openSMBoTCP.js'), + 'openSMTP' : require(__dirname + '/plugins/aws/ec2/openSMTP.js'), + 'openSQLServer' : require(__dirname + '/plugins/aws/ec2/openSQLServer.js'), + 'openSSH' : require(__dirname + '/plugins/aws/ec2/openSSH.js'), + 'openTelnet' : require(__dirname + '/plugins/aws/ec2/openTelnet.js'), + 'openVNCClient' : require(__dirname + '/plugins/aws/ec2/openVNCClient.js'), + 'openVNCServer' : require(__dirname + '/plugins/aws/ec2/openVNCServer.js'), + 'openElasticsearch' : require(__dirname + '/plugins/aws/ec2/openElasticsearch.js'), + 'vpcElasticIpLimit' : require(__dirname + '/plugins/aws/ec2/vpcElasticIpLimit.js'), + 'classicInstances' : require(__dirname + '/plugins/aws/ec2/classicInstances.js'), + 'flowLogsEnabled' : require(__dirname + '/plugins/aws/ec2/flowLogsEnabled.js'), + 'vpcMultipleSubnets' : require(__dirname + '/plugins/aws/ec2/multipleSubnets.js'), + 'overlappingSecurityGroups' : require(__dirname + '/plugins/aws/ec2/overlappingSecurityGroups.js'), + 'publicAmi' : require(__dirname + '/plugins/aws/ec2/publicAmi.js'), + 'encryptedAmi' : require(__dirname + '/plugins/aws/ec2/encryptedAmi.js'), + 'instanceIamRole' : require(__dirname + '/plugins/aws/ec2/instanceIamRole.js'), + 'ebsEncryptionEnabled' : require(__dirname + '/plugins/aws/ec2/ebsEncryptionEnabled.js'), + 'ebsSnapshotPrivate' : require(__dirname + '/plugins/aws/ec2/ebsSnapshotPrivate.js'), + 'natMultiAz' : require(__dirname + '/plugins/aws/ec2/natMultiAz.js'), + 'defaultVpcInUse' : require(__dirname + '/plugins/aws/ec2/defaultVpcInUse.js'), + 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), + 'crossVpcPublicPrivate' : require(__dirname + '/plugins/aws/ec2/crossVpcPublicPrivate.js'), + 'ebsEncryptedSnapshots' : require(__dirname + '/plugins/aws/ec2/ebsEncryptedSnapshots.js'), + 'ec2MetadataOptions' : require(__dirname + '/plugins/aws/ec2/ec2MetadataOptions.js'), + + 'efsEncryptionEnabled' : require(__dirname + '/plugins/aws/efs/efsEncryptionEnabled.js'), + + 'ecrRepositoryPolicy' : require(__dirname + '/plugins/aws/ecr/ecrRepositoryPolicy.js'), + 'ecrRepositoryTagImmutability' : require(__dirname + '/plugins/aws/ecr/ecrRepositoryTagImmutability.js'), + + 'eksKubernetesVersion' : require(__dirname + '/plugins/aws/eks/eksKubernetesVersion.js'), + 'eksLoggingEnabled' : require(__dirname + '/plugins/aws/eks/eksLoggingEnabled.js'), + 'eksPrivateEndpoint' : require(__dirname + '/plugins/aws/eks/eksPrivateEndpoint.js'), + 'eksSecurityGroups' : require(__dirname + '/plugins/aws/eks/eksSecurityGroups.js'), + + 'insecureCiphers' : require(__dirname + '/plugins/aws/elb/insecureCiphers.js'), + 'elbHttpsOnly' : require(__dirname + '/plugins/aws/elb/elbHttpsOnly.js'), + 'elbLoggingEnabled' : require(__dirname + '/plugins/aws/elb/elbLoggingEnabled.js'), + 'elbNoInstances' : require(__dirname + '/plugins/aws/elb/elbNoInstances.js'), + + 'elbv2LoggingEnabled' : require(__dirname + '/plugins/aws/elbv2/elbv2LoggingEnabled.js'), + 'elbv2HttpsOnly' : require(__dirname + '/plugins/aws/elbv2/elbv2HttpsOnly.js'), + 'elbv2NoInstances' : require(__dirname + '/plugins/aws/elbv2/elbv2NoInstances.js'), + 'elbv2WafEnabled' : require(__dirname + '/plugins/aws/elbv2/elbv2WafEnabled.js'), + + 'esPublicEndpoint' : require(__dirname + '/plugins/aws/es/esPublicEndpoint.js'), + 'esRequireIAMAuth' : require(__dirname + '/plugins/aws/es/esRequireIAMAuth.js'), + 'esEncryptedDomain' : require(__dirname + '/plugins/aws/es/esEncryptedDomain.js'), + 'esNodeToNodeEncryption' : require(__dirname + '/plugins/aws/es/esNodeToNodeEncryption.js'), + 'esLoggingEnabled' : require(__dirname + '/plugins/aws/es/esLoggingEnabled.js'), + 'esUpgradeAvailable' : require(__dirname + '/plugins/aws/es/esUpgradeAvailable.js'), + 'esHttpsOnly' : require(__dirname + '/plugins/aws/es/esHttpsOnly.js'), + + 'accessKeysExtra' : require(__dirname + '/plugins/aws/iam/accessKeysExtra.js'), + 'accessKeysLastUsed' : require(__dirname + '/plugins/aws/iam/accessKeysLastUsed.js'), + 'accessKeysRotated' : require(__dirname + '/plugins/aws/iam/accessKeysRotated.js'), + 'certificateExpiry' : require(__dirname + '/plugins/aws/iam/certificateExpiry.js'), + 'emptyGroups' : require(__dirname + '/plugins/aws/iam/emptyGroups.js'), + 'iamUserAdmins' : require(__dirname + '/plugins/aws/iam/iamUserAdmins.js'), + 'iamUserNameRegex' : require(__dirname + '/plugins/aws/iam/iamUserNameRegex.js'), + 'iamRolePolicies' : require(__dirname + '/plugins/aws/iam/iamRolePolicies.js'), + 'maxPasswordAge' : require(__dirname + '/plugins/aws/iam/maxPasswordAge.js'), + 'minPasswordLength' : require(__dirname + '/plugins/aws/iam/minPasswordLength.js'), + 'noUserIamPolicies' : require(__dirname + '/plugins/aws/iam/noUserIamPolicies.js'), + 'passwordExpiration' : require(__dirname + '/plugins/aws/iam/passwordExpiration.js'), + 'passwordRequiresLowercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresLowercase.js'), + 'passwordRequiresNumbers' : require(__dirname + '/plugins/aws/iam/passwordRequiresNumbers.js'), + 'passwordRequiresSymbols' : require(__dirname + '/plugins/aws/iam/passwordRequiresSymbols.js'), + 'passwordRequiresUppercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresUppercase.js'), + 'passwordReusePrevention' : require(__dirname + '/plugins/aws/iam/passwordReusePrevention.js'), + 'rootAccessKeys' : require(__dirname + '/plugins/aws/iam/rootAccessKeys.js'), + 'rootAccountInUse' : require(__dirname + '/plugins/aws/iam/rootAccountInUse.js'), + 'rootHardwareMfa' : require(__dirname + '/plugins/aws/iam/rootHardwareMfa.js'), + 'rootMfaEnabled' : require(__dirname + '/plugins/aws/iam/rootMfaEnabled.js'), + 'sshKeysRotated' : require(__dirname + '/plugins/aws/iam/sshKeysRotated.js'), + 'usersMfaEnabled' : require(__dirname + '/plugins/aws/iam/usersMfaEnabled.js'), + 'usersPasswordAndKeys' : require(__dirname + '/plugins/aws/iam/usersPasswordAndKeys.js'), + 'usersPasswordLastUsed' : require(__dirname + '/plugins/aws/iam/usersPasswordLastUsed.js'), + 'canaryKeysUsed' : require(__dirname + '/plugins/aws/iam/canaryKeysUsed.js'), + 'kinesisEncrypted' : require(__dirname + '/plugins/aws/kinesis/kinesisEncrypted.js'), + 'firehoseEncrypted' : require(__dirname + '/plugins/aws/firehose/firehoseEncrypted.js'), + 'kmsKeyRotation' : require(__dirname + '/plugins/aws/kms/kmsKeyRotation.js'), + 'kmsScheduledDeletion' : require(__dirname + '/plugins/aws/kms/kmsScheduledDeletion.js'), + 'kmsKeyPolicy' : require(__dirname + '/plugins/aws/kms/kmsKeyPolicy.js'), + 'kmsDefaultKeyUsage' : require(__dirname + '/plugins/aws/kms/kmsDefaultKeyUsage.js'), + + 'rdsAutomatedBackups' : require(__dirname + '/plugins/aws/rds/rdsAutomatedBackups.js'), + 'rdsEncryptionEnabled' : require(__dirname + '/plugins/aws/rds/rdsEncryptionEnabled.js'), + 'rdsLoggingEnabled' : require(__dirname + '/plugins/aws/rds/rdsLoggingEnabled.js'), + 'rdsPubliclyAccessible' : require(__dirname + '/plugins/aws/rds/rdsPubliclyAccessible.js'), + 'rdsRestorable' : require(__dirname + '/plugins/aws/rds/rdsRestorable.js'), + 'rdsMultiAz' : require(__dirname + '/plugins/aws/rds/rdsMultiAz.js'), + 'rdsSnapshotEncryption' : require(__dirname + '/plugins/aws/rds/rdsSnapshotEncryption.js'), + 'rdsMinorVersionUpgrade' : require(__dirname + '/plugins/aws/rds/rdsMinorVersionUpgrade.js'), + + 'domainAutoRenew' : require(__dirname + '/plugins/aws/route53/domainAutoRenew.js'), + 'domainExpiry' : require(__dirname + '/plugins/aws/route53/domainExpiry.js'), + 'domainTransferLock' : require(__dirname + '/plugins/aws/route53/domainTransferLock.js'), + + 'bucketEncryptionInTransit' : require(__dirname + '/plugins/aws/s3/bucketEncryptionInTransit.js'), + 'bucketAllUsersPolicy' : require(__dirname + '/plugins/aws/s3/bucketAllUsersPolicy.js'), + 'bucketAllUsersAcl' : require(__dirname + '/plugins/aws/s3/bucketAllUsersAcl.js'), + 'bucketVersioning' : require(__dirname + '/plugins/aws/s3/bucketVersioning.js'), + 'bucketLogging' : require(__dirname + '/plugins/aws/s3/bucketLogging.js'), + 's3Encryption' : require(__dirname + '/plugins/aws/s3/s3Encryption.js'), + 'bucketPublicAccessBlock' : require(__dirname + '/plugins/aws/s3/bucketPublicAccessBlock.js'), + 'bucketEncryption' : require(__dirname + '/plugins/aws/s3/bucketEncryption.js'), + 'bucketWebsiteEnabled' : require(__dirname + '/plugins/aws/s3/bucketWebsiteEnabled.js'), + 'bucketEnforceEncryption' : require(__dirname + '/plugins/aws/s3/bucketEnforceEncryption.js'), + + 'notebookDataEncrypted' : require(__dirname + '/plugins/aws/sagemaker/notebookDataEncrypted.js'), + 'notebookDirectInternetAccess' : require(__dirname + '/plugins/aws/sagemaker/notebookDirectInternetAccess.js'), + + 'dkimEnabled' : require(__dirname + '/plugins/aws/ses/dkimEnabled.js'), + + 'topicPolicies' : require(__dirname + '/plugins/aws/sns/topicPolicies.js'), + 'sqsCrossAccount' : require(__dirname + '/plugins/aws/sqs/sqsCrossAccount.js'), + 'sqsEncrypted' : require(__dirname + '/plugins/aws/sqs/sqsEncrypted.js'), + + 'ssmEncryptedParameters' : require(__dirname + '/plugins/aws/ssm/ssmEncryptedParameters.js'), + 'ssmActiveOnAllInstances' : require(__dirname + '/plugins/aws/ssm/ssmActiveOnAllInstances.js'), + 'ssmAgentLatestVersion' : require(__dirname + '/plugins/aws/ssm/ssmAgentLatestVersion.js'), + + 'lambdaOldRuntimes' : require(__dirname + '/plugins/aws/lambda/lambdaOldRuntimes.js'), + 'lambdaVpcConfig' : require(__dirname + '/plugins/aws/lambda/lambdaVpcConfig.js'), + 'lambdaPublicAccess' : require(__dirname + '/plugins/aws/lambda/lambdaPublicAccess.js'), + 'lambdaLogGroups' : require(__dirname + '/plugins/aws/lambda/lambdaLogGroups.js'), + + 'monitoringMetrics' : require(__dirname + '/plugins/aws/cloudwatchlogs/monitoringMetrics.js'), + + 'redshiftEncryptionEnabled' : require(__dirname + '/plugins/aws/redshift/redshiftEncryptionEnabled.js'), + 'redshiftPubliclyAccessible' : require(__dirname + '/plugins/aws/redshift/redshiftPubliclyAccessible.js'), + + 'transferLoggingEnabled' : require(__dirname + '/plugins/aws/transfer/transferLoggingEnabled.js'), + + 'shieldAdvancedEnabled' : require(__dirname + '/plugins/aws/shield/shieldAdvancedEnabled.js'), + 'shieldEmergencyContacts' : require(__dirname + '/plugins/aws/shield/shieldEmergencyContacts.js'), + 'shieldProtections' : require(__dirname + '/plugins/aws/shield/shieldProtections.js'), + + 'enableAllFeatures' : require(__dirname + '/plugins/aws/organizations/enableAllFeatures.js'), + 'organizationInvite' : require(__dirname + '/plugins/aws/organizations/organizationInvite.js'), + 'guardDutyEnabled' : require(__dirname + '/plugins/aws/guardduty/guarddutyEnabled.js'), + 'guardDutyMaster' : require(__dirname + '/plugins/aws/guardduty/guarddutyMaster.js'), + + 'xrayEncryptionEnabled' : require(__dirname + '/plugins/aws/xray/xrayEncryptionEnabled.js') }, azure : { 'fileServiceEncryption' : require(__dirname + '/plugins/azure/storageaccounts/fileServiceEncryption.js'), diff --git a/helpers/aws/regions.js b/helpers/aws/regions.js index 4609df93b3..3749d0093f 100644 --- a/helpers/aws/regions.js +++ b/helpers/aws/regions.js @@ -35,6 +35,7 @@ module.exports = { route53: ['us-east-1'], route53domains: ['us-east-1'], s3: ['us-east-1'], + cloudformation: regions, cloudtrail: regions, cloudwatchlogs: regions, configservice: regions, diff --git a/index.js b/index.js index 3b97033a22..cceed66ab0 100644 --- a/index.js +++ b/index.js @@ -113,7 +113,13 @@ if(process.env.GOOGLE_APPLICATION_CREDENTIALS){ } // Custom settings - place plugin-specific settings here -var settings = {}; +var settings = { + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] + } +}; // If running in GovCloud, uncomment the following // settings.govcloud = true; diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 658dc42f3d..6075fd59f7 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -1,3 +1,4 @@ +var async = require('async'); var helpers = require('../../../helpers/aws'); module.exports = { @@ -8,63 +9,59 @@ module.exports = { link: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html', recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], - compliance: { - hipaa: 'HIPAA requires all data to be transmitted over secure channels. ' + - 'CloudFront HTTPS redirection should be used to ensure site visitors ' + - 'are always connecting over a secure channel.' - }, - // settings : { secretWords : ["password", "privatekey", "secret"] }, run: function(cache, settings, callback) { - var results = []; var source = {}; + var regions = helpers.regions(settings); + secretWords = settings.plainTextParameters.secretWords; - var region = helpers.defaultRegion(settings); + async.each(regions.cloudformation, function(region, rcb){ - var describeStacks = helpers.addSource(cache, source, - ['cloudformation', 'describeStacks', region]); + var describeStacks = helpers.addSource(cache, source, + ['cloudformation', 'describeStacks', region]); + + if (!describeStacks) return rcb(); - console.log(describeStacks); - console.log("results received"); - // if (!describeStacks) return callback(null, results, source); + if (describeStacks.err || !describeStacks.data) { + helpers.addResult(results, 3, + 'Unable to describe stacks: ' + helpers.addError(describeStacks), region); + return rcb(); + } - // if (describeStacks.err || !describeStacks.data) { - // helpers.addResult(results, 3, - // 'Unable to describe stacks: ' + helpers.addError(describeStacks)); - // return callback(null, results, source); - // } + if (!describeStacks.data.length) { + helpers.addResult(results, 0, 'No stack description found', region); + return rcb(); + } + + var parameterFound; + describeStacks.data.forEach(function(stack){ + parameterFound = false; - // if (!describeStacks.data.length) { - // helpers.addResult(results, 0, 'No stacks descriptions found'); - // return callback(null, results, source); - // } - // console.log(describeStacks.data); - // loop through stacks for every template retrieval - // describeStacks.data.forEach(function(Distribution){ - // var stackTemplate = helpers.addSource(cache, source, - // ['cloudformation', 'getTemplate', region]); - - // if (!describeStacks) return callback(null, results, source); - - // if (describeStacks.err || !describeStacks.data) { - // helpers.addResult(results, 3, - // 'Unable to describe stacks: ' + helpers.addError(describeStacks)); - // return callback(null, results, source); - // } + if(!stack.Parameters.length) { + helpers.addResult(results, 0, + 'The template did not contain any potentially-sensitive parameters', region); + return; + } - // if (Distribution.DefaultCacheBehavior.ViewerProtocolPolicy == 'redirect-to-https') { - // helpers.addResult(results, 0, 'CloudFront distribution ' + - // 'is configured to redirect non-HTTPS traffic to HTTPS', 'global', Distribution.ARN); - // } else if (Distribution.DefaultCacheBehavior.ViewerProtocolPolicy == 'https-only') { - // helpers.addResult(results, 0, 'The CloudFront ' + - // 'distribution is set to use HTTPS only.', 'global', Distribution.ARN); - // } else { - // helpers.addResult(results, 2, 'CloudFront distribution ' + - // 'is not configured to use HTTPS', 'global', Distribution.ARN); - // } - // }); + stack.Parameters.forEach(function(parameter){ + if(secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameterFound) { + parameterFound = true; + helpers.addResult(results, 1, + 'The template contained one of the following potentially-sensitive parameters: secret, key, password', region); + return; + } + }); + + if(!parameterFound) { + helpers.addResult(results, 0, + 'The template did not contain any potentially-sensitive parameters', region); + } - callback(null, results, source); + }); + rcb(); + }, function(){ + callback(null, results, source); + }); } }; \ No newline at end of file diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js new file mode 100644 index 0000000000..792f454333 --- /dev/null +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -0,0 +1,161 @@ +var expect = require('chai').expect; +const plainTextParameters = require('./plainTextParameters'); +const settings = { + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] + } +}; +const describeStacks = [ + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [ + { + ParameterKey: 'Secret', + ParameterValue: 'bucketwithsecretparameter1' + }, + { + ParameterKey: 'Password', + ParameterValue: 'bucketwithsecretparameter1' + } + ], + CreationTime: '2020-08-13T13:34:52.435Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + }, + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [ + { + ParameterKey: 'S3BucketName', + ParameterValue: 'testbucketplaintext1' + } + ], + CreationTime: '2020-08-12T09:42:04.803Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + }, + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [], + CreationTime: '2020-08-12T09:42:04.803Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + } +] + +const createCache = (stacks) => { + return { + cloudformation: { + describeStacks: { + 'us-east-1': { + data: stacks + }, + }, + }, + }; +}; + +const createErrorCache = () => { + return { + cloudformation: { + describeStacks: { + 'us-east-1': { + err: { + message: 'error describing cloudformation stacks' + }, + }, + }, + }, + }; +}; + +const createNullCache = () => { + return { + cloudformation: { + describeStacks: { + 'us-east-1': null, + }, + }, + }; +}; + +describe('plainTextParameters', function () { + describe('run', function () { + it('should FAIL if Stack parameters contain one of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[0]]); + plainTextParameters.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(1); + done(); + }); + }); + + it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[1]]); + plainTextParameters.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should PASS if unable to describe stacks', function (done) { + const cache = createCache([]); + plainTextParameters.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should PASS if there is no parameter in the stack', function (done) { + const cache = createCache([describeStacks[2]]); + plainTextParameters.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should not return any results if unable to fetch any stack description', function (done) { + const cache = createNullCache(); + plainTextParameters.run(cache, settings, (err, results) => { + expect(results.length).to.equal(0); + done(); + }); + }); + + it('should UNKNOWN if error occurs while fetching stack description', function (done) { + const cache = createErrorCache(); + plainTextParameters.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + }); +}); \ No newline at end of file From 7c8616c9c54503cb3c9e2f7b565fd89478a21bef Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 15:30:57 +0500 Subject: [PATCH 04/71] Added plugin and spec file for launch wizard security groups --- exports.js | 4 +- plugins/aws/ec2/launchWizardSecurityGroups.js | 55 +++++++ .../ec2/launchWizardSecurityGroups.spec.js | 153 ++++++++++++++++++ plugins/aws/ec2/vpcEndpointAcceptance.spec.js | 9 -- 4 files changed, 211 insertions(+), 10 deletions(-) create mode 100644 plugins/aws/ec2/launchWizardSecurityGroups.js create mode 100644 plugins/aws/ec2/launchWizardSecurityGroups.spec.js diff --git a/exports.js b/exports.js index 912d562838..0c8b893a09 100644 --- a/exports.js +++ b/exports.js @@ -29,6 +29,7 @@ module.exports = { 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), + 'launchWizardSecurityGroups' : require(__dirname + '/plugins/aws/ec2/launchWizardSecurityGroups'), 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), @@ -71,8 +72,9 @@ module.exports = { 'ebsSnapshotPrivate' : require(__dirname + '/plugins/aws/ec2/ebsSnapshotPrivate.js'), 'natMultiAz' : require(__dirname + '/plugins/aws/ec2/natMultiAz.js'), 'defaultVpcInUse' : require(__dirname + '/plugins/aws/ec2/defaultVpcInUse.js'), - 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), + 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), 'crossVpcPublicPrivate' : require(__dirname + '/plugins/aws/ec2/crossVpcPublicPrivate.js'), + 'vpcEndpointAcceptance' : require(__dirname + '/plugins/aws/ec2/vpcEndpointAcceptance'), 'ebsEncryptedSnapshots' : require(__dirname + '/plugins/aws/ec2/ebsEncryptedSnapshots.js'), 'ec2MetadataOptions' : require(__dirname + '/plugins/aws/ec2/ec2MetadataOptions.js'), diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js new file mode 100644 index 0000000000..252cabd11c --- /dev/null +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -0,0 +1,55 @@ +var async = require('async'); +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'EC2 LaunchWizard Security Groups', + category: 'EC2', + description: 'Ensures security groups created by the EC2 launch wizard are not used', + more_info: 'The EC2 launch wizard frequently creates insecure security groups that are exposed publicly. These groups should not be used and custom security groups should be created instead.', + link: 'https://docs.aws.amazon.com/launchwizard/latest/userguide/launch-wizard-sap-security-groups.html', + recommended_action: 'Delete the launch wizard security group and replace it with a custom security group.', + apis: ['EC2:describeSecurityGroups'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(settings); + + async.each(regions.ec2, function(region, rcb){ + var describeSecurityGroups = helpers.addSource(cache, source, + ['ec2', 'describeSecurityGroups', region]); + + if (!describeSecurityGroups) return rcb(); + + if (describeSecurityGroups.err || !describeSecurityGroups.data) { + helpers.addResult(results, 3, + 'Unable to query for security groups: ' + helpers.addError(describeSecurityGroups), region); + return rcb(); + } + + if (!describeSecurityGroups.data.length) { + helpers.addResult(results, 0, 'No security groups present', region); + return rcb(); + } + + for (var s in describeSecurityGroups.data) { + var sg = describeSecurityGroups.data[s]; + var resource = sg.GroupId; + + if (sg.GroupName.toLowerCase().startsWith('launch-wizard')) { + helpers.addResult(results, 2, + 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', + region, resource); + } else { + helpers.addResult(results, 0, + 'Security Group ' + sg.GroupName + ' was not launched using EC2 launch wizard', + region, resource); + } + } + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.spec.js b/plugins/aws/ec2/launchWizardSecurityGroups.spec.js new file mode 100644 index 0000000000..a2b8baa2e9 --- /dev/null +++ b/plugins/aws/ec2/launchWizardSecurityGroups.spec.js @@ -0,0 +1,153 @@ +var expect = require('chai').expect; +const launchWizardSecurityGroups = require('./launchWizardSecurityGroups'); + +const securityGroups = [ + { + "Description": "launch-wizard-1 created 2020-08-10T14:28:09.271+05:00", + "GroupName": "launch-wizard-1", + "IpPermissions": [ + { + "FromPort": 22, + "IpProtocol": "tcp", + "IpRanges": [ + { + "CidrIp": "0.0.0.0/0" + } + ], + "Ipv6Ranges": [], + "PrefixListIds": [], + "ToPort": 22, + "UserIdGroupPairs": [] + } + ], + "OwnerId": "560213429563", + "GroupId": "sg-0ff1642cae23c309a", + "IpPermissionsEgress": [ + { + "IpProtocol": "-1", + "IpRanges": [ + { + "CidrIp": "0.0.0.0/0" + } + ], + "Ipv6Ranges": [], + "PrefixListIds": [], + "UserIdGroupPairs": [] + } + ], + "Tags": [], + "VpcId": "vpc-99de2fe4" + }, + { + "Description": "Allows SSh access to developer", + "GroupName": "spec-test-sg", + "IpPermissions": [], + "OwnerId": "560213429563", + "GroupId": "sg-0b5f2771716acfee4", + "IpPermissionsEgress": [ + { + "FromPort": 22, + "IpProtocol": "tcp", + "IpRanges": [ + { + "CidrIp": "0.0.0.0/0" + } + ], + "Ipv6Ranges": [ + { + "CidrIpv6": "::/0" + } + ], + "PrefixListIds": [], + "ToPort": 22, + "UserIdGroupPairs": [] + } + ], + "Tags": [], + "VpcId": "vpc-99de2fe4" + } + ]; + +const createCache = (securityGroups) => { + return { + ec2: { + describeSecurityGroups: { + 'us-east-1': { + data: securityGroups + }, + }, + }, + }; +}; + +const createErrorCache = () => { + return { + ec2: { + describeSecurityGroups: { + 'us-east-1': { + err: { + message: 'error describing security groups' + }, + }, + }, + }, + }; +}; + +const createNullCache = () => { + return { + ec2: { + describeSecurityGroups: { + 'us-east-1': null, + }, + }, + }; +}; + +describe('launchWizardSecurityGroups', function () { + describe('run', function () { + it('should PASS if security groups was not created using EC2 launch wizard', function (done) { + const cache = createCache([securityGroups[1]]); + launchWizardSecurityGroups.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should FAIL if security groups was created using EC2 launch wizard', function (done) { + const cache = createCache([securityGroups[0]]); + launchWizardSecurityGroups.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + done(); + }); + }); + + it('should PASS if no security groups are detected', function (done) { + const cache = createCache([]); + launchWizardSecurityGroups.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should UNKNOWN if there was an error describing security groups', function (done) { + const cache = createErrorCache(); + launchWizardSecurityGroups.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + it('should not return any results if unable to query for security groups', function (done) { + const cache = createNullCache(); + launchWizardSecurityGroups.run(cache, {}, (err, results) => { + expect(results.length).to.equal(0); + done(); + }); + }); + }); +}); diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js index 0946f5a682..62053b7238 100644 --- a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js +++ b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js @@ -112,15 +112,6 @@ describe('vpcEndpointAcceptance', function () { }); }); - it('should PASS if no VPC endpoint services are detected', function (done) { - const cache = createCache([]); - vpcEndpointAcceptance.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - done(); - }); - }); - it('should UNKNOWN if there was an error querying for VPC endpoint services', function (done) { const cache = createErrorCache(); vpcEndpointAcceptance.run(cache, {}, (err, results) => { From 3da6672d99647eeb68b4966e069826edf6319e4c Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 16:50:46 +0500 Subject: [PATCH 05/71] Refactored code in plaintextParameters plugin and spec file --- exports.js | 4 ++- index.js | 8 +----- .../aws/cloudformation/plainTextParameters.js | 28 +++++++++++++------ .../plainTextParameters.spec.js | 28 ++++++++----------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/exports.js b/exports.js index 56084148d3..ee2b81f9ee 100644 --- a/exports.js +++ b/exports.js @@ -13,7 +13,9 @@ module.exports = { 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), - 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), + + 'plaintextParameters' : require(__dirname + '/plugins/aws/cloudformation/plaintextParameters.js'), + 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), diff --git a/index.js b/index.js index cceed66ab0..3b97033a22 100644 --- a/index.js +++ b/index.js @@ -113,13 +113,7 @@ if(process.env.GOOGLE_APPLICATION_CREDENTIALS){ } // Custom settings - place plugin-specific settings here -var settings = { - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] - } -}; +var settings = {}; // If running in GovCloud, uncomment the following // settings.govcloud = true; diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 6075fd59f7..2ed03af7f0 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -9,12 +9,19 @@ module.exports = { link: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html', recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], + settings: { + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] + } + }, run: function(cache, settings, callback) { var results = []; var source = {}; var regions = helpers.regions(settings); - secretWords = settings.plainTextParameters.secretWords; + secretWords = this.settings.plainTextParameters.secretWords; async.each(regions.cloudformation, function(region, rcb){ @@ -35,30 +42,33 @@ module.exports = { } var parameterFound; - describeStacks.data.forEach(function(stack){ + for (var s in describeStacks.data){ + // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id + var stack = describeStacks.data[s]; + var resource = stack.StackId; parameterFound = false; if(!stack.Parameters.length) { helpers.addResult(results, 0, - 'The template did not contain any potentially-sensitive parameters', region); - return; + 'The template does not contain any potentially-sensitive parameters', region, resource); + return rcb(); } stack.Parameters.forEach(function(parameter){ - if(secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameterFound) { + if(!parameterFound && secretWords.includes(parameter.ParameterKey.toLowerCase())) { parameterFound = true; helpers.addResult(results, 1, - 'The template contained one of the following potentially-sensitive parameters: secret, key, password', region); - return; + 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); } }); if(!parameterFound) { helpers.addResult(results, 0, - 'The template did not contain any potentially-sensitive parameters', region); + 'Template does not contain any potentially-sensitive parameters', region, resource); } - }); + } + rcb(); }, function(){ callback(null, results, source); diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index 792f454333..490d978002 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -1,12 +1,6 @@ var expect = require('chai').expect; -const plainTextParameters = require('./plainTextParameters'); -const settings = { - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] - } -}; +const plaintextParameters = require('./plaintextParameters'); + const describeStacks = [ { StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', @@ -102,20 +96,20 @@ const createNullCache = () => { }; }; -describe('plainTextParameters', function () { +describe('plaintextParameters', function () { describe('run', function () { - it('should FAIL if Stack parameters contain one of secret words ["password" , "privatekey", "secret"]', function (done) { + it('should WARN if template contains one of secret words ["password" , "privatekey", "secret"]', function (done) { const cache = createCache([describeStacks[0]]); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(1); done(); }); }); - it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { + it('should PASS if template does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { const cache = createCache([describeStacks[1]]); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -124,7 +118,7 @@ describe('plainTextParameters', function () { it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -133,7 +127,7 @@ describe('plainTextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -142,7 +136,7 @@ describe('plainTextParameters', function () { it('should not return any results if unable to fetch any stack description', function (done) { const cache = createNullCache(); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(0); done(); }); @@ -150,7 +144,7 @@ describe('plainTextParameters', function () { it('should UNKNOWN if error occurs while fetching stack description', function (done) { const cache = createErrorCache(); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); From ce6532523dca0f672a0824cfd3cc1e8161d9984c Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Fri, 14 Aug 2020 18:09:45 +0500 Subject: [PATCH 06/71] SPLOIT-113: Updated custom settings --- plugins/aws/cloudformation/plainTextParameters.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 2ed03af7f0..0a53028425 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -10,10 +10,11 @@ module.exports = { recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], settings: { - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] + plain_text_parameters: { + name: "CloudFormation Plaintext Parameters", + description: "A comma-delimited list of parameter strings that indicate a sensitive value", + regex: "[a-zA-Z0-9,]", + default: "secret,password,privatekey" } }, @@ -21,8 +22,7 @@ module.exports = { var results = []; var source = {}; var regions = helpers.regions(settings); - secretWords = this.settings.plainTextParameters.secretWords; - + secretWords = this.settings.plain_text_parameters.default; async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, From e70b96aac28a75ed99c4f51b9d89e0003d0e5b0b Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 21:24:30 +0500 Subject: [PATCH 07/71] Made PR requested changes --- plugins/aws/cloudformation/plainTextParameters.js | 8 ++++---- plugins/aws/ec2/launchWizardSecurityGroups.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 0a53028425..30da85236a 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -22,7 +22,7 @@ module.exports = { var results = []; var source = {}; var regions = helpers.regions(settings); - secretWords = this.settings.plain_text_parameters.default; + var secretWords = this.settings.plain_text_parameters.default; async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, @@ -48,14 +48,14 @@ module.exports = { var resource = stack.StackId; parameterFound = false; - if(!stack.Parameters.length) { + if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'The template does not contain any potentially-sensitive parameters', region, resource); + 'Template does not contain any potentially-sensitive parameters', region, resource); return rcb(); } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && secretWords.includes(parameter.ParameterKey.toLowerCase())) { + if((!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()))) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js index 252cabd11c..0e028778a7 100644 --- a/plugins/aws/ec2/launchWizardSecurityGroups.js +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -36,7 +36,7 @@ module.exports = { var sg = describeSecurityGroups.data[s]; var resource = sg.GroupId; - if (sg.GroupName.toLowerCase().startsWith('launch-wizard')) { + if (sg.GroupName && sg.GroupName.toLowerCase().startsWith('launch-wizard')) { helpers.addResult(results, 2, 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', region, resource); From f62a1d5037ebd20dda78959f5d2b0a073bb4abee Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 15 Aug 2020 01:07:50 +0500 Subject: [PATCH 08/71] SPLOIT-113: Added regex to check if NoEcho is enabled --- plugins/aws/cloudformation/plainTextParameters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 30da85236a..f6a5ce193a 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -55,7 +55,7 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if((!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()))) { + if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match("^[\*]+$")) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); From 1b80ac47565110c4ba276805f233d1aa11620a40 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 04:13:40 +0500 Subject: [PATCH 09/71] Accommodated PR changes --- exports.js | 2 +- plugins/aws/cloudformation/plainTextParameters.js | 6 +++--- .../aws/cloudformation/plainTextParameters.spec.js | 2 +- plugins/aws/ec2/launchWizardSecurityGroups.js | 11 +++++++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/exports.js b/exports.js index ee2b81f9ee..a61fddd2cc 100644 --- a/exports.js +++ b/exports.js @@ -14,7 +14,7 @@ module.exports = { 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), - 'plaintextParameters' : require(__dirname + '/plugins/aws/cloudformation/plaintextParameters.js'), + 'plaintextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index f6a5ce193a..5e5f227feb 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -46,12 +46,12 @@ module.exports = { // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id var stack = describeStacks.data[s]; var resource = stack.StackId; - parameterFound = false; + let parameterFound = false; if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'Template does not contain any potentially-sensitive parameters', region, resource); - return rcb(); + 'Template does not contain any parameters', region, resource); + continue; } stack.Parameters.forEach(function(parameter){ diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index 490d978002..cdad731db6 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -1,5 +1,5 @@ var expect = require('chai').expect; -const plaintextParameters = require('./plaintextParameters'); +const plaintextParameters = require('./plainTextParameters'); const describeStacks = [ { diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js index 0e028778a7..48f040b7ed 100644 --- a/plugins/aws/ec2/launchWizardSecurityGroups.js +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -28,7 +28,7 @@ module.exports = { } if (!describeSecurityGroups.data.length) { - helpers.addResult(results, 0, 'No security groups present', region); + helpers.addResult(results, 0, 'No security groups found', region); return rcb(); } @@ -36,7 +36,14 @@ module.exports = { var sg = describeSecurityGroups.data[s]; var resource = sg.GroupId; - if (sg.GroupName && sg.GroupName.toLowerCase().startsWith('launch-wizard')) { + if(!sg.GroupName) { + helpers.addResult(results, 2, + 'Unable to get group name of security group', + region, resource); + continue; + } + + if (sg.GroupName.toLowerCase().startsWith('launch-wizard')) { helpers.addResult(results, 2, 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', region, resource); From c0dc834c5739260ca9acfc7f11ef78bf73987535 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 06:09:46 +0500 Subject: [PATCH 10/71] Fixed eslint issues --- plugins/aws/cloudformation/plainTextParameters.js | 15 +++++++-------- plugins/aws/ec2/launchWizardSecurityGroups.js | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 5e5f227feb..5f9906591c 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -11,10 +11,10 @@ module.exports = { apis: ['CloudFormation:describeStacks'], settings: { plain_text_parameters: { - name: "CloudFormation Plaintext Parameters", - description: "A comma-delimited list of parameter strings that indicate a sensitive value", - regex: "[a-zA-Z0-9,]", - default: "secret,password,privatekey" + name: 'CloudFormation Plaintext Parameters', + description: 'A comma-delimited list of parameter strings that indicate a sensitive value', + regex: '[a-zA-Z0-9,]', + default: 'secret,password,privatekey' } }, @@ -33,7 +33,7 @@ module.exports = { if (describeStacks.err || !describeStacks.data) { helpers.addResult(results, 3, 'Unable to describe stacks: ' + helpers.addError(describeStacks), region); - return rcb(); + return rcb(); } if (!describeStacks.data.length) { @@ -41,7 +41,6 @@ module.exports = { return rcb(); } - var parameterFound; for (var s in describeStacks.data){ // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id var stack = describeStacks.data[s]; @@ -55,7 +54,7 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match("^[\*]+$")) { + if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); @@ -64,7 +63,7 @@ module.exports = { if(!parameterFound) { helpers.addResult(results, 0, - 'Template does not contain any potentially-sensitive parameters', region, resource); + 'Template does not contain any potentially-sensitive parameters', region, resource); } } diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js index 48f040b7ed..caa173be82 100644 --- a/plugins/aws/ec2/launchWizardSecurityGroups.js +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -45,8 +45,8 @@ module.exports = { if (sg.GroupName.toLowerCase().startsWith('launch-wizard')) { helpers.addResult(results, 2, - 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', - region, resource); + 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', + region, resource); } else { helpers.addResult(results, 0, 'Security Group ' + sg.GroupName + ' was not launched using EC2 launch wizard', From 7d457bd2c6def38589b805cb7de4d2cb8340b95d Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:38:01 +0500 Subject: [PATCH 11/71] Update exports.js --- exports.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exports.js b/exports.js index a61fddd2cc..5a338cfa5e 100644 --- a/exports.js +++ b/exports.js @@ -14,7 +14,7 @@ module.exports = { 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), - 'plaintextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), + 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), From 8c2466c91d58ce0de34eb75c62bcb02e844efd32 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:41:20 +0500 Subject: [PATCH 12/71] Fixed eslint issues --- index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 3b97033a22..262b4c8c73 100644 --- a/index.js +++ b/index.js @@ -10,12 +10,12 @@ var GoogleConfig; // OPTION 1: Configure service provider credentials through hard-coded config objects -// AWSConfig = { -// accessKeyId: '', -// secretAccessKey: '', -// sessionToken: '', -// region: 'us-east-1' -// }; +AWSConfig = { + accessKeyId: 'AKIAYE32SRU5XWXGIMV3', + secretAccessKey: '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', + sessionToken: '', + region: 'us-east-1' +}; // AzureConfig = { // ApplicationID: '', // A.K.A ClientID From ccb92eef611140d3c36698417db0755e6483ea74 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:42:00 +0500 Subject: [PATCH 13/71] Update index.js --- index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 262b4c8c73..019fc78f81 100644 --- a/index.js +++ b/index.js @@ -10,12 +10,12 @@ var GoogleConfig; // OPTION 1: Configure service provider credentials through hard-coded config objects -AWSConfig = { - accessKeyId: 'AKIAYE32SRU5XWXGIMV3', - secretAccessKey: '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', - sessionToken: '', - region: 'us-east-1' -}; +// AWSConfig = { +// accessKeyId: '', +// secretAccessKey: '', +// sessionToken: '', +// region: '' +// }; // AzureConfig = { // ApplicationID: '', // A.K.A ClientID From 93c553d4c7be0b44e7553149f7a2f3f461103008 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:48:02 +0500 Subject: [PATCH 14/71] Update index.js --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 019fc78f81..6f61d149a8 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ var GoogleConfig; // accessKeyId: '', // secretAccessKey: '', // sessionToken: '', -// region: '' +// region: 'us-east-1' // }; // AzureConfig = { From cf21d1d1513bef533823eb547389a0d3ede895cf Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 15:59:26 +0500 Subject: [PATCH 15/71] Added cloudformation in china and gov regions --- helpers/aws/regions_china.js | 1 + helpers/aws/regions_gov.js | 1 + 2 files changed, 2 insertions(+) diff --git a/helpers/aws/regions_china.js b/helpers/aws/regions_china.js index 18c482aca6..3d82dca40c 100644 --- a/helpers/aws/regions_china.js +++ b/helpers/aws/regions_china.js @@ -18,6 +18,7 @@ module.exports = { s3: regions, cloudtrail: regions, cloudwatchlogs: regions, + cloudformation: regions, configservice: regions, dms: regions, dynamodb: regions, diff --git a/helpers/aws/regions_gov.js b/helpers/aws/regions_gov.js index f84f8f923d..c55424fce6 100644 --- a/helpers/aws/regions_gov.js +++ b/helpers/aws/regions_gov.js @@ -18,6 +18,7 @@ module.exports = { s3: regions, cloudtrail: regions, cloudwatchlogs: regions, + cloudformation: regions, configservice: regions, dms: regions, dynamodb: regions, From aac8ece7116bcc8bf98a826339f543ecb69a5e10 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 20:15:26 +0500 Subject: [PATCH 16/71] Accomodated PR changes --- .../aws/cloudformation/plainTextParameters.js | 18 +-- .../plainTextParameters.spec.js | 144 +++++++++++------- 2 files changed, 95 insertions(+), 67 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 5f9906591c..e6e899268f 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -27,7 +27,6 @@ module.exports = { var describeStacks = helpers.addSource(cache, source, ['cloudformation', 'describeStacks', region]); - if (!describeStacks) return rcb(); if (describeStacks.err || !describeStacks.data) { @@ -45,7 +44,7 @@ module.exports = { // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id var stack = describeStacks.data[s]; var resource = stack.StackId; - let parameterFound = false; + let foundStrings = []; if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, @@ -54,18 +53,19 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { - parameterFound = true; - helpers.addResult(results, 1, - 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); + if(parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { + foundStrings.push(parameter.ParameterKey); } }); - - if(!parameterFound) { + + if(foundStrings && foundStrings.length) { + helpers.addResult(results, 1, + 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); + } + else { helpers.addResult(results, 0, 'Template does not contain any potentially-sensitive parameters', region, resource); } - } rcb(); diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index cdad731db6..d471a555f3 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -2,62 +2,81 @@ var expect = require('chai').expect; const plaintextParameters = require('./plainTextParameters'); const describeStacks = [ - { - StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', - StackName: 'TestStack', - Parameters: [ - { - ParameterKey: 'Secret', - ParameterValue: 'bucketwithsecretparameter1' - }, - { - ParameterKey: 'Password', - ParameterValue: 'bucketwithsecretparameter1' - } - ], - CreationTime: '2020-08-13T13:34:52.435Z', - RollbackConfiguration: { RollbackTriggers: [] }, - StackStatus: 'CREATE_COMPLETE', - DisableRollback: false, - NotificationARNs: [], - Capabilities: [], - Outputs: [], - Tags: [], - DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } - }, - { - StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', - StackName: 'TestStack', - Parameters: [ - { - ParameterKey: 'S3BucketName', - ParameterValue: 'testbucketplaintext1' - } - ], - CreationTime: '2020-08-12T09:42:04.803Z', - RollbackConfiguration: { RollbackTriggers: [] }, - StackStatus: 'CREATE_COMPLETE', - DisableRollback: false, - NotificationARNs: [], - Capabilities: [], - Outputs: [], - Tags: [], - DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } - }, - { - StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', - StackName: 'TestStack', - Parameters: [], - CreationTime: '2020-08-12T09:42:04.803Z', - RollbackConfiguration: { RollbackTriggers: [] }, - StackStatus: 'CREATE_COMPLETE', - DisableRollback: false, - NotificationARNs: [], - Capabilities: [], - Outputs: [], - Tags: [], - DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } - } + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [ + { + ParameterKey: 'Secret', + ParameterValue: 'bucketwithsecretparameter1' + }, + { + ParameterKey: 'Password', + ParameterValue: 'bucketwithsecretparameter1' + } + ], + CreationTime: '2020-08-13T13:34:52.435Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + }, + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [ + { + ParameterKey: 'S3BucketName', + ParameterValue: 'testbucketplaintext1' + } + ], + CreationTime: '2020-08-12T09:42:04.803Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + }, + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [ + { + ParameterKey: 'Secret', + ParameterValue: '****' + } + ], + CreationTime: '2020-08-13T13:34:52.435Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + }, + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [], + CreationTime: '2020-08-12T09:42:04.803Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + } ] const createCache = (stacks) => { @@ -98,7 +117,7 @@ const createNullCache = () => { describe('plaintextParameters', function () { describe('run', function () { - it('should WARN if template contains one of secret words ["password" , "privatekey", "secret"]', function (done) { + it('should WARN if template contains one of secret words', function (done) { const cache = createCache([describeStacks[0]]); plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); @@ -107,7 +126,7 @@ describe('plaintextParameters', function () { }); }); - it('should PASS if template does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { + it('should PASS if template does not contain any of secret words', function (done) { const cache = createCache([describeStacks[1]]); plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); @@ -116,6 +135,15 @@ describe('plaintextParameters', function () { }); }); + it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { + const cache = createCache([describeStacks[2]]); + plaintextParameters.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); plaintextParameters.run(cache, {}, (err, results) => { From 7707dbd49add468d82ae24466b8bda804e9c2f4b Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 21:12:35 +0500 Subject: [PATCH 17/71] Updated status in result of failure --- plugins/aws/cloudformation/plainTextParameters.js | 2 +- plugins/aws/cloudformation/plainTextParameters.spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index e6e899268f..6803911fef 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -59,7 +59,7 @@ module.exports = { }); if(foundStrings && foundStrings.length) { - helpers.addResult(results, 1, + helpers.addResult(results, 2, 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); } else { diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index d471a555f3..03e8abf7e9 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -117,11 +117,11 @@ const createNullCache = () => { describe('plaintextParameters', function () { describe('run', function () { - it('should WARN if template contains one of secret words', function (done) { + it('should FAIL if template contains one of secret words', function (done) { const cache = createCache([describeStacks[0]]); plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); - expect(results[0].status).to.equal(1); + expect(results[0].status).to.equal(2); done(); }); }); From a50876d1ca73b48ae9105553f76ddeae49c4150d Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:59:28 +0500 Subject: [PATCH 18/71] Added Same Availability Zone in ASG and ELB plugin --- exports.js | 1 + plugins/aws/autoscaling/sameAzElb.js | 89 ++++++ plugins/aws/autoscaling/sameAzElb.spec.js | 348 ++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 plugins/aws/autoscaling/sameAzElb.js create mode 100644 plugins/aws/autoscaling/sameAzElb.spec.js diff --git a/exports.js b/exports.js index 5a338cfa5e..cf69873f5b 100644 --- a/exports.js +++ b/exports.js @@ -5,6 +5,7 @@ module.exports = { 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), + 'sameAzElb' : require(__dirname + '/plugins/aws/autoscaling/sameAzElb.js'), 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js new file mode 100644 index 0000000000..12150b9994 --- /dev/null +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -0,0 +1,89 @@ +var async = require('async'); +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'AutoScaling Group Missing ELB', + category: 'AutoScaling', + description: 'Ensures all autoscaling groups are referencing active load balancers.', + more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', + link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', + recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', + apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancer'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(settings); + + async.each(regions.autoscaling, function(region, rcb){ + var autoScalingGroups = helpers.addSource(cache, source, + ['autoscaling', 'describeAutoScalingGroups', region]); + + if (!autoScalingGroups) return rcb(); + + if (autoScalingGroups.err || !autoScalingGroups.data) { + helpers.addResult(results, 3, + 'Unable to query for AutoScaling groups: ' + + helpers.addError(autoScalingGroups), region); + return rcb(); + } + + if (!autoScalingGroups.data.length) { + helpers.addResult(results, 0, 'No AutoScaling groups found', region); + return rcb(); + } + + autoScalingGroups.data.forEach(function(asg){ + asgAvailabilityZones = asg.AvailabilityZones; + if(asg.HealthCheckType == "ELB") { + if (asg.LoadBalancerNames) { + // create ELBs and check + asg.LoadBalancerNames.forEach(function(elbName){ + var elasticLoadBalancer = helpers.addSource(cache, source, + ['autoscaling', 'describeLoadBalancers', region, elbName]); + + if(!elasticLoadBalancer || !elasticLoadBalancer.data || !elasticLoadBalancer.data.LoadBalancerDescriptions) { + console.log('Load balancer not found in AutoScaling group ' + elbName); + helpers.addResult(results, 2, + 'Load balancer not found in AutoScaling group', + region, elbName); + } + else { + var differentAzFound = false; + elbAvailabilityZones = elasticLoadBalancer.LoadBalancerDescriptions.AvailabilityZones; + elbAvailabilityZones.foreach(function(elbAz){ + if(!asgAvailabilityZones.includes(elbAz)) { + differentAzFound = true; + } + }); + if(differentAzFound) { + console.log('Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group'); + helpers.addResult(results, 2, 'Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + } + else { + console.log('Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group'); + helpers.addResult(results, 0, 'Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + } + } + }); + } + else { + console.log('Load balancer not found in AutoScaling group'); + helpers.addResult(results, 2, + 'Load balancer not found in AutoScaling group', + region, elbName); + } + } + else { + console.log('AutoScaling group does not utilize a load balancer'); + helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, elbName); + } + + }); + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js new file mode 100644 index 0000000000..699fe97c62 --- /dev/null +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -0,0 +1,348 @@ +var expect = require('chai').expect; +const sameAzElb = require('./sameAzElb'); + +const autoScalingGroups = [ + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": ["my-load-balancer", "my-load-balancer2"], + "TargetGroupARNs": [], + "HealthCheckType": "EC2", + "HealthCheckGracePeriod": 300, + "Instances": [ + { + "InstanceId": "i-093267d7a579c4bee", + "InstanceType": "t2.micro", + "AvailabilityZone": "us-east-1a", + "LifecycleState": "InService", + "HealthStatus": "Healthy", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "1" + }, + "ProtectedFromScaleIn": false + } + ], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": ['my-load-balancer'], + "TargetGroupARNs": [], + "HealthCheckType": "ELB", + "HealthCheckGracePeriod": 300, + "Instances": [], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, +]; + +const loadBalancers = [ + { + "LoadBalancerDescriptions": [ + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + }, + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ + ], + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } + ], + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } + ] + }, + { + "LoadBalancerDescriptions": [ + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + }, + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ + ], + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } + ], + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } + ] + }, +]; + + +const createCache = (asgs, elb) => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': { + data: asgs + }, + }, + describeLoadBalancers: { + 'us-east-1': { + [asgs[0].LoadBalancerNames]: { + data: elb + }, + }, + }, + }, + }; +}; + +const createErrorCache = () => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': { + err: { + message: 'error describing autoscaling groups' + }, + }, + }, + }, + }; +}; + +const createNullCache = () => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': null, + }, + }, + }; +}; + +describe('sameAzElb', function () { + describe('run', function () { + it('should PASS if autoscaling group utilizes active load balancer', function (done) { + const cache = createCache([autoScalingGroups[1]], loadBalancers[0]); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should FAIL if autoscaling group does not utilizes active load balancer', function (done) { + const cache = createCache([autoScalingGroups[1]], []); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + done(); + }); + }); + + it('should UNKNOWN if unable to describe autoscaling group found', function (done) { + const cache = createErrorCache(); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + it('should not return anything if no autoscaling group found', function (done) { + const cache = createNullCache(); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(0); + done(); + }); + }); + + }); +}); \ No newline at end of file From a4ea92f1b8c5baa7867659a7315805b15b2bf203 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Fri, 14 Aug 2020 05:55:55 +0500 Subject: [PATCH 19/71] SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation --- exports.js | 7 ++++ index.js | 8 ++++- .../plainTextParameters.spec.js | 32 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/exports.js b/exports.js index 5a338cfa5e..b3eca51f7d 100644 --- a/exports.js +++ b/exports.js @@ -13,9 +13,13 @@ module.exports = { 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), +<<<<<<< HEAD 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), +======= + 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), @@ -31,7 +35,10 @@ module.exports = { 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), +<<<<<<< HEAD 'launchWizardSecurityGroups' : require(__dirname + '/plugins/aws/ec2/launchWizardSecurityGroups'), +======= +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), diff --git a/index.js b/index.js index 6f61d149a8..2f90a29fa4 100644 --- a/index.js +++ b/index.js @@ -113,7 +113,13 @@ if(process.env.GOOGLE_APPLICATION_CREDENTIALS){ } // Custom settings - place plugin-specific settings here -var settings = {}; +var settings = { + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] + } +}; // If running in GovCloud, uncomment the following // settings.govcloud = true; diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index 03e8abf7e9..cdf14fb564 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -115,6 +115,7 @@ const createNullCache = () => { }; }; +<<<<<<< HEAD describe('plaintextParameters', function () { describe('run', function () { it('should FAIL if template contains one of secret words', function (done) { @@ -122,10 +123,20 @@ describe('plaintextParameters', function () { plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); +======= +describe('plainTextParameters', function () { + describe('run', function () { + it('should FAIL if Stack parameters contain one of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[0]]); + plainTextParameters.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(1); +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation done(); }); }); +<<<<<<< HEAD it('should PASS if template does not contain any of secret words', function (done) { const cache = createCache([describeStacks[1]]); plaintextParameters.run(cache, {}, (err, results) => { @@ -138,6 +149,11 @@ describe('plaintextParameters', function () { it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { const cache = createCache([describeStacks[2]]); plaintextParameters.run(cache, {}, (err, results) => { +======= + it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[1]]); + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -146,7 +162,11 @@ describe('plaintextParameters', function () { it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); +<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { +======= + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -155,7 +175,11 @@ describe('plaintextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); +<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { +======= + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -164,7 +188,11 @@ describe('plaintextParameters', function () { it('should not return any results if unable to fetch any stack description', function (done) { const cache = createNullCache(); +<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { +======= + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(0); done(); }); @@ -172,7 +200,11 @@ describe('plaintextParameters', function () { it('should UNKNOWN if error occurs while fetching stack description', function (done) { const cache = createErrorCache(); +<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { +======= + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); From 8e5b6be04baa584ba2e3aafc64076ad49175f6ef Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 15:30:57 +0500 Subject: [PATCH 20/71] Added plugin and spec file for launch wizard security groups --- exports.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/exports.js b/exports.js index b3eca51f7d..5a338cfa5e 100644 --- a/exports.js +++ b/exports.js @@ -13,13 +13,9 @@ module.exports = { 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), -<<<<<<< HEAD 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), -======= - 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), @@ -35,10 +31,7 @@ module.exports = { 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), -<<<<<<< HEAD 'launchWizardSecurityGroups' : require(__dirname + '/plugins/aws/ec2/launchWizardSecurityGroups'), -======= ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), From a9afe440de80c185bdd8df63bad1c28c990daa99 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Thu, 13 Aug 2020 19:59:59 +0500 Subject: [PATCH 21/71] Added vpcEndpointAcceptance plugin and spec file --- plugins/aws/ec2/vpcEndpointAcceptance.spec.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js index 62053b7238..0946f5a682 100644 --- a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js +++ b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js @@ -112,6 +112,15 @@ describe('vpcEndpointAcceptance', function () { }); }); + it('should PASS if no VPC endpoint services are detected', function (done) { + const cache = createCache([]); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + it('should UNKNOWN if there was an error querying for VPC endpoint services', function (done) { const cache = createErrorCache(); vpcEndpointAcceptance.run(cache, {}, (err, results) => { From 0323a988487eb779a7897c75f464d38217877c19 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 16:50:46 +0500 Subject: [PATCH 22/71] Refactored code in plaintextParameters plugin and spec file --- index.js | 8 +--- .../aws/cloudformation/plainTextParameters.js | 22 +++++++++-- .../plainTextParameters.spec.js | 38 ++++++------------- 3 files changed, 31 insertions(+), 37 deletions(-) diff --git a/index.js b/index.js index 2f90a29fa4..6f61d149a8 100644 --- a/index.js +++ b/index.js @@ -113,13 +113,7 @@ if(process.env.GOOGLE_APPLICATION_CREDENTIALS){ } // Custom settings - place plugin-specific settings here -var settings = { - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] - } -}; +var settings = {}; // If running in GovCloud, uncomment the following // settings.govcloud = true; diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 6803911fef..912e7f4eda 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -10,11 +10,18 @@ module.exports = { recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], settings: { +<<<<<<< HEAD plain_text_parameters: { name: 'CloudFormation Plaintext Parameters', description: 'A comma-delimited list of parameter strings that indicate a sensitive value', regex: '[a-zA-Z0-9,]', default: 'secret,password,privatekey' +======= + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] +>>>>>>> 3da6672... Refactored code in plaintextParameters plugin and spec file } }, @@ -22,7 +29,12 @@ module.exports = { var results = []; var source = {}; var regions = helpers.regions(settings); +<<<<<<< HEAD var secretWords = this.settings.plain_text_parameters.default; +======= + secretWords = this.settings.plainTextParameters.secretWords; + +>>>>>>> 3da6672... Refactored code in plaintextParameters plugin and spec file async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, @@ -48,13 +60,15 @@ module.exports = { if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'Template does not contain any parameters', region, resource); - continue; + 'The template does not contain any potentially-sensitive parameters', region, resource); + return rcb(); } stack.Parameters.forEach(function(parameter){ - if(parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { - foundStrings.push(parameter.ParameterKey); + if(!parameterFound && secretWords.includes(parameter.ParameterKey.toLowerCase())) { + parameterFound = true; + helpers.addResult(results, 1, + 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); } }); diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index cdf14fb564..d7ac6fed08 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -115,7 +115,7 @@ const createNullCache = () => { }; }; -<<<<<<< HEAD + describe('plaintextParameters', function () { describe('run', function () { it('should FAIL if template contains one of secret words', function (done) { @@ -123,20 +123,18 @@ describe('plaintextParameters', function () { plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); -======= -describe('plainTextParameters', function () { - describe('run', function () { - it('should FAIL if Stack parameters contain one of secret words ["password" , "privatekey", "secret"]', function (done) { + }); + }); + + it('should WARN if template contains one of secret words ["password" , "privatekey", "secret"]', function (done) { const cache = createCache([describeStacks[0]]); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(1); ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation done(); }); }); -<<<<<<< HEAD it('should PASS if template does not contain any of secret words', function (done) { const cache = createCache([describeStacks[1]]); plaintextParameters.run(cache, {}, (err, results) => { @@ -149,11 +147,12 @@ describe('plainTextParameters', function () { it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { const cache = createCache([describeStacks[2]]); plaintextParameters.run(cache, {}, (err, results) => { -======= it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { const cache = createCache([describeStacks[1]]); plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation + it('should PASS if template does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[1]]); + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -162,11 +161,8 @@ describe('plainTextParameters', function () { it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); -<<<<<<< HEAD + plaintextParameters.run(cache, {}, (err, results) => { -======= - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -175,11 +171,8 @@ describe('plainTextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); -<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { -======= - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -188,11 +181,8 @@ describe('plainTextParameters', function () { it('should not return any results if unable to fetch any stack description', function (done) { const cache = createNullCache(); -<<<<<<< HEAD + plaintextParameters.run(cache, {}, (err, results) => { -======= - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(0); done(); }); @@ -200,11 +190,7 @@ describe('plainTextParameters', function () { it('should UNKNOWN if error occurs while fetching stack description', function (done) { const cache = createErrorCache(); -<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { -======= - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); From aab9f79d36b4ead3765e8f14871f9e7b54959990 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Fri, 14 Aug 2020 18:09:45 +0500 Subject: [PATCH 23/71] SPLOIT-113: Updated custom settings --- plugins/aws/cloudformation/plainTextParameters.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 912e7f4eda..487cac1ccc 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -10,18 +10,12 @@ module.exports = { recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], settings: { -<<<<<<< HEAD + plain_text_parameters: { name: 'CloudFormation Plaintext Parameters', description: 'A comma-delimited list of parameter strings that indicate a sensitive value', regex: '[a-zA-Z0-9,]', default: 'secret,password,privatekey' -======= - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] ->>>>>>> 3da6672... Refactored code in plaintextParameters plugin and spec file } }, @@ -29,12 +23,8 @@ module.exports = { var results = []; var source = {}; var regions = helpers.regions(settings); -<<<<<<< HEAD var secretWords = this.settings.plain_text_parameters.default; -======= - secretWords = this.settings.plainTextParameters.secretWords; ->>>>>>> 3da6672... Refactored code in plaintextParameters plugin and spec file async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, From 12d88c1f2b7f80c945494189d9df30615e60a929 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 21:24:30 +0500 Subject: [PATCH 24/71] Made PR requested changes --- plugins/aws/cloudformation/plainTextParameters.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 487cac1ccc..b41ada95bc 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -24,7 +24,6 @@ module.exports = { var source = {}; var regions = helpers.regions(settings); var secretWords = this.settings.plain_text_parameters.default; - async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, @@ -50,12 +49,12 @@ module.exports = { if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'The template does not contain any potentially-sensitive parameters', region, resource); + 'Template does not contain any potentially-sensitive parameters', region, resource); return rcb(); } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && secretWords.includes(parameter.ParameterKey.toLowerCase())) { + if((!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()))) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); From 7dd1a5b4c70e20e199b066877fef1a606e508b3d Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 15 Aug 2020 01:07:50 +0500 Subject: [PATCH 25/71] SPLOIT-113: Added regex to check if NoEcho is enabled --- plugins/aws/cloudformation/plainTextParameters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index b41ada95bc..3b1eeb2f8b 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -54,7 +54,7 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if((!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()))) { + if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match("^[\*]+$")) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); From c8a23c3104275291d6fccd34ce4007beb2bbd90a Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 04:13:40 +0500 Subject: [PATCH 26/71] Accommodated PR changes --- plugins/aws/cloudformation/plainTextParameters.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 3b1eeb2f8b..ee646df8dd 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -49,8 +49,8 @@ module.exports = { if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'Template does not contain any potentially-sensitive parameters', region, resource); - return rcb(); + 'Template does not contain any parameters', region, resource); + continue; } stack.Parameters.forEach(function(parameter){ From 92821dd47008f13bc10143dd8c3b64db692b1f55 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 06:09:46 +0500 Subject: [PATCH 27/71] Fixed eslint issues --- plugins/aws/cloudformation/plainTextParameters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index ee646df8dd..32db53c891 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -54,7 +54,7 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match("^[\*]+$")) { + if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); From e32accc18389dc0e73db708f5bf184738bdab68c Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:42:00 +0500 Subject: [PATCH 28/71] Update index.js --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 6f61d149a8..019fc78f81 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ var GoogleConfig; // accessKeyId: '', // secretAccessKey: '', // sessionToken: '', -// region: 'us-east-1' +// region: '' // }; // AzureConfig = { From 90094a2c0401ac04f24fd08c5c7309596ae77200 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:48:02 +0500 Subject: [PATCH 29/71] Update index.js --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 019fc78f81..6f61d149a8 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ var GoogleConfig; // accessKeyId: '', // secretAccessKey: '', // sessionToken: '', -// region: '' +// region: 'us-east-1' // }; // AzureConfig = { From c29ab07cfdc4dde85c3d6b5468f3c69738643286 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 20:15:26 +0500 Subject: [PATCH 30/71] Accomodated PR changes --- plugins/aws/cloudformation/plainTextParameters.js | 10 ++++++---- .../cloudformation/plainTextParameters.spec.js | 15 ++++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 32db53c891..e98c754121 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -54,15 +54,17 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { - parameterFound = true; - helpers.addResult(results, 1, - 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); + if(parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { + foundStrings.push(parameter.ParameterKey); } }); if(foundStrings && foundStrings.length) { +<<<<<<< HEAD helpers.addResult(results, 2, +======= + helpers.addResult(results, 1, +>>>>>>> aac8ece... Accomodated PR changes 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); } else { diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index d7ac6fed08..7493e245b3 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -147,11 +147,14 @@ describe('plaintextParameters', function () { it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { const cache = createCache([describeStacks[2]]); plaintextParameters.run(cache, {}, (err, results) => { - it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { - const cache = createCache([describeStacks[1]]); - plainTextParameters.run(cache, settings, (err, results) => { - it('should PASS if template does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { - const cache = createCache([describeStacks[1]]); + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { + const cache = createCache([describeStacks[2]]); plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -161,7 +164,6 @@ describe('plaintextParameters', function () { it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); - plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -171,7 +173,6 @@ describe('plaintextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); - plaintextParameters.run(cache, {}, (err, results) => { plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); From 49f12025532e2a62511d4a7bf60af22af0060f9d Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 21:12:35 +0500 Subject: [PATCH 31/71] Updated status in result of failure --- plugins/aws/cloudformation/plainTextParameters.js | 4 ---- plugins/aws/cloudformation/plainTextParameters.spec.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index e98c754121..00289847cb 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -60,11 +60,7 @@ module.exports = { }); if(foundStrings && foundStrings.length) { -<<<<<<< HEAD helpers.addResult(results, 2, -======= - helpers.addResult(results, 1, ->>>>>>> aac8ece... Accomodated PR changes 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); } else { diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index 7493e245b3..c590d10ce9 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -130,7 +130,7 @@ describe('plaintextParameters', function () { const cache = createCache([describeStacks[0]]); plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); - expect(results[0].status).to.equal(1); + expect(results[0].status).to.equal(2); done(); }); }); From 9ceec61dd66dd8f95f446980f38cf986cbb3bef3 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:59:28 +0500 Subject: [PATCH 32/71] Added Same Availability Zone in ASG and ELB plugin --- exports.js | 1 + plugins/aws/autoscaling/sameAzElb.js | 89 ++++++ plugins/aws/autoscaling/sameAzElb.spec.js | 348 ++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 plugins/aws/autoscaling/sameAzElb.js create mode 100644 plugins/aws/autoscaling/sameAzElb.spec.js diff --git a/exports.js b/exports.js index 5a338cfa5e..cf69873f5b 100644 --- a/exports.js +++ b/exports.js @@ -5,6 +5,7 @@ module.exports = { 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), + 'sameAzElb' : require(__dirname + '/plugins/aws/autoscaling/sameAzElb.js'), 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js new file mode 100644 index 0000000000..12150b9994 --- /dev/null +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -0,0 +1,89 @@ +var async = require('async'); +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'AutoScaling Group Missing ELB', + category: 'AutoScaling', + description: 'Ensures all autoscaling groups are referencing active load balancers.', + more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', + link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', + recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', + apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancer'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(settings); + + async.each(regions.autoscaling, function(region, rcb){ + var autoScalingGroups = helpers.addSource(cache, source, + ['autoscaling', 'describeAutoScalingGroups', region]); + + if (!autoScalingGroups) return rcb(); + + if (autoScalingGroups.err || !autoScalingGroups.data) { + helpers.addResult(results, 3, + 'Unable to query for AutoScaling groups: ' + + helpers.addError(autoScalingGroups), region); + return rcb(); + } + + if (!autoScalingGroups.data.length) { + helpers.addResult(results, 0, 'No AutoScaling groups found', region); + return rcb(); + } + + autoScalingGroups.data.forEach(function(asg){ + asgAvailabilityZones = asg.AvailabilityZones; + if(asg.HealthCheckType == "ELB") { + if (asg.LoadBalancerNames) { + // create ELBs and check + asg.LoadBalancerNames.forEach(function(elbName){ + var elasticLoadBalancer = helpers.addSource(cache, source, + ['autoscaling', 'describeLoadBalancers', region, elbName]); + + if(!elasticLoadBalancer || !elasticLoadBalancer.data || !elasticLoadBalancer.data.LoadBalancerDescriptions) { + console.log('Load balancer not found in AutoScaling group ' + elbName); + helpers.addResult(results, 2, + 'Load balancer not found in AutoScaling group', + region, elbName); + } + else { + var differentAzFound = false; + elbAvailabilityZones = elasticLoadBalancer.LoadBalancerDescriptions.AvailabilityZones; + elbAvailabilityZones.foreach(function(elbAz){ + if(!asgAvailabilityZones.includes(elbAz)) { + differentAzFound = true; + } + }); + if(differentAzFound) { + console.log('Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group'); + helpers.addResult(results, 2, 'Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + } + else { + console.log('Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group'); + helpers.addResult(results, 0, 'Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + } + } + }); + } + else { + console.log('Load balancer not found in AutoScaling group'); + helpers.addResult(results, 2, + 'Load balancer not found in AutoScaling group', + region, elbName); + } + } + else { + console.log('AutoScaling group does not utilize a load balancer'); + helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, elbName); + } + + }); + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js new file mode 100644 index 0000000000..699fe97c62 --- /dev/null +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -0,0 +1,348 @@ +var expect = require('chai').expect; +const sameAzElb = require('./sameAzElb'); + +const autoScalingGroups = [ + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": ["my-load-balancer", "my-load-balancer2"], + "TargetGroupARNs": [], + "HealthCheckType": "EC2", + "HealthCheckGracePeriod": 300, + "Instances": [ + { + "InstanceId": "i-093267d7a579c4bee", + "InstanceType": "t2.micro", + "AvailabilityZone": "us-east-1a", + "LifecycleState": "InService", + "HealthStatus": "Healthy", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "1" + }, + "ProtectedFromScaleIn": false + } + ], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": ['my-load-balancer'], + "TargetGroupARNs": [], + "HealthCheckType": "ELB", + "HealthCheckGracePeriod": 300, + "Instances": [], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, +]; + +const loadBalancers = [ + { + "LoadBalancerDescriptions": [ + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + }, + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ + ], + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } + ], + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } + ] + }, + { + "LoadBalancerDescriptions": [ + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + }, + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ + ], + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } + ], + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } + ] + }, +]; + + +const createCache = (asgs, elb) => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': { + data: asgs + }, + }, + describeLoadBalancers: { + 'us-east-1': { + [asgs[0].LoadBalancerNames]: { + data: elb + }, + }, + }, + }, + }; +}; + +const createErrorCache = () => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': { + err: { + message: 'error describing autoscaling groups' + }, + }, + }, + }, + }; +}; + +const createNullCache = () => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': null, + }, + }, + }; +}; + +describe('sameAzElb', function () { + describe('run', function () { + it('should PASS if autoscaling group utilizes active load balancer', function (done) { + const cache = createCache([autoScalingGroups[1]], loadBalancers[0]); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should FAIL if autoscaling group does not utilizes active load balancer', function (done) { + const cache = createCache([autoScalingGroups[1]], []); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + done(); + }); + }); + + it('should UNKNOWN if unable to describe autoscaling group found', function (done) { + const cache = createErrorCache(); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + it('should not return anything if no autoscaling group found', function (done) { + const cache = createNullCache(); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(0); + done(); + }); + }); + + }); +}); \ No newline at end of file From 5ad291e80e1cdc69a663c10746e9bf4168695ff5 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 22 Aug 2020 07:24:45 +0500 Subject: [PATCH 33/71] Feature/43: Updated describeLoadBalancers api call --- plugins/aws/autoscaling/sameAzElb.js | 70 ++-- plugins/aws/autoscaling/sameAzElb.spec.js | 400 +++++++++++++--------- 2 files changed, 269 insertions(+), 201 deletions(-) diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index 12150b9994..9988c65be0 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -8,7 +8,7 @@ module.exports = { more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', - apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancer'], + apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers'], run: function(cache, settings, callback) { var results = []; @@ -18,13 +18,20 @@ module.exports = { async.each(regions.autoscaling, function(region, rcb){ var autoScalingGroups = helpers.addSource(cache, source, ['autoscaling', 'describeAutoScalingGroups', region]); + + var elasticLoadBalancers = helpers.addSource(cache, source, + ['elb', 'describeLoadBalancers', region]); + + if (!autoScalingGroups || !elasticLoadBalancers) return rcb(); - if (!autoScalingGroups) return rcb(); if (autoScalingGroups.err || !autoScalingGroups.data) { - helpers.addResult(results, 3, - 'Unable to query for AutoScaling groups: ' + - helpers.addError(autoScalingGroups), region); + helpers.addResult(results, 3, 'Unable to query for AutoScaling groups: ' + helpers.addError(autoScalingGroups), region); + return rcb(); + } + + if (elasticLoadBalancers.err || !elasticLoadBalancers.data) { + helpers.addResult(results, 3, 'Unable to query for load balancers: ' + helpers.addError(elasticLoadBalancers), region); return rcb(); } @@ -34,51 +41,48 @@ module.exports = { } autoScalingGroups.data.forEach(function(asg){ - asgAvailabilityZones = asg.AvailabilityZones; - if(asg.HealthCheckType == "ELB") { - if (asg.LoadBalancerNames) { - // create ELBs and check - asg.LoadBalancerNames.forEach(function(elbName){ - var elasticLoadBalancer = helpers.addSource(cache, source, - ['autoscaling', 'describeLoadBalancers', region, elbName]); + var asgAvailabilityZones = asg.AvailabilityZones; + var asgName = asg.AutoScalingGroupName; + var differentAzFound = false; + + if(asg.HealthCheckType == 'ELB') { + if (asg.LoadBalancerNames && asg.LoadBalancerNames.length) { - if(!elasticLoadBalancer || !elasticLoadBalancer.data || !elasticLoadBalancer.data.LoadBalancerDescriptions) { - console.log('Load balancer not found in AutoScaling group ' + elbName); - helpers.addResult(results, 2, - 'Load balancer not found in AutoScaling group', - region, elbName); - } - else { - var differentAzFound = false; - elbAvailabilityZones = elasticLoadBalancer.LoadBalancerDescriptions.AvailabilityZones; - elbAvailabilityZones.foreach(function(elbAz){ + if (!elasticLoadBalancers.data.length) { + helpers.addResult(results, 2, 'No load balancers found', region); + return rcb(); + } + + elasticLoadBalancers.data.forEach(function(elb) { + var elbName = elb.LoadBalancerName; + + if(asg.LoadBalancerNames.includes(elb.LoadBalancerName)){ + differentAzFound = false; + var elbAvailabilityZones = elb.AvailabilityZones; + + //if Load Balancer is in any AZ different from AutoScaling group's AZs then mark ELB as different + elbAvailabilityZones.forEach(function(elbAz){ if(!asgAvailabilityZones.includes(elbAz)) { differentAzFound = true; } }); + if(differentAzFound) { - console.log('Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group'); - helpers.addResult(results, 2, 'Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + helpers.addResult(results, 2, 'Load balancer "' + elbName + '" is not in the same AZ as of AutoScaling group', region, asgName); } else { - console.log('Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group'); - helpers.addResult(results, 0, 'Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + helpers.addResult(results, 0, 'Load balancer "' + elbName + '" is in the same AZ as of AutoScaling group', region, asgName); } } }); } else { - console.log('Load balancer not found in AutoScaling group'); - helpers.addResult(results, 2, - 'Load balancer not found in AutoScaling group', - region, elbName); + helpers.addResult(results, 2, 'AutoScaling group does not have any Load Balancer associated', region, asgName); } } else { - console.log('AutoScaling group does not utilize a load balancer'); - helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, elbName); + helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, asgName); } - }); rcb(); diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js index 699fe97c62..32a43489d4 100644 --- a/plugins/aws/autoscaling/sameAzElb.spec.js +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -17,9 +17,9 @@ const autoScalingGroups = [ "AvailabilityZones": [ "us-east-1a" ], - "LoadBalancerNames": ["my-load-balancer", "my-load-balancer2"], + "LoadBalancerNames": ["my-load-balancer"], "TargetGroupARNs": [], - "HealthCheckType": "EC2", + "HealthCheckType": "ELB", "HealthCheckGracePeriod": 300, "Instances": [ { @@ -78,189 +78,226 @@ const autoScalingGroups = [ "NewInstancesProtectedFromScaleIn": false, "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": [], + "TargetGroupARNs": [], + "HealthCheckType": "EC2", + "HealthCheckGracePeriod": 300, + "Instances": [ + { + "InstanceId": "i-093267d7a579c4bee", + "InstanceType": "t2.micro", + "AvailabilityZone": "us-east-1a", + "LifecycleState": "InService", + "HealthStatus": "Healthy", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "1" + }, + "ProtectedFromScaleIn": false + } + ], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, ]; const loadBalancers = [ { - "LoadBalancerDescriptions": [ + "AvailabilityZones": [ + "us-east-1a" + ], + "BackendServerDescriptions": [ { - "AvailabilityZones": [ - "us-west-2a" - ], - "BackendServerDescriptions": [ - { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { "InstancePort": 80, - "PolicyNames": [ - "my-ProxyProtocol-policy" - ] - } - ], - "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", - "CreatedTime": "2020-08-18T23:12:00.954Z", - "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "HealthCheck": { - "HealthyThreshold": 2, - "Interval": 30, - "Target": "HTTP:80/png", - "Timeout": 3, - "UnhealthyThreshold": 2 + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + ] }, - "Instances": [ - { - "InstanceId": "i-207d9717" + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" }, - { - "InstanceId": "i-afefb49b" - } + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ ], - "ListenerDescriptions": [ - { - "Listener": { - "InstancePort": 80, - "InstanceProtocol": "HTTP", - "LoadBalancerPort": 80, - "Protocol": "HTTP" - }, - "PolicyNames": [ - ] - }, + "LBCookieStickinessPolicies": [ { - "Listener": { - "InstancePort": 443, - "InstanceProtocol": "HTTPS", - "LoadBalancerPort": 443, - "Protocol": "HTTPS", - "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" - }, - "PolicyNames": [ - "ELBSecurityPolicy-2015-03" - ] + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" } ], - "LoadBalancerName": "my-load-balancer", - "Policies": { - "AppCookieStickinessPolicies": [ - ], - "LBCookieStickinessPolicies": [ - { - "CookieExpirationPeriod": 60, - "PolicyName": "my-duration-cookie-policy" - } - ], - "OtherPolicies": [ - "my-PublicKey-policy", - "my-authentication-policy", - "my-SSLNegotiation-policy", - "my-ProxyProtocol-policy", - "ELBSecurityPolicy-2015-03" + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + }, + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" }, - "Scheme": "internet-facing", - "SecurityGroups": [ - "sg-a61988c3" - ], - "SourceSecurityGroup": { - "GroupName": "my-elb-sg", - "OwnerAlias": "123456789012" - }, - "Subnets": [ - "subnet-15aaab61" - ], - "VPCId": "vpc-a01106c2" + { + "InstanceId": "i-afefb49b" } - ] - }, - { - "LoadBalancerDescriptions": [ + ], + "ListenerDescriptions": [ { - "AvailabilityZones": [ - "us-west-2a" - ], - "BackendServerDescriptions": [ - { + "Listener": { "InstancePort": 80, - "PolicyNames": [ - "my-ProxyProtocol-policy" - ] - } - ], - "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", - "CreatedTime": "2020-08-18T23:12:00.954Z", - "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "HealthCheck": { - "HealthyThreshold": 2, - "Interval": 30, - "Target": "HTTP:80/png", - "Timeout": 3, - "UnhealthyThreshold": 2 - }, - "Instances": [ - { - "InstanceId": "i-207d9717" + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" }, - { - "InstanceId": "i-afefb49b" - } - ], - "ListenerDescriptions": [ - { - "Listener": { - "InstancePort": 80, - "InstanceProtocol": "HTTP", - "LoadBalancerPort": 80, - "Protocol": "HTTP" - }, - "PolicyNames": [ - ] + "PolicyNames": [ + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" }, - { - "Listener": { - "InstancePort": 443, - "InstanceProtocol": "HTTPS", - "LoadBalancerPort": 443, - "Protocol": "HTTPS", - "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" - }, - "PolicyNames": [ - "ELBSecurityPolicy-2015-03" - ] - } - ], - "LoadBalancerName": "my-load-balancer", - "Policies": { - "AppCookieStickinessPolicies": [ - ], - "LBCookieStickinessPolicies": [ - { - "CookieExpirationPeriod": 60, - "PolicyName": "my-duration-cookie-policy" - } - ], - "OtherPolicies": [ - "my-PublicKey-policy", - "my-authentication-policy", - "my-SSLNegotiation-policy", - "my-ProxyProtocol-policy", + "PolicyNames": [ "ELBSecurityPolicy-2015-03" ] - }, - "Scheme": "internet-facing", - "SecurityGroups": [ - "sg-a61988c3" + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ ], - "SourceSecurityGroup": { - "GroupName": "my-elb-sg", - "OwnerAlias": "123456789012" - }, - "Subnets": [ - "subnet-15aaab61" + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } ], - "VPCId": "vpc-a01106c2" - } - ] - }, + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } ]; @@ -272,11 +309,11 @@ const createCache = (asgs, elb) => { data: asgs }, }, + }, + elb:{ describeLoadBalancers: { 'us-east-1': { - [asgs[0].LoadBalancerNames]: { - data: elb - }, + data: elb }, }, }, @@ -294,6 +331,15 @@ const createErrorCache = () => { }, }, }, + elb: { + describeLoadBalancers: { + 'us-east-1': { + err: { + message: 'error describing load balancers' + }, + }, + }, + }, }; }; @@ -309,8 +355,17 @@ const createNullCache = () => { describe('sameAzElb', function () { describe('run', function () { - it('should PASS if autoscaling group utilizes active load balancer', function (done) { - const cache = createCache([autoScalingGroups[1]], loadBalancers[0]); + it('should PASS if load balancer is in the same Availability Zone as of AutoScaling group', function (done) { + const cache = createCache([autoScalingGroups[0]], [loadBalancers[0]]); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should PASS if AutoScaling does not utilizes load balancer as HealthCheckType', function (done) { + const cache = createCache([autoScalingGroups[2]], [loadBalancers[1]]); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -318,7 +373,16 @@ describe('sameAzElb', function () { }); }); - it('should FAIL if autoscaling group does not utilizes active load balancer', function (done) { + it('should FAIL if load balancer is not in the same Availability Zone as of AutoScaling group', function (done) { + const cache = createCache([autoScalingGroups[1]], [loadBalancers[1]]); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + done(); + }); + }); + + it('should FAIL if autoscaling group utilizes an inactive load balancer', function (done) { const cache = createCache([autoScalingGroups[1]], []); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); From 357cd0b45590f5b8c80966660fbbc6ac55e29ff8 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Fri, 14 Aug 2020 05:55:55 +0500 Subject: [PATCH 34/71] SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation --- exports.js | 7 +++++ index.js | 16 ++++++++++ .../plainTextParameters.spec.js | 29 +++++++------------ 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/exports.js b/exports.js index 7139c7640e..c29e867d52 100644 --- a/exports.js +++ b/exports.js @@ -14,9 +14,13 @@ module.exports = { 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), +<<<<<<< HEAD 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), +======= + 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), @@ -32,7 +36,10 @@ module.exports = { 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), +<<<<<<< HEAD 'launchWizardSecurityGroups' : require(__dirname + '/plugins/aws/ec2/launchWizardSecurityGroups'), +======= +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), diff --git a/index.js b/index.js index 26e612ef56..993593d97f 100755 --- a/index.js +++ b/index.js @@ -96,6 +96,22 @@ try { console.error('ERROR: Config file could not be loaded. Please ensure you have copied the config_example.js file to config.js'); process.exit(1); } +<<<<<<< HEAD +======= +if(process.env.GOOGLE_APPLICATION_CREDENTIALS){ + GoogleConfig = require(process.env.GOOGLE_APPLICATION_CREDENTIALS); + GoogleConfig.project = GoogleConfig.project_id; +} + +// Custom settings - place plugin-specific settings here +var settings = { + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] + } +}; +>>>>>>> a4ea92f... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation function loadHelperFile(path) { try { diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index 03e8abf7e9..b75a66ddb5 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -115,29 +115,20 @@ const createNullCache = () => { }; }; -describe('plaintextParameters', function () { +describe('plainTextParameters', function () { describe('run', function () { - it('should FAIL if template contains one of secret words', function (done) { + it('should FAIL if Stack parameters contain one of secret words ["password" , "privatekey", "secret"]', function (done) { const cache = createCache([describeStacks[0]]); - plaintextParameters.run(cache, {}, (err, results) => { + plainTextParameters.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); - expect(results[0].status).to.equal(2); + expect(results[0].status).to.equal(1); done(); }); }); - it('should PASS if template does not contain any of secret words', function (done) { + it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { const cache = createCache([describeStacks[1]]); - plaintextParameters.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - done(); - }); - }); - - it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { - const cache = createCache([describeStacks[2]]); - plaintextParameters.run(cache, {}, (err, results) => { + plainTextParameters.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -146,7 +137,7 @@ describe('plaintextParameters', function () { it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); - plaintextParameters.run(cache, {}, (err, results) => { + plainTextParameters.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -155,7 +146,7 @@ describe('plaintextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); - plaintextParameters.run(cache, {}, (err, results) => { + plainTextParameters.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -164,7 +155,7 @@ describe('plaintextParameters', function () { it('should not return any results if unable to fetch any stack description', function (done) { const cache = createNullCache(); - plaintextParameters.run(cache, {}, (err, results) => { + plainTextParameters.run(cache, settings, (err, results) => { expect(results.length).to.equal(0); done(); }); @@ -172,7 +163,7 @@ describe('plaintextParameters', function () { it('should UNKNOWN if error occurs while fetching stack description', function (done) { const cache = createErrorCache(); - plaintextParameters.run(cache, {}, (err, results) => { + plainTextParameters.run(cache, settings, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); From 77ca002dbdc1dcaac51452167b071eb1dd9178cf Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 15:30:57 +0500 Subject: [PATCH 35/71] Added plugin and spec file for launch wizard security groups --- exports.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/exports.js b/exports.js index c29e867d52..7139c7640e 100644 --- a/exports.js +++ b/exports.js @@ -14,13 +14,9 @@ module.exports = { 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), -<<<<<<< HEAD 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), -======= - 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), @@ -36,10 +32,7 @@ module.exports = { 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), -<<<<<<< HEAD 'launchWizardSecurityGroups' : require(__dirname + '/plugins/aws/ec2/launchWizardSecurityGroups'), -======= ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), From 73172b8eb8842afc4f6a69ba82aa763f9d8eacdf Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Thu, 13 Aug 2020 19:59:59 +0500 Subject: [PATCH 36/71] Added vpcEndpointAcceptance plugin and spec file --- plugins/aws/ec2/vpcEndpointAcceptance.spec.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js index 62053b7238..0946f5a682 100644 --- a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js +++ b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js @@ -112,6 +112,15 @@ describe('vpcEndpointAcceptance', function () { }); }); + it('should PASS if no VPC endpoint services are detected', function (done) { + const cache = createCache([]); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + it('should UNKNOWN if there was an error querying for VPC endpoint services', function (done) { const cache = createErrorCache(); vpcEndpointAcceptance.run(cache, {}, (err, results) => { From 7b448ff450d02aeda95714087905d9b060421bd1 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 16:50:46 +0500 Subject: [PATCH 37/71] Refactored code in plaintextParameters plugin and spec file --- .../aws/cloudformation/plainTextParameters.js | 22 +++++----- .../plainTextParameters.spec.js | 41 +++++++++++++++---- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index e03a98bd08..0b3e156b8a 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -10,11 +10,10 @@ module.exports = { recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], settings: { - plain_text_parameters: { - name: 'CloudFormation Plaintext Parameters', - description: 'A comma-delimited list of parameter strings that indicate a sensitive value', - regex: '[a-zA-Z0-9,]', - default: 'secret,password,privatekey' + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] } }, @@ -22,7 +21,8 @@ module.exports = { var results = []; var source = {}; var regions = helpers.regions(settings); - var secretWords = this.settings.plain_text_parameters.default; + secretWords = this.settings.plainTextParameters.secretWords; + async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, @@ -48,13 +48,15 @@ module.exports = { if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'Template does not contain any parameters', region, resource); - continue; + 'The template does not contain any potentially-sensitive parameters', region, resource); + return rcb(); } stack.Parameters.forEach(function(parameter){ - if(parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { - foundStrings.push(parameter.ParameterKey); + if(!parameterFound && secretWords.includes(parameter.ParameterKey.toLowerCase())) { + parameterFound = true; + helpers.addResult(results, 1, + 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); } }); diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index b75a66ddb5..d7ac6fed08 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -115,20 +115,44 @@ const createNullCache = () => { }; }; -describe('plainTextParameters', function () { + +describe('plaintextParameters', function () { describe('run', function () { - it('should FAIL if Stack parameters contain one of secret words ["password" , "privatekey", "secret"]', function (done) { + it('should FAIL if template contains one of secret words', function (done) { const cache = createCache([describeStacks[0]]); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + }); + }); + + it('should WARN if template contains one of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[0]]); + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(1); done(); }); }); + it('should PASS if template does not contain any of secret words', function (done) { + const cache = createCache([describeStacks[1]]); + plaintextParameters.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { + const cache = createCache([describeStacks[2]]); + plaintextParameters.run(cache, {}, (err, results) => { it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { const cache = createCache([describeStacks[1]]); plainTextParameters.run(cache, settings, (err, results) => { + it('should PASS if template does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[1]]); + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -137,7 +161,8 @@ describe('plainTextParameters', function () { it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); - plainTextParameters.run(cache, settings, (err, results) => { + + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -146,7 +171,8 @@ describe('plainTextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -155,7 +181,8 @@ describe('plainTextParameters', function () { it('should not return any results if unable to fetch any stack description', function (done) { const cache = createNullCache(); - plainTextParameters.run(cache, settings, (err, results) => { + + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(0); done(); }); @@ -163,7 +190,7 @@ describe('plainTextParameters', function () { it('should UNKNOWN if error occurs while fetching stack description', function (done) { const cache = createErrorCache(); - plainTextParameters.run(cache, settings, (err, results) => { + plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); From 50d9d70715bd00ba3b8e3b76269f2d8e97ea19f4 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Fri, 14 Aug 2020 18:09:45 +0500 Subject: [PATCH 38/71] SPLOIT-113: Updated custom settings --- index.js | 3 --- plugins/aws/cloudformation/plainTextParameters.js | 12 +++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 993593d97f..b9e7b029ab 100755 --- a/index.js +++ b/index.js @@ -96,8 +96,6 @@ try { console.error('ERROR: Config file could not be loaded. Please ensure you have copied the config_example.js file to config.js'); process.exit(1); } -<<<<<<< HEAD -======= if(process.env.GOOGLE_APPLICATION_CREDENTIALS){ GoogleConfig = require(process.env.GOOGLE_APPLICATION_CREDENTIALS); GoogleConfig.project = GoogleConfig.project_id; @@ -111,7 +109,6 @@ var settings = { ] } }; ->>>>>>> a4ea92f... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation function loadHelperFile(path) { try { diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 0b3e156b8a..c9d9f83f33 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -10,10 +10,12 @@ module.exports = { recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], settings: { - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] + + plain_text_parameters: { + name: 'CloudFormation Plaintext Parameters', + description: 'A comma-delimited list of parameter strings that indicate a sensitive value', + regex: '[a-zA-Z0-9,]', + default: 'secret,password,privatekey' } }, @@ -21,7 +23,7 @@ module.exports = { var results = []; var source = {}; var regions = helpers.regions(settings); - secretWords = this.settings.plainTextParameters.secretWords; + var secretWords = this.settings.plain_text_parameters.default; async.each(regions.cloudformation, function(region, rcb){ From 46ff92cf64080381f122946d5ea4ef79df76f0ec Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 21:24:30 +0500 Subject: [PATCH 39/71] Made PR requested changes --- plugins/aws/cloudformation/plainTextParameters.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index c9d9f83f33..e396861897 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -24,7 +24,6 @@ module.exports = { var source = {}; var regions = helpers.regions(settings); var secretWords = this.settings.plain_text_parameters.default; - async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, @@ -50,12 +49,12 @@ module.exports = { if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'The template does not contain any potentially-sensitive parameters', region, resource); + 'Template does not contain any potentially-sensitive parameters', region, resource); return rcb(); } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && secretWords.includes(parameter.ParameterKey.toLowerCase())) { + if((!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()))) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); From ec1e5efe8b96f6cbf75cb6f73df79de25008cc97 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 15 Aug 2020 01:07:50 +0500 Subject: [PATCH 40/71] SPLOIT-113: Added regex to check if NoEcho is enabled --- plugins/aws/cloudformation/plainTextParameters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index e396861897..c51ac0dcf4 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -54,7 +54,7 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if((!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()))) { + if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match("^[\*]+$")) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); From e89dd5e81b5e61bbbce3be203de259815b53a023 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 04:13:40 +0500 Subject: [PATCH 41/71] Accommodated PR changes --- plugins/aws/cloudformation/plainTextParameters.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index c51ac0dcf4..19ac1eed53 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -49,8 +49,8 @@ module.exports = { if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'Template does not contain any potentially-sensitive parameters', region, resource); - return rcb(); + 'Template does not contain any parameters', region, resource); + continue; } stack.Parameters.forEach(function(parameter){ From 4d7ee30cc8604f2fdb0fe778ffcb3c35502cfa62 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 06:09:46 +0500 Subject: [PATCH 42/71] Fixed eslint issues --- plugins/aws/cloudformation/plainTextParameters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 19ac1eed53..5bda4d33b1 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -54,7 +54,7 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match("^[\*]+$")) { + if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); From f5c9bf0799a1bcd75b9c7648ceea7140a6498fcd Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:42:00 +0500 Subject: [PATCH 43/71] Update index.js --- index.js | 138 +++++++++++++++++++++++++++---------------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/index.js b/index.js index b9e7b029ab..0484fec705 100755 --- a/index.js +++ b/index.js @@ -1,74 +1,74 @@ #!/usr/bin/env node -const { ArgumentParser } = require('argparse'); -const engine = require('./engine'); - -console.log(` - _____ _ _ _____ _ _ _ - / ____| | | |/ ____| | | (_) | - | | | | ___ _ _ __| | (___ _ __ | | ___ _| |_ - | | | |/ _ \\| | | |/ _\` |\\___ \\| '_ \\| |/ _ \\| | __| - | |____| | (_) | |_| | (_| |____) | |_) | | (_) | | |_ - \\_____|_|\\___/ \\__,_|\\__,_|_____/| .__/|_|\\___/|_|\\__| - | | - |_| - - CloudSploit by Aqua Security, Ltd. - Cloud security auditing for AWS, Azure, GCP, Oracle, and GitHub -`); - -const parser = new ArgumentParser({}); - -parser.add_argument('--config', { - help: 'The path to a CloudSploit config file containing cloud credentials. See config_example.js' -}); - -parser.add_argument('--compliance', { - help: 'Compliance mode. Only return results applicable to the selected program.', - choices: ['hipaa', 'cis', 'cis1', 'cis2', 'pci'], - action: 'append' -}); -parser.add_argument('--plugin', { - help: 'A specific plugin to run. If none provided, all plugins will be run. Obtain from the exports.js file. E.g. acmValidation' -}); -parser.add_argument('--govcloud', { - help: 'AWS only. Enables GovCloud mode.', - action: 'store_true' -}); -parser.add_argument('--china', { - help: 'AWS only. Enables AWS China mode.', - action: 'store_true' -}); -parser.add_argument('--csv', { help: 'Output: CSV file' }); -parser.add_argument('--json', { help: 'Output: JSON file' }); -parser.add_argument('--junit', { help: 'Output: Junit file' }); -parser.add_argument('--console', { - help: 'Console output format. Default: table', - choices: ['none', 'text', 'table'], - default: 'table' -}); -parser.add_argument('--collection', { help: 'Output: full collection JSON as file' }); -parser.add_argument('--ignore-ok', { - help: 'Ignore passing (OK) results', - action: 'store_true' -}); -parser.add_argument('--exit-code', { - help: 'Exits with a non-zero status code if non-passing results are found', - action: 'store_true' -}); -parser.add_argument('--skip-paginate', { - help: 'AWS only. Skips pagination (for debugging).', - action: 'store_false' -}); -parser.add_argument('--suppress', { - help: 'Suppress results matching the provided Regex. Format: pluginId:region:resourceId', - action: 'append' -}); - -let settings = parser.parse_args(); -let cloudConfig = {}; - -settings.cloud = 'aws'; +var engine = require('./engine'); + +var AWSConfig; +var AzureConfig; +var GitHubConfig; +var OracleConfig; +var GoogleConfig; + +// OPTION 1: Configure service provider credentials through hard-coded config objects + +// AWSConfig = { +// accessKeyId: '', +// secretAccessKey: '', +// sessionToken: '', +// region: '' +// }; + +// AzureConfig = { +// ApplicationID: '', // A.K.A ClientID +// KeyValue: '', // Secret +// DirectoryID: '', // A.K.A TenantID or Domain +// SubscriptionID: '', +// location: 'East US' +// }; + +// GitHubConfig = { +// token: '', // GitHub app token +// url: 'https://api.github.com', // BaseURL if not using public GitHub +// organization: false, // Set to true if the login is an organization +// login: '' // The login id for the user or organization +// }; + +// Oracle Important Note: +// Please read Oracle API's key generation instructions: config/_oracle/keys/Readme.md +// You will want an API signing key to fill the keyFingerprint and privateKey params +// OracleConfig = { +// RESTversion: '/20160918', +// tenancyId: 'ocid1.tenancy.oc1..', +// compartmentId: 'ocid1.compartment.oc1..', +// userId: 'ocid1.user.oc1..', +// keyFingerprint: 'YOURKEYFINGERPRINT', +// keyValue: "-----BEGIN PRIVATE KEY-----\nYOUR-PRIVATE-KEY-GOES-HERE\n-----END PRIVATE KEY-----\n", +// region: 'us-ashburn-1', +// }; + +// GoogleConfig = { +// "type": "service_account", +// "project": "your-project-name", +// "client_email": "cloudsploit@your-project-name.iam.gserviceaccount.com", +// "private_key": "-----BEGIN PRIVATE KEY-----\nYOUR-PRIVATE-KEY-GOES-HERE\n-----END PRIVATE KEY-----\n", +// }; + +// OPTION 2: Import a service provider config file containing credentials + +// AWSConfig = require(__dirname + '/aws_credentials.json'); +// AzureConfig = require(__dirname + '/azure_credentials.json'); +// GitHubConfig = require(__dirname + '/github_credentials.json'); +// OracleConfig = require(__dirname + '/oracle_credentials.json'); +// GoogleConfig = require(__dirname + '/google_credentials.json'); + +// OPTION 3: ENV configuration with service provider env vars +if(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY){ + AWSConfig = { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + sessionToken: process.env.AWS_SESSION_TOKEN, + region: process.env.AWS_DEFAULT_REGION || 'us-east-1' + }; +} // Now execute the scans using the defined configuration information. if (!settings.config) { From 53e18b8d6f575caa387507c1dcc5e1fa59914c58 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:48:02 +0500 Subject: [PATCH 44/71] Update index.js --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 0484fec705..de6616b22a 100755 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ var GoogleConfig; // accessKeyId: '', // secretAccessKey: '', // sessionToken: '', -// region: '' +// region: 'us-east-1' // }; // AzureConfig = { From 6ec73bc80abccbaaa104aa932fc6e48d7ae6f436 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 20:15:26 +0500 Subject: [PATCH 45/71] Accomodated PR changes --- plugins/aws/cloudformation/plainTextParameters.js | 10 ++++++---- .../cloudformation/plainTextParameters.spec.js | 15 ++++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 5bda4d33b1..94e7f92cff 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -54,15 +54,17 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { - parameterFound = true; - helpers.addResult(results, 1, - 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); + if(parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { + foundStrings.push(parameter.ParameterKey); } }); if(foundStrings && foundStrings.length) { +<<<<<<< HEAD helpers.addResult(results, 2, +======= + helpers.addResult(results, 1, +>>>>>>> aac8ece... Accomodated PR changes 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); } else { diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index d7ac6fed08..7493e245b3 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -147,11 +147,14 @@ describe('plaintextParameters', function () { it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { const cache = createCache([describeStacks[2]]); plaintextParameters.run(cache, {}, (err, results) => { - it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { - const cache = createCache([describeStacks[1]]); - plainTextParameters.run(cache, settings, (err, results) => { - it('should PASS if template does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { - const cache = createCache([describeStacks[1]]); + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { + const cache = createCache([describeStacks[2]]); plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -161,7 +164,6 @@ describe('plaintextParameters', function () { it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); - plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -171,7 +173,6 @@ describe('plaintextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); - plaintextParameters.run(cache, {}, (err, results) => { plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); From 2f55a118e1ad6692c2ea78209ec6852cb7782354 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 21:12:35 +0500 Subject: [PATCH 46/71] Updated status in result of failure --- plugins/aws/cloudformation/plainTextParameters.js | 4 ---- plugins/aws/cloudformation/plainTextParameters.spec.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 94e7f92cff..133fa51b6b 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -60,11 +60,7 @@ module.exports = { }); if(foundStrings && foundStrings.length) { -<<<<<<< HEAD helpers.addResult(results, 2, -======= - helpers.addResult(results, 1, ->>>>>>> aac8ece... Accomodated PR changes 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); } else { diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index 7493e245b3..c590d10ce9 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -130,7 +130,7 @@ describe('plaintextParameters', function () { const cache = createCache([describeStacks[0]]); plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); - expect(results[0].status).to.equal(1); + expect(results[0].status).to.equal(2); done(); }); }); From 0ba2fbf09b4518ed41903f7a68809b6a3179cf6b Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 13 Aug 2020 00:34:00 +0500 Subject: [PATCH 47/71] SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation --- exports.js | 3 - .../aws/cloudformation/plainTextParameters.js | 99 +++++++++---------- 2 files changed, 46 insertions(+), 56 deletions(-) diff --git a/exports.js b/exports.js index 7139c7640e..d2ab19673e 100644 --- a/exports.js +++ b/exports.js @@ -14,9 +14,7 @@ module.exports = { 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), - 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), - 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), @@ -32,7 +30,6 @@ module.exports = { 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), - 'launchWizardSecurityGroups' : require(__dirname + '/plugins/aws/ec2/launchWizardSecurityGroups'), 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 133fa51b6b..0b4863765c 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/aws'); module.exports = { @@ -9,69 +8,63 @@ module.exports = { link: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html', recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], - settings: { - - plain_text_parameters: { - name: 'CloudFormation Plaintext Parameters', - description: 'A comma-delimited list of parameter strings that indicate a sensitive value', - regex: '[a-zA-Z0-9,]', - default: 'secret,password,privatekey' - } + compliance: { + hipaa: 'HIPAA requires all data to be transmitted over secure channels. ' + + 'CloudFront HTTPS redirection should be used to ensure site visitors ' + + 'are always connecting over a secure channel.' }, + // settings : { secretWords : ["password", "privatekey", "secret"] }, run: function(cache, settings, callback) { + var results = []; var source = {}; - var regions = helpers.regions(settings); - var secretWords = this.settings.plain_text_parameters.default; - async.each(regions.cloudformation, function(region, rcb){ - var describeStacks = helpers.addSource(cache, source, - ['cloudformation', 'describeStacks', region]); - if (!describeStacks) return rcb(); + var region = helpers.defaultRegion(settings); - if (describeStacks.err || !describeStacks.data) { - helpers.addResult(results, 3, - 'Unable to describe stacks: ' + helpers.addError(describeStacks), region); - return rcb(); - } + var describeStacks = helpers.addSource(cache, source, + ['cloudformation', 'describeStacks', region]); - if (!describeStacks.data.length) { - helpers.addResult(results, 0, 'No CloudFormation stacks found', region); - return rcb(); - } - - for (var s in describeStacks.data){ - // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id - var stack = describeStacks.data[s]; - var resource = stack.StackId; - let foundStrings = []; + console.log(describeStacks); + console.log("results received"); + // if (!describeStacks) return callback(null, results, source); - if(!stack.Parameters || !stack.Parameters.length) { - helpers.addResult(results, 0, - 'Template does not contain any parameters', region, resource); - continue; - } + // if (describeStacks.err || !describeStacks.data) { + // helpers.addResult(results, 3, + // 'Unable to describe stacks: ' + helpers.addError(describeStacks)); + // return callback(null, results, source); + // } - stack.Parameters.forEach(function(parameter){ - if(parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { - foundStrings.push(parameter.ParameterKey); - } - }); + // if (!describeStacks.data.length) { + // helpers.addResult(results, 0, 'No stacks descriptions found'); + // return callback(null, results, source); + // } + // console.log(describeStacks.data); + // loop through stacks for every template retrieval + // describeStacks.data.forEach(function(Distribution){ + // var stackTemplate = helpers.addSource(cache, source, + // ['cloudformation', 'getTemplate', region]); + + // if (!describeStacks) return callback(null, results, source); + + // if (describeStacks.err || !describeStacks.data) { + // helpers.addResult(results, 3, + // 'Unable to describe stacks: ' + helpers.addError(describeStacks)); + // return callback(null, results, source); + // } - if(foundStrings && foundStrings.length) { - helpers.addResult(results, 2, - 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); - } - else { - helpers.addResult(results, 0, - 'Template does not contain any potentially-sensitive parameters', region, resource); - } - } + // if (Distribution.DefaultCacheBehavior.ViewerProtocolPolicy == 'redirect-to-https') { + // helpers.addResult(results, 0, 'CloudFront distribution ' + + // 'is configured to redirect non-HTTPS traffic to HTTPS', 'global', Distribution.ARN); + // } else if (Distribution.DefaultCacheBehavior.ViewerProtocolPolicy == 'https-only') { + // helpers.addResult(results, 0, 'The CloudFront ' + + // 'distribution is set to use HTTPS only.', 'global', Distribution.ARN); + // } else { + // helpers.addResult(results, 2, 'CloudFront distribution ' + + // 'is not configured to use HTTPS', 'global', Distribution.ARN); + // } + // }); - rcb(); - }, function(){ - callback(null, results, source); - }); + callback(null, results, source); } }; From e9415b917bd99069cbf1fb8c3a9423ae127fb51b Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Fri, 14 Aug 2020 05:55:55 +0500 Subject: [PATCH 48/71] SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation --- exports.js | 6 +- index.js | 15 +-- .../aws/cloudformation/plainTextParameters.js | 91 ++++++++-------- .../plainTextParameters.spec.js | 100 ++++++++++++++++++ 4 files changed, 150 insertions(+), 62 deletions(-) diff --git a/exports.js b/exports.js index d2ab19673e..fa901a2af7 100644 --- a/exports.js +++ b/exports.js @@ -5,7 +5,6 @@ module.exports = { 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), - 'emptyASG' : require(__dirname + '/plugins/aws/autoscaling/emptyASG.js'), 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), @@ -72,9 +71,8 @@ module.exports = { 'ebsSnapshotPrivate' : require(__dirname + '/plugins/aws/ec2/ebsSnapshotPrivate.js'), 'natMultiAz' : require(__dirname + '/plugins/aws/ec2/natMultiAz.js'), 'defaultVpcInUse' : require(__dirname + '/plugins/aws/ec2/defaultVpcInUse.js'), - 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), + 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), 'crossVpcPublicPrivate' : require(__dirname + '/plugins/aws/ec2/crossVpcPublicPrivate.js'), - 'vpcEndpointAcceptance' : require(__dirname + '/plugins/aws/ec2/vpcEndpointAcceptance'), 'ebsEncryptedSnapshots' : require(__dirname + '/plugins/aws/ec2/ebsEncryptedSnapshots.js'), 'ec2MetadataOptions' : require(__dirname + '/plugins/aws/ec2/ec2MetadataOptions.js'), @@ -114,7 +112,6 @@ module.exports = { 'iamUserAdmins' : require(__dirname + '/plugins/aws/iam/iamUserAdmins.js'), 'iamUserNameRegex' : require(__dirname + '/plugins/aws/iam/iamUserNameRegex.js'), 'iamRolePolicies' : require(__dirname + '/plugins/aws/iam/iamRolePolicies.js'), - 'iamRoleLastUsed' : require(__dirname + '/plugins/aws/iam/iamRoleLastUsed.js'), 'maxPasswordAge' : require(__dirname + '/plugins/aws/iam/maxPasswordAge.js'), 'minPasswordLength' : require(__dirname + '/plugins/aws/iam/minPasswordLength.js'), 'noUserIamPolicies' : require(__dirname + '/plugins/aws/iam/noUserIamPolicies.js'), @@ -125,7 +122,6 @@ module.exports = { 'passwordRequiresUppercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresUppercase.js'), 'passwordReusePrevention' : require(__dirname + '/plugins/aws/iam/passwordReusePrevention.js'), 'rootAccessKeys' : require(__dirname + '/plugins/aws/iam/rootAccessKeys.js'), - 'rootSigningCertificate' : require(__dirname + '/plugins/aws/iam/rootSigningCertificate.js'), 'rootAccountInUse' : require(__dirname + '/plugins/aws/iam/rootAccountInUse.js'), 'rootHardwareMfa' : require(__dirname + '/plugins/aws/iam/rootHardwareMfa.js'), 'rootMfaEnabled' : require(__dirname + '/plugins/aws/iam/rootMfaEnabled.js'), diff --git a/index.js b/index.js index de6616b22a..45aa3d7df9 100755 --- a/index.js +++ b/index.js @@ -110,16 +110,11 @@ var settings = { } }; -function loadHelperFile(path) { - try { - var contents = require(path); - } catch (e) { - console.error(`ERROR: The credential file could not be loaded ${path}`); - console.error(e); - process.exit(1); - } - return contents; -} +// If running in GovCloud, uncomment the following +// settings.govcloud = true; + +// If running in AWS China, uncomment the following +// settings.china = true; function checkRequiredKeys(obj, keys) { keys.forEach(function(key){ diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 0b4863765c..f57b6ec2b7 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -1,3 +1,4 @@ +var async = require('async'); var helpers = require('../../../helpers/aws'); module.exports = { @@ -8,63 +9,59 @@ module.exports = { link: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html', recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], - compliance: { - hipaa: 'HIPAA requires all data to be transmitted over secure channels. ' + - 'CloudFront HTTPS redirection should be used to ensure site visitors ' + - 'are always connecting over a secure channel.' - }, - // settings : { secretWords : ["password", "privatekey", "secret"] }, run: function(cache, settings, callback) { - var results = []; var source = {}; + var regions = helpers.regions(settings); + secretWords = settings.plainTextParameters.secretWords; - var region = helpers.defaultRegion(settings); + async.each(regions.cloudformation, function(region, rcb){ - var describeStacks = helpers.addSource(cache, source, - ['cloudformation', 'describeStacks', region]); + var describeStacks = helpers.addSource(cache, source, + ['cloudformation', 'describeStacks', region]); + + if (!describeStacks) return rcb(); - console.log(describeStacks); - console.log("results received"); - // if (!describeStacks) return callback(null, results, source); + if (describeStacks.err || !describeStacks.data) { + helpers.addResult(results, 3, + 'Unable to describe stacks: ' + helpers.addError(describeStacks), region); + return rcb(); + } - // if (describeStacks.err || !describeStacks.data) { - // helpers.addResult(results, 3, - // 'Unable to describe stacks: ' + helpers.addError(describeStacks)); - // return callback(null, results, source); - // } + if (!describeStacks.data.length) { + helpers.addResult(results, 0, 'No stack description found', region); + return rcb(); + } + + var parameterFound; + describeStacks.data.forEach(function(stack){ + parameterFound = false; - // if (!describeStacks.data.length) { - // helpers.addResult(results, 0, 'No stacks descriptions found'); - // return callback(null, results, source); - // } - // console.log(describeStacks.data); - // loop through stacks for every template retrieval - // describeStacks.data.forEach(function(Distribution){ - // var stackTemplate = helpers.addSource(cache, source, - // ['cloudformation', 'getTemplate', region]); - - // if (!describeStacks) return callback(null, results, source); - - // if (describeStacks.err || !describeStacks.data) { - // helpers.addResult(results, 3, - // 'Unable to describe stacks: ' + helpers.addError(describeStacks)); - // return callback(null, results, source); - // } + if(!stack.Parameters.length) { + helpers.addResult(results, 0, + 'The template did not contain any potentially-sensitive parameters', region); + return; + } - // if (Distribution.DefaultCacheBehavior.ViewerProtocolPolicy == 'redirect-to-https') { - // helpers.addResult(results, 0, 'CloudFront distribution ' + - // 'is configured to redirect non-HTTPS traffic to HTTPS', 'global', Distribution.ARN); - // } else if (Distribution.DefaultCacheBehavior.ViewerProtocolPolicy == 'https-only') { - // helpers.addResult(results, 0, 'The CloudFront ' + - // 'distribution is set to use HTTPS only.', 'global', Distribution.ARN); - // } else { - // helpers.addResult(results, 2, 'CloudFront distribution ' + - // 'is not configured to use HTTPS', 'global', Distribution.ARN); - // } - // }); + stack.Parameters.forEach(function(parameter){ + if(secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameterFound) { + parameterFound = true; + helpers.addResult(results, 1, + 'The template contained one of the following potentially-sensitive parameters: secret, key, password', region); + return; + } + }); + + if(!parameterFound) { + helpers.addResult(results, 0, + 'The template did not contain any potentially-sensitive parameters', region); + } - callback(null, results, source); + }); + rcb(); + }, function(){ + callback(null, results, source); + }); } }; diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index c590d10ce9..238ff315a9 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -1,4 +1,5 @@ var expect = require('chai').expect; +<<<<<<< HEAD const plaintextParameters = require('./plainTextParameters'); const describeStacks = [ @@ -77,6 +78,73 @@ const describeStacks = [ Tags: [], DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } } +======= +const plainTextParameters = require('./plainTextParameters'); +const settings = { + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] + } +}; +const describeStacks = [ + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [ + { + ParameterKey: 'Secret', + ParameterValue: 'bucketwithsecretparameter1' + }, + { + ParameterKey: 'Password', + ParameterValue: 'bucketwithsecretparameter1' + } + ], + CreationTime: '2020-08-13T13:34:52.435Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + }, + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [ + { + ParameterKey: 'S3BucketName', + ParameterValue: 'testbucketplaintext1' + } + ], + CreationTime: '2020-08-12T09:42:04.803Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + }, + { + StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', + StackName: 'TestStack', + Parameters: [], + CreationTime: '2020-08-12T09:42:04.803Z', + RollbackConfiguration: { RollbackTriggers: [] }, + StackStatus: 'CREATE_COMPLETE', + DisableRollback: false, + NotificationARNs: [], + Capabilities: [], + Outputs: [], + Tags: [], + DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } + } +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation ] const createCache = (stacks) => { @@ -115,6 +183,7 @@ const createNullCache = () => { }; }; +<<<<<<< HEAD describe('plaintextParameters', function () { describe('run', function () { @@ -131,10 +200,20 @@ describe('plaintextParameters', function () { plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); +======= +describe('plainTextParameters', function () { + describe('run', function () { + it('should FAIL if Stack parameters contain one of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[0]]); + plainTextParameters.run(cache, settings, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(1); +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation done(); }); }); +<<<<<<< HEAD it('should PASS if template does not contain any of secret words', function (done) { const cache = createCache([describeStacks[1]]); plaintextParameters.run(cache, {}, (err, results) => { @@ -156,6 +235,11 @@ describe('plaintextParameters', function () { it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { const cache = createCache([describeStacks[2]]); plaintextParameters.run(cache, {}, (err, results) => { +======= + it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { + const cache = createCache([describeStacks[1]]); + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -164,7 +248,11 @@ describe('plaintextParameters', function () { it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); +<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { +======= + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -173,7 +261,11 @@ describe('plaintextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); +<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { +======= + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -182,8 +274,12 @@ describe('plaintextParameters', function () { it('should not return any results if unable to fetch any stack description', function (done) { const cache = createNullCache(); +<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { +======= + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(0); done(); }); @@ -191,7 +287,11 @@ describe('plaintextParameters', function () { it('should UNKNOWN if error occurs while fetching stack description', function (done) { const cache = createErrorCache(); +<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { +======= + plainTextParameters.run(cache, settings, (err, results) => { +>>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); From 882077517afe5da597b13ec40e4d705ae7613872 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 15:30:57 +0500 Subject: [PATCH 49/71] Added plugin and spec file for launch wizard security groups --- exports.js | 4 +++- plugins/aws/ec2/launchWizardSecurityGroups.js | 15 ++++----------- plugins/aws/ec2/vpcEndpointAcceptance.spec.js | 9 --------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/exports.js b/exports.js index fa901a2af7..56084148d3 100644 --- a/exports.js +++ b/exports.js @@ -29,6 +29,7 @@ module.exports = { 'dynamoKmsEncryption' : require(__dirname + '/plugins/aws/dynamodb/dynamoKmsEncryption.js'), 'defaultSecurityGroup' : require(__dirname + '/plugins/aws/ec2/defaultSecurityGroup.js'), + 'launchWizardSecurityGroups' : require(__dirname + '/plugins/aws/ec2/launchWizardSecurityGroups'), 'elasticIpLimit' : require(__dirname + '/plugins/aws/ec2/elasticIpLimit.js'), 'subnetIpAvailability' : require(__dirname + '/plugins/aws/ec2/subnetIpAvailability.js'), 'excessiveSecurityGroups' : require(__dirname + '/plugins/aws/ec2/excessiveSecurityGroups.js'), @@ -71,8 +72,9 @@ module.exports = { 'ebsSnapshotPrivate' : require(__dirname + '/plugins/aws/ec2/ebsSnapshotPrivate.js'), 'natMultiAz' : require(__dirname + '/plugins/aws/ec2/natMultiAz.js'), 'defaultVpcInUse' : require(__dirname + '/plugins/aws/ec2/defaultVpcInUse.js'), - 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), + 'defaultVpcExists' : require(__dirname + '/plugins/aws/ec2/defaultVpcExists.js'), 'crossVpcPublicPrivate' : require(__dirname + '/plugins/aws/ec2/crossVpcPublicPrivate.js'), + 'vpcEndpointAcceptance' : require(__dirname + '/plugins/aws/ec2/vpcEndpointAcceptance'), 'ebsEncryptedSnapshots' : require(__dirname + '/plugins/aws/ec2/ebsEncryptedSnapshots.js'), 'ec2MetadataOptions' : require(__dirname + '/plugins/aws/ec2/ec2MetadataOptions.js'), diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js index 5c73babd28..252cabd11c 100644 --- a/plugins/aws/ec2/launchWizardSecurityGroups.js +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -28,25 +28,18 @@ module.exports = { } if (!describeSecurityGroups.data.length) { - helpers.addResult(results, 0, 'No security groups found', region); + helpers.addResult(results, 0, 'No security groups present', region); return rcb(); } for (var s in describeSecurityGroups.data) { var sg = describeSecurityGroups.data[s]; - var resource = 'arn:aws:ec2:' + region + ':' + sg.OwnerId + ':security-group/' + sg.GroupId; + var resource = sg.GroupId; - if(!sg.GroupName) { - helpers.addResult(results, 2, - 'Unable to get group name of security group', - region, resource); - continue; - } - if (sg.GroupName.toLowerCase().startsWith('launch-wizard')) { helpers.addResult(results, 2, - 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', - region, resource); + 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', + region, resource); } else { helpers.addResult(results, 0, 'Security Group ' + sg.GroupName + ' was not launched using EC2 launch wizard', diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js index 0946f5a682..62053b7238 100644 --- a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js +++ b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js @@ -112,15 +112,6 @@ describe('vpcEndpointAcceptance', function () { }); }); - it('should PASS if no VPC endpoint services are detected', function (done) { - const cache = createCache([]); - vpcEndpointAcceptance.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - done(); - }); - }); - it('should UNKNOWN if there was an error querying for VPC endpoint services', function (done) { const cache = createErrorCache(); vpcEndpointAcceptance.run(cache, {}, (err, results) => { From 8e6b23b5ad41c0a70bfabff1276416718c9039d1 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Thu, 13 Aug 2020 19:59:59 +0500 Subject: [PATCH 50/71] Added vpcEndpointAcceptance plugin and spec file --- plugins/aws/ec2/vpcEndpointAcceptance.spec.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js index 62053b7238..0946f5a682 100644 --- a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js +++ b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js @@ -112,6 +112,15 @@ describe('vpcEndpointAcceptance', function () { }); }); + it('should PASS if no VPC endpoint services are detected', function (done) { + const cache = createCache([]); + vpcEndpointAcceptance.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + it('should UNKNOWN if there was an error querying for VPC endpoint services', function (done) { const cache = createErrorCache(); vpcEndpointAcceptance.run(cache, {}, (err, results) => { From 82d840671935c1139fabb28f8f786918e4b02a13 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 16:50:46 +0500 Subject: [PATCH 51/71] Refactored code in plaintextParameters plugin and spec file --- exports.js | 4 +- index.js | 8 +- .../aws/cloudformation/plainTextParameters.js | 28 +++-- .../plainTextParameters.spec.js | 119 ------------------ 4 files changed, 23 insertions(+), 136 deletions(-) diff --git a/exports.js b/exports.js index 56084148d3..ee2b81f9ee 100644 --- a/exports.js +++ b/exports.js @@ -13,7 +13,9 @@ module.exports = { 'cloudfrontHttpsOnly' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontHttpsOnly.js'), 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), - 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), + + 'plaintextParameters' : require(__dirname + '/plugins/aws/cloudformation/plaintextParameters.js'), + 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), 'cloudtrailEnabled' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailEnabled.js'), diff --git a/index.js b/index.js index 45aa3d7df9..53b6b4fb75 100755 --- a/index.js +++ b/index.js @@ -102,13 +102,7 @@ if(process.env.GOOGLE_APPLICATION_CREDENTIALS){ } // Custom settings - place plugin-specific settings here -var settings = { - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] - } -}; +var settings = {}; // If running in GovCloud, uncomment the following // settings.govcloud = true; diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index f57b6ec2b7..8e538c23d4 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -9,12 +9,19 @@ module.exports = { link: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html', recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], + settings: { + plainTextParameters: { + secretWords: [ + 'secret', 'password', 'privatekey' + ] + } + }, run: function(cache, settings, callback) { var results = []; var source = {}; var regions = helpers.regions(settings); - secretWords = settings.plainTextParameters.secretWords; + secretWords = this.settings.plainTextParameters.secretWords; async.each(regions.cloudformation, function(region, rcb){ @@ -35,30 +42,33 @@ module.exports = { } var parameterFound; - describeStacks.data.forEach(function(stack){ + for (var s in describeStacks.data){ + // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id + var stack = describeStacks.data[s]; + var resource = stack.StackId; parameterFound = false; if(!stack.Parameters.length) { helpers.addResult(results, 0, - 'The template did not contain any potentially-sensitive parameters', region); - return; + 'The template does not contain any potentially-sensitive parameters', region, resource); + return rcb(); } stack.Parameters.forEach(function(parameter){ - if(secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameterFound) { + if(!parameterFound && secretWords.includes(parameter.ParameterKey.toLowerCase())) { parameterFound = true; helpers.addResult(results, 1, - 'The template contained one of the following potentially-sensitive parameters: secret, key, password', region); - return; + 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); } }); if(!parameterFound) { helpers.addResult(results, 0, - 'The template did not contain any potentially-sensitive parameters', region); + 'Template does not contain any potentially-sensitive parameters', region, resource); } - }); + } + rcb(); }, function(){ callback(null, results, source); diff --git a/plugins/aws/cloudformation/plainTextParameters.spec.js b/plugins/aws/cloudformation/plainTextParameters.spec.js index 238ff315a9..03e8abf7e9 100644 --- a/plugins/aws/cloudformation/plainTextParameters.spec.js +++ b/plugins/aws/cloudformation/plainTextParameters.spec.js @@ -1,5 +1,4 @@ var expect = require('chai').expect; -<<<<<<< HEAD const plaintextParameters = require('./plainTextParameters'); const describeStacks = [ @@ -78,73 +77,6 @@ const describeStacks = [ Tags: [], DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } } -======= -const plainTextParameters = require('./plainTextParameters'); -const settings = { - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] - } -}; -const describeStacks = [ - { - StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', - StackName: 'TestStack', - Parameters: [ - { - ParameterKey: 'Secret', - ParameterValue: 'bucketwithsecretparameter1' - }, - { - ParameterKey: 'Password', - ParameterValue: 'bucketwithsecretparameter1' - } - ], - CreationTime: '2020-08-13T13:34:52.435Z', - RollbackConfiguration: { RollbackTriggers: [] }, - StackStatus: 'CREATE_COMPLETE', - DisableRollback: false, - NotificationARNs: [], - Capabilities: [], - Outputs: [], - Tags: [], - DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } - }, - { - StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', - StackName: 'TestStack', - Parameters: [ - { - ParameterKey: 'S3BucketName', - ParameterValue: 'testbucketplaintext1' - } - ], - CreationTime: '2020-08-12T09:42:04.803Z', - RollbackConfiguration: { RollbackTriggers: [] }, - StackStatus: 'CREATE_COMPLETE', - DisableRollback: false, - NotificationARNs: [], - Capabilities: [], - Outputs: [], - Tags: [], - DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } - }, - { - StackId: 'arn:aws:cloudformation:us-east-1:55005500:stack/TestStack/1493b310-dc80-11ea-b8ab-1214c28caebf', - StackName: 'TestStack', - Parameters: [], - CreationTime: '2020-08-12T09:42:04.803Z', - RollbackConfiguration: { RollbackTriggers: [] }, - StackStatus: 'CREATE_COMPLETE', - DisableRollback: false, - NotificationARNs: [], - Capabilities: [], - Outputs: [], - Tags: [], - DriftInformation: { StackDriftStatus: 'NOT_CHECKED' } - } ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation ] const createCache = (stacks) => { @@ -183,8 +115,6 @@ const createNullCache = () => { }; }; -<<<<<<< HEAD - describe('plaintextParameters', function () { describe('run', function () { it('should FAIL if template contains one of secret words', function (done) { @@ -192,28 +122,10 @@ describe('plaintextParameters', function () { plaintextParameters.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); - }); - }); - - it('should WARN if template contains one of secret words ["password" , "privatekey", "secret"]', function (done) { - const cache = createCache([describeStacks[0]]); - plaintextParameters.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(2); -======= -describe('plainTextParameters', function () { - describe('run', function () { - it('should FAIL if Stack parameters contain one of secret words ["password" , "privatekey", "secret"]', function (done) { - const cache = createCache([describeStacks[0]]); - plainTextParameters.run(cache, settings, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(1); ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation done(); }); }); -<<<<<<< HEAD it('should PASS if template does not contain any of secret words', function (done) { const cache = createCache([describeStacks[1]]); plaintextParameters.run(cache, {}, (err, results) => { @@ -232,27 +144,9 @@ describe('plainTextParameters', function () { }); }); - it('should PASS if template contains any of secret words but with NoEcho enabled', function (done) { - const cache = createCache([describeStacks[2]]); - plaintextParameters.run(cache, {}, (err, results) => { -======= - it('should PASS if Stack parameters does not contain any of secret words ["password" , "privatekey", "secret"]', function (done) { - const cache = createCache([describeStacks[1]]); - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - done(); - }); - }); - it('should PASS if unable to describe stacks', function (done) { const cache = createCache([]); -<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { -======= - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -261,11 +155,7 @@ describe('plainTextParameters', function () { it('should PASS if there is no parameter in the stack', function (done) { const cache = createCache([describeStacks[2]]); -<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { -======= - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); done(); @@ -274,12 +164,7 @@ describe('plainTextParameters', function () { it('should not return any results if unable to fetch any stack description', function (done) { const cache = createNullCache(); -<<<<<<< HEAD - plaintextParameters.run(cache, {}, (err, results) => { -======= - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(0); done(); }); @@ -287,11 +172,7 @@ describe('plainTextParameters', function () { it('should UNKNOWN if error occurs while fetching stack description', function (done) { const cache = createErrorCache(); -<<<<<<< HEAD plaintextParameters.run(cache, {}, (err, results) => { -======= - plainTextParameters.run(cache, settings, (err, results) => { ->>>>>>> 306d721... SPLOIT-113: Added Plain Text Parameters plugin for CloudFormation expect(results.length).to.equal(1); expect(results[0].status).to.equal(3); done(); From b022a527f8eb642304b4e38cdd5c082f060e448b Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Fri, 14 Aug 2020 18:09:45 +0500 Subject: [PATCH 52/71] SPLOIT-113: Updated custom settings --- plugins/aws/cloudformation/plainTextParameters.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 8e538c23d4..7f1cd285ae 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -10,10 +10,11 @@ module.exports = { recommended_action: 'Update the sensitive parameters to use the NoEcho property.', apis: ['CloudFormation:describeStacks'], settings: { - plainTextParameters: { - secretWords: [ - 'secret', 'password', 'privatekey' - ] + plain_text_parameters: { + name: "CloudFormation Plaintext Parameters", + description: "A comma-delimited list of parameter strings that indicate a sensitive value", + regex: "[a-zA-Z0-9,]", + default: "secret,password,privatekey" } }, @@ -21,8 +22,7 @@ module.exports = { var results = []; var source = {}; var regions = helpers.regions(settings); - secretWords = this.settings.plainTextParameters.secretWords; - + secretWords = this.settings.plain_text_parameters.default; async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, From d3ee38043531013576170b657eb1fe429231cec8 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Fri, 14 Aug 2020 21:24:30 +0500 Subject: [PATCH 53/71] Made PR requested changes --- plugins/aws/cloudformation/plainTextParameters.js | 8 ++++---- plugins/aws/ec2/launchWizardSecurityGroups.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 7f1cd285ae..9b91b0d2e6 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -22,7 +22,7 @@ module.exports = { var results = []; var source = {}; var regions = helpers.regions(settings); - secretWords = this.settings.plain_text_parameters.default; + var secretWords = this.settings.plain_text_parameters.default; async.each(regions.cloudformation, function(region, rcb){ var describeStacks = helpers.addSource(cache, source, @@ -48,14 +48,14 @@ module.exports = { var resource = stack.StackId; parameterFound = false; - if(!stack.Parameters.length) { + if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'The template does not contain any potentially-sensitive parameters', region, resource); + 'Template does not contain any potentially-sensitive parameters', region, resource); return rcb(); } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && secretWords.includes(parameter.ParameterKey.toLowerCase())) { + if((!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()))) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js index 252cabd11c..0e028778a7 100644 --- a/plugins/aws/ec2/launchWizardSecurityGroups.js +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -36,7 +36,7 @@ module.exports = { var sg = describeSecurityGroups.data[s]; var resource = sg.GroupId; - if (sg.GroupName.toLowerCase().startsWith('launch-wizard')) { + if (sg.GroupName && sg.GroupName.toLowerCase().startsWith('launch-wizard')) { helpers.addResult(results, 2, 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', region, resource); From 560d273d4265e8eddd90de4da7768b174326281c Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 15 Aug 2020 01:07:50 +0500 Subject: [PATCH 54/71] SPLOIT-113: Added regex to check if NoEcho is enabled --- plugins/aws/cloudformation/plainTextParameters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 9b91b0d2e6..e2890bc58e 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -55,7 +55,7 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if((!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()))) { + if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match("^[\*]+$")) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); From 9195d32e992bf53a2b81447e2290fa71e9f8ae58 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 04:13:40 +0500 Subject: [PATCH 55/71] Accommodated PR changes --- exports.js | 2 +- plugins/aws/cloudformation/plainTextParameters.js | 6 +++--- plugins/aws/ec2/launchWizardSecurityGroups.js | 11 +++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/exports.js b/exports.js index ee2b81f9ee..a61fddd2cc 100644 --- a/exports.js +++ b/exports.js @@ -14,7 +14,7 @@ module.exports = { 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), - 'plaintextParameters' : require(__dirname + '/plugins/aws/cloudformation/plaintextParameters.js'), + 'plaintextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index e2890bc58e..f5ebb6898b 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -46,12 +46,12 @@ module.exports = { // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id var stack = describeStacks.data[s]; var resource = stack.StackId; - parameterFound = false; + let parameterFound = false; if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, - 'Template does not contain any potentially-sensitive parameters', region, resource); - return rcb(); + 'Template does not contain any parameters', region, resource); + continue; } stack.Parameters.forEach(function(parameter){ diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js index 0e028778a7..48f040b7ed 100644 --- a/plugins/aws/ec2/launchWizardSecurityGroups.js +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -28,7 +28,7 @@ module.exports = { } if (!describeSecurityGroups.data.length) { - helpers.addResult(results, 0, 'No security groups present', region); + helpers.addResult(results, 0, 'No security groups found', region); return rcb(); } @@ -36,7 +36,14 @@ module.exports = { var sg = describeSecurityGroups.data[s]; var resource = sg.GroupId; - if (sg.GroupName && sg.GroupName.toLowerCase().startsWith('launch-wizard')) { + if(!sg.GroupName) { + helpers.addResult(results, 2, + 'Unable to get group name of security group', + region, resource); + continue; + } + + if (sg.GroupName.toLowerCase().startsWith('launch-wizard')) { helpers.addResult(results, 2, 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', region, resource); From 0287cc54eb44d1b44e77f7750837cbe8878773b5 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 06:09:46 +0500 Subject: [PATCH 56/71] Fixed eslint issues --- plugins/aws/cloudformation/plainTextParameters.js | 15 +++++++-------- plugins/aws/ec2/launchWizardSecurityGroups.js | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index f5ebb6898b..52be0d6ea8 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -11,10 +11,10 @@ module.exports = { apis: ['CloudFormation:describeStacks'], settings: { plain_text_parameters: { - name: "CloudFormation Plaintext Parameters", - description: "A comma-delimited list of parameter strings that indicate a sensitive value", - regex: "[a-zA-Z0-9,]", - default: "secret,password,privatekey" + name: 'CloudFormation Plaintext Parameters', + description: 'A comma-delimited list of parameter strings that indicate a sensitive value', + regex: '[a-zA-Z0-9,]', + default: 'secret,password,privatekey' } }, @@ -33,7 +33,7 @@ module.exports = { if (describeStacks.err || !describeStacks.data) { helpers.addResult(results, 3, 'Unable to describe stacks: ' + helpers.addError(describeStacks), region); - return rcb(); + return rcb(); } if (!describeStacks.data.length) { @@ -41,7 +41,6 @@ module.exports = { return rcb(); } - var parameterFound; for (var s in describeStacks.data){ // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id var stack = describeStacks.data[s]; @@ -55,7 +54,7 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match("^[\*]+$")) { + if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { parameterFound = true; helpers.addResult(results, 1, 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); @@ -64,7 +63,7 @@ module.exports = { if(!parameterFound) { helpers.addResult(results, 0, - 'Template does not contain any potentially-sensitive parameters', region, resource); + 'Template does not contain any potentially-sensitive parameters', region, resource); } } diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js index 48f040b7ed..caa173be82 100644 --- a/plugins/aws/ec2/launchWizardSecurityGroups.js +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -45,8 +45,8 @@ module.exports = { if (sg.GroupName.toLowerCase().startsWith('launch-wizard')) { helpers.addResult(results, 2, - 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', - region, resource); + 'Security Group ' + sg.GroupName + ' was launched using EC2 launch wizard', + region, resource); } else { helpers.addResult(results, 0, 'Security Group ' + sg.GroupName + ' was not launched using EC2 launch wizard', From 1315ccdc619767cbdd12284ecc534a185fa3994a Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:38:01 +0500 Subject: [PATCH 57/71] Update exports.js --- exports.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exports.js b/exports.js index a61fddd2cc..5a338cfa5e 100644 --- a/exports.js +++ b/exports.js @@ -14,7 +14,7 @@ module.exports = { 'cloudfrontLoggingEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js'), 'cloudfrontWafEnabled' : require(__dirname + '/plugins/aws/cloudfront/cloudfrontWafEnabled.js'), - 'plaintextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), + 'plainTextParameters' : require(__dirname + '/plugins/aws/cloudformation/plainTextParameters.js'), 'cloudtrailBucketAccessLogging' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js'), 'cloudtrailBucketDelete' : require(__dirname + '/plugins/aws/cloudtrail/cloudtrailBucketDelete.js'), From 229461ce784cc135fe4d9ebacea68cacd1523551 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:42:00 +0500 Subject: [PATCH 58/71] Update index.js --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 53b6b4fb75..94b3d7af71 100755 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ var GoogleConfig; // accessKeyId: '', // secretAccessKey: '', // sessionToken: '', -// region: 'us-east-1' +// region: '' // }; // AzureConfig = { From f5a3b8b09786882bc807deb980e9b4e7ca9b5451 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 14:48:02 +0500 Subject: [PATCH 59/71] Update index.js --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 94b3d7af71..53b6b4fb75 100755 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ var GoogleConfig; // accessKeyId: '', // secretAccessKey: '', // sessionToken: '', -// region: '' +// region: 'us-east-1' // }; // AzureConfig = { From c574f76cb8f427d08ad5f4fcf258d16a16bf0ddb Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Tue, 18 Aug 2020 20:15:26 +0500 Subject: [PATCH 60/71] Accomodated PR changes --- .../aws/cloudformation/plainTextParameters.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 52be0d6ea8..d05776bf37 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -27,7 +27,6 @@ module.exports = { var describeStacks = helpers.addSource(cache, source, ['cloudformation', 'describeStacks', region]); - if (!describeStacks) return rcb(); if (describeStacks.err || !describeStacks.data) { @@ -45,7 +44,7 @@ module.exports = { // arn:aws:cloudformation:region:account-id:stack/stack-name/stack-id var stack = describeStacks.data[s]; var resource = stack.StackId; - let parameterFound = false; + let foundStrings = []; if(!stack.Parameters || !stack.Parameters.length) { helpers.addResult(results, 0, @@ -54,18 +53,19 @@ module.exports = { } stack.Parameters.forEach(function(parameter){ - if(!parameterFound && parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { - parameterFound = true; - helpers.addResult(results, 1, - 'Template contains one of the following potentially-sensitive parameters: secret, key, password', region, resource); + if(parameter.ParameterKey && secretWords.includes(parameter.ParameterKey.toLowerCase()) && !parameter.ParameterValue.match('^[*]+$')) { + foundStrings.push(parameter.ParameterKey); } }); - - if(!parameterFound) { + + if(foundStrings && foundStrings.length) { + helpers.addResult(results, 1, + 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); + } + else { helpers.addResult(results, 0, 'Template does not contain any potentially-sensitive parameters', region, resource); } - } rcb(); From 1a4b495fa18e3447c906128d6c1fb3575e5a775e Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 18 Aug 2020 21:12:35 +0500 Subject: [PATCH 61/71] Updated status in result of failure --- plugins/aws/cloudformation/plainTextParameters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index d05776bf37..3bd118572e 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -59,7 +59,7 @@ module.exports = { }); if(foundStrings && foundStrings.length) { - helpers.addResult(results, 1, + helpers.addResult(results, 2, 'Template contains the following potentially-sensitive parameters: ' + foundStrings, region, resource); } else { From 645abeba5735e00d53da769f61f0951aa5f91f4c Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 29 Aug 2020 02:44:42 +0500 Subject: [PATCH 62/71] Removed unnecesary rebase changes --- exports.js | 3 + index.js | 160 +++++++++--------- .../aws/cloudformation/plainTextParameters.js | 2 +- plugins/aws/ec2/launchWizardSecurityGroups.js | 2 +- plugins/aws/ec2/vpcEndpointAcceptance.spec.js | 9 - 5 files changed, 84 insertions(+), 92 deletions(-) diff --git a/exports.js b/exports.js index 5a338cfa5e..7139c7640e 100644 --- a/exports.js +++ b/exports.js @@ -5,6 +5,7 @@ module.exports = { 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), + 'emptyASG' : require(__dirname + '/plugins/aws/autoscaling/emptyASG.js'), 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), @@ -116,6 +117,7 @@ module.exports = { 'iamUserAdmins' : require(__dirname + '/plugins/aws/iam/iamUserAdmins.js'), 'iamUserNameRegex' : require(__dirname + '/plugins/aws/iam/iamUserNameRegex.js'), 'iamRolePolicies' : require(__dirname + '/plugins/aws/iam/iamRolePolicies.js'), + 'iamRoleLastUsed' : require(__dirname + '/plugins/aws/iam/iamRoleLastUsed.js'), 'maxPasswordAge' : require(__dirname + '/plugins/aws/iam/maxPasswordAge.js'), 'minPasswordLength' : require(__dirname + '/plugins/aws/iam/minPasswordLength.js'), 'noUserIamPolicies' : require(__dirname + '/plugins/aws/iam/noUserIamPolicies.js'), @@ -126,6 +128,7 @@ module.exports = { 'passwordRequiresUppercase' : require(__dirname + '/plugins/aws/iam/passwordRequiresUppercase.js'), 'passwordReusePrevention' : require(__dirname + '/plugins/aws/iam/passwordReusePrevention.js'), 'rootAccessKeys' : require(__dirname + '/plugins/aws/iam/rootAccessKeys.js'), + 'rootSigningCertificate' : require(__dirname + '/plugins/aws/iam/rootSigningCertificate.js'), 'rootAccountInUse' : require(__dirname + '/plugins/aws/iam/rootAccountInUse.js'), 'rootHardwareMfa' : require(__dirname + '/plugins/aws/iam/rootHardwareMfa.js'), 'rootMfaEnabled' : require(__dirname + '/plugins/aws/iam/rootMfaEnabled.js'), diff --git a/index.js b/index.js index 53b6b4fb75..26e612ef56 100755 --- a/index.js +++ b/index.js @@ -1,74 +1,74 @@ #!/usr/bin/env node -var engine = require('./engine'); - -var AWSConfig; -var AzureConfig; -var GitHubConfig; -var OracleConfig; -var GoogleConfig; - -// OPTION 1: Configure service provider credentials through hard-coded config objects - -// AWSConfig = { -// accessKeyId: '', -// secretAccessKey: '', -// sessionToken: '', -// region: 'us-east-1' -// }; - -// AzureConfig = { -// ApplicationID: '', // A.K.A ClientID -// KeyValue: '', // Secret -// DirectoryID: '', // A.K.A TenantID or Domain -// SubscriptionID: '', -// location: 'East US' -// }; - -// GitHubConfig = { -// token: '', // GitHub app token -// url: 'https://api.github.com', // BaseURL if not using public GitHub -// organization: false, // Set to true if the login is an organization -// login: '' // The login id for the user or organization -// }; - -// Oracle Important Note: -// Please read Oracle API's key generation instructions: config/_oracle/keys/Readme.md -// You will want an API signing key to fill the keyFingerprint and privateKey params -// OracleConfig = { -// RESTversion: '/20160918', -// tenancyId: 'ocid1.tenancy.oc1..', -// compartmentId: 'ocid1.compartment.oc1..', -// userId: 'ocid1.user.oc1..', -// keyFingerprint: 'YOURKEYFINGERPRINT', -// keyValue: "-----BEGIN PRIVATE KEY-----\nYOUR-PRIVATE-KEY-GOES-HERE\n-----END PRIVATE KEY-----\n", -// region: 'us-ashburn-1', -// }; - -// GoogleConfig = { -// "type": "service_account", -// "project": "your-project-name", -// "client_email": "cloudsploit@your-project-name.iam.gserviceaccount.com", -// "private_key": "-----BEGIN PRIVATE KEY-----\nYOUR-PRIVATE-KEY-GOES-HERE\n-----END PRIVATE KEY-----\n", -// }; - -// OPTION 2: Import a service provider config file containing credentials - -// AWSConfig = require(__dirname + '/aws_credentials.json'); -// AzureConfig = require(__dirname + '/azure_credentials.json'); -// GitHubConfig = require(__dirname + '/github_credentials.json'); -// OracleConfig = require(__dirname + '/oracle_credentials.json'); -// GoogleConfig = require(__dirname + '/google_credentials.json'); - -// OPTION 3: ENV configuration with service provider env vars -if(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY){ - AWSConfig = { - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, - sessionToken: process.env.AWS_SESSION_TOKEN, - region: process.env.AWS_DEFAULT_REGION || 'us-east-1' - }; -} +const { ArgumentParser } = require('argparse'); +const engine = require('./engine'); + +console.log(` + _____ _ _ _____ _ _ _ + / ____| | | |/ ____| | | (_) | + | | | | ___ _ _ __| | (___ _ __ | | ___ _| |_ + | | | |/ _ \\| | | |/ _\` |\\___ \\| '_ \\| |/ _ \\| | __| + | |____| | (_) | |_| | (_| |____) | |_) | | (_) | | |_ + \\_____|_|\\___/ \\__,_|\\__,_|_____/| .__/|_|\\___/|_|\\__| + | | + |_| + + CloudSploit by Aqua Security, Ltd. + Cloud security auditing for AWS, Azure, GCP, Oracle, and GitHub +`); + +const parser = new ArgumentParser({}); + +parser.add_argument('--config', { + help: 'The path to a CloudSploit config file containing cloud credentials. See config_example.js' +}); + +parser.add_argument('--compliance', { + help: 'Compliance mode. Only return results applicable to the selected program.', + choices: ['hipaa', 'cis', 'cis1', 'cis2', 'pci'], + action: 'append' +}); +parser.add_argument('--plugin', { + help: 'A specific plugin to run. If none provided, all plugins will be run. Obtain from the exports.js file. E.g. acmValidation' +}); +parser.add_argument('--govcloud', { + help: 'AWS only. Enables GovCloud mode.', + action: 'store_true' +}); +parser.add_argument('--china', { + help: 'AWS only. Enables AWS China mode.', + action: 'store_true' +}); +parser.add_argument('--csv', { help: 'Output: CSV file' }); +parser.add_argument('--json', { help: 'Output: JSON file' }); +parser.add_argument('--junit', { help: 'Output: Junit file' }); +parser.add_argument('--console', { + help: 'Console output format. Default: table', + choices: ['none', 'text', 'table'], + default: 'table' +}); +parser.add_argument('--collection', { help: 'Output: full collection JSON as file' }); +parser.add_argument('--ignore-ok', { + help: 'Ignore passing (OK) results', + action: 'store_true' +}); +parser.add_argument('--exit-code', { + help: 'Exits with a non-zero status code if non-passing results are found', + action: 'store_true' +}); +parser.add_argument('--skip-paginate', { + help: 'AWS only. Skips pagination (for debugging).', + action: 'store_false' +}); +parser.add_argument('--suppress', { + help: 'Suppress results matching the provided Regex. Format: pluginId:region:resourceId', + action: 'append' +}); + +let settings = parser.parse_args(); +let cloudConfig = {}; + +settings.cloud = 'aws'; // Now execute the scans using the defined configuration information. if (!settings.config) { @@ -96,19 +96,17 @@ try { console.error('ERROR: Config file could not be loaded. Please ensure you have copied the config_example.js file to config.js'); process.exit(1); } -if(process.env.GOOGLE_APPLICATION_CREDENTIALS){ - GoogleConfig = require(process.env.GOOGLE_APPLICATION_CREDENTIALS); - GoogleConfig.project = GoogleConfig.project_id; -} - -// Custom settings - place plugin-specific settings here -var settings = {}; -// If running in GovCloud, uncomment the following -// settings.govcloud = true; - -// If running in AWS China, uncomment the following -// settings.china = true; +function loadHelperFile(path) { + try { + var contents = require(path); + } catch (e) { + console.error(`ERROR: The credential file could not be loaded ${path}`); + console.error(e); + process.exit(1); + } + return contents; +} function checkRequiredKeys(obj, keys) { keys.forEach(function(key){ diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 3bd118572e..e03a98bd08 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -36,7 +36,7 @@ module.exports = { } if (!describeStacks.data.length) { - helpers.addResult(results, 0, 'No stack description found', region); + helpers.addResult(results, 0, 'No CloudFormation stacks found', region); return rcb(); } diff --git a/plugins/aws/ec2/launchWizardSecurityGroups.js b/plugins/aws/ec2/launchWizardSecurityGroups.js index caa173be82..5c73babd28 100644 --- a/plugins/aws/ec2/launchWizardSecurityGroups.js +++ b/plugins/aws/ec2/launchWizardSecurityGroups.js @@ -34,7 +34,7 @@ module.exports = { for (var s in describeSecurityGroups.data) { var sg = describeSecurityGroups.data[s]; - var resource = sg.GroupId; + var resource = 'arn:aws:ec2:' + region + ':' + sg.OwnerId + ':security-group/' + sg.GroupId; if(!sg.GroupName) { helpers.addResult(results, 2, diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js index 0946f5a682..62053b7238 100644 --- a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js +++ b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js @@ -112,15 +112,6 @@ describe('vpcEndpointAcceptance', function () { }); }); - it('should PASS if no VPC endpoint services are detected', function (done) { - const cache = createCache([]); - vpcEndpointAcceptance.run(cache, {}, (err, results) => { - expect(results.length).to.equal(1); - expect(results[0].status).to.equal(0); - done(); - }); - }); - it('should UNKNOWN if there was an error querying for VPC endpoint services', function (done) { const cache = createErrorCache(); vpcEndpointAcceptance.run(cache, {}, (err, results) => { From 9c56541df447c7ef2817c1da533c3dd0a11e0021 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 22 Aug 2020 07:24:45 +0500 Subject: [PATCH 63/71] Feature/43: Updated describeLoadBalancers api call --- plugins/aws/autoscaling/sameAzElb.js | 102 ++++-- plugins/aws/autoscaling/sameAzElb.spec.js | 413 ++++++++++++---------- 2 files changed, 287 insertions(+), 228 deletions(-) diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index 12150b9994..ea120f4c52 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -8,77 +8,107 @@ module.exports = { more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', - apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancer'], + apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers', 'ELBv2:describeLoadBalancers'], run: function(cache, settings, callback) { var results = []; var source = {}; var regions = helpers.regions(settings); + var loadBalancers = {}; async.each(regions.autoscaling, function(region, rcb){ var autoScalingGroups = helpers.addSource(cache, source, ['autoscaling', 'describeAutoScalingGroups', region]); + + var elasticLoadBalancers = helpers.addSource(cache, source, + ['elb', 'describeLoadBalancers', region]); - if (!autoScalingGroups) return rcb(); + var elasticLoadBalancersV2 = helpers.addSource(cache, source, + ['elbv2', 'describeLoadBalancers', region]); + + if (!autoScalingGroups || !elasticLoadBalancers || !elasticLoadBalancersV2) return rcb(); if (autoScalingGroups.err || !autoScalingGroups.data) { - helpers.addResult(results, 3, - 'Unable to query for AutoScaling groups: ' + - helpers.addError(autoScalingGroups), region); + helpers.addResult(results, 3, 'Unable to query for AutoScaling groups: ' + helpers.addError(autoScalingGroups), region); + return rcb(); + } + + if (elasticLoadBalancers.err || !elasticLoadBalancers.data) { + helpers.addResult(results, 3, 'Unable to query for Classic load balancers: ' + helpers.addError(elasticLoadBalancers), region); + return rcb(); + } + + if (elasticLoadBalancersV2.err || !elasticLoadBalancersV2.data) { + helpers.addResult(results, 3, 'Unable to query for Application/Network load balancers: ' + helpers.addError(elasticLoadBalancersV2), region); return rcb(); } if (!autoScalingGroups.data.length) { - helpers.addResult(results, 0, 'No AutoScaling groups found', region); + helpers.addResult(results, 0, 'No AutoScaling group found', region); return rcb(); } + + if (elasticLoadBalancers.data.length) { + elasticLoadBalancers.data.forEach(function(elb) { + if(elb.LoadBalancerName) { + loadBalancers[elb.LoadBalancerName] = elb; + } + }); + } - autoScalingGroups.data.forEach(function(asg){ - asgAvailabilityZones = asg.AvailabilityZones; - if(asg.HealthCheckType == "ELB") { - if (asg.LoadBalancerNames) { - // create ELBs and check - asg.LoadBalancerNames.forEach(function(elbName){ - var elasticLoadBalancer = helpers.addSource(cache, source, - ['autoscaling', 'describeLoadBalancers', region, elbName]); - - if(!elasticLoadBalancer || !elasticLoadBalancer.data || !elasticLoadBalancer.data.LoadBalancerDescriptions) { - console.log('Load balancer not found in AutoScaling group ' + elbName); - helpers.addResult(results, 2, - 'Load balancer not found in AutoScaling group', - region, elbName); - } - else { - var differentAzFound = false; - elbAvailabilityZones = elasticLoadBalancer.LoadBalancerDescriptions.AvailabilityZones; - elbAvailabilityZones.foreach(function(elbAz){ + if (elasticLoadBalancersV2.data.length) { + elasticLoadBalancersV2.data.forEach(function(elbv2) { + if(elbv2.LoadBalancerName) { + loadBalancers[elbv2.LoadBalancerName] = elbv2; + } + }); + } + + autoScalingGroups.data.forEach(function(asg) { + var asgAvailabilityZones = asg.AvailabilityZones; + var differentAzFound = false; + var resource = asg.AutoScalingGroupARN; + + if(asg.HealthCheckType == 'ELB') { + if(!loadBalancers.length) { + helpers.addResult(results, 0, 'No Load Balancer found', region); + return rcb(); + } + + if (asg.LoadBalancerNames && asg.LoadBalancerNames.length) { + + asg.LoadBalancerNames.forEach(function(elbName) { + if(elbName in loadBalancers) { + var loadBalancer = loadBalancers[elbName]; + differentAzFound = false; + var elbAvailabilityZones = loadBalancer.AvailabilityZones; + + // if Load Balancer is in any AZ different from AutoScaling group's AZs then add an error result + elbAvailabilityZones.forEach(function(elbAz) { if(!asgAvailabilityZones.includes(elbAz)) { differentAzFound = true; } }); + if(differentAzFound) { - console.log('Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group'); - helpers.addResult(results, 2, 'Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + helpers.addResult(results, 2, 'Load balancer "' + elbName + '" is not in the same AZ as of AutoScaling group', region, resource); } else { - console.log('Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group'); - helpers.addResult(results, 0, 'Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + helpers.addResult(results, 0, 'Load balancer "' + elbName + '" is in the same AZ as of AutoScaling group', region, resource); } } + else { + helpers.addResult(results, 2, 'AutoScaling group utilizes an inactive load balancer "'+ elbName + '"', region, resource); + } }); } else { - console.log('Load balancer not found in AutoScaling group'); - helpers.addResult(results, 2, - 'Load balancer not found in AutoScaling group', - region, elbName); + helpers.addResult(results, 2, 'AutoScaling group does not have any Load Balancer associated', region, resource); } } else { - console.log('AutoScaling group does not utilize a load balancer'); - helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, elbName); + helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, resource); } - }); rcb(); diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js index 699fe97c62..f013ffcb05 100644 --- a/plugins/aws/autoscaling/sameAzElb.spec.js +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -4,7 +4,7 @@ const sameAzElb = require('./sameAzElb'); const autoScalingGroups = [ { "AutoScalingGroupName": "auto-scaling-test-group", - "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:111122223333:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", "LaunchTemplate": { "LaunchTemplateId": "lt-0f1f6b356026abc86", "LaunchTemplateName": "auto-scaling-template", @@ -15,11 +15,16 @@ const autoScalingGroups = [ "DesiredCapacity": 1, "DefaultCooldown": 300, "AvailabilityZones": [ + "us-east-1f", + "us-east-1e", + "us-east-1d", + "us-east-1c", + "us-east-1b", "us-east-1a" ], - "LoadBalancerNames": ["my-load-balancer", "my-load-balancer2"], + "LoadBalancerNames": ["asgEmptyElb"], "TargetGroupARNs": [], - "HealthCheckType": "EC2", + "HealthCheckType": "ELB", "HealthCheckGracePeriod": 300, "Instances": [ { @@ -45,11 +50,43 @@ const autoScalingGroups = [ "Default" ], "NewInstancesProtectedFromScaleIn": false, - "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + "ServiceLinkedRoleARN": "arn:aws:iam::111122223333:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" }, { "AutoScalingGroupName": "auto-scaling-test-group", - "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:111122223333:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a", + "us-west-1a" + ], + "LoadBalancerNames": ["my-load-balancer2"], + "TargetGroupARNs": [], + "HealthCheckType": "ELB", + "HealthCheckGracePeriod": 300, + "Instances": [], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::111122223333:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:111122223333:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", "LaunchTemplate": { "LaunchTemplateId": "lt-0f1f6b356026abc86", "LaunchTemplateName": "auto-scaling-template", @@ -62,7 +99,7 @@ const autoScalingGroups = [ "AvailabilityZones": [ "us-east-1a" ], - "LoadBalancerNames": ['my-load-balancer'], + "LoadBalancerNames": [], "TargetGroupARNs": [], "HealthCheckType": "ELB", "HealthCheckGracePeriod": 300, @@ -76,195 +113,134 @@ const autoScalingGroups = [ "Default" ], "NewInstancesProtectedFromScaleIn": false, - "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + "ServiceLinkedRoleARN": "arn:aws:iam::111122223333:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:111122223333:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": [], + "TargetGroupARNs": [], + "HealthCheckType": "EC2", + "HealthCheckGracePeriod": 300, + "Instances": [], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::111122223333:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" }, ]; const loadBalancers = [ { - "LoadBalancerDescriptions": [ + "LoadBalancerName": "asgEmptyElb", + "DNSName": "asgEmptyElb-1112223333.us-east-1.elb.amazonaws.com", + "CanonicalHostedZoneName": "asgEmptyElb-1112223333.us-east-1.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z35SXDOTRQ7X7K", + "ListenerDescriptions": [ { - "AvailabilityZones": [ - "us-west-2a" - ], - "BackendServerDescriptions": [ - { - "InstancePort": 80, - "PolicyNames": [ - "my-ProxyProtocol-policy" - ] - } - ], - "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", - "CreatedTime": "2020-08-18T23:12:00.954Z", - "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "HealthCheck": { - "HealthyThreshold": 2, - "Interval": 30, - "Target": "HTTP:80/png", - "Timeout": 3, - "UnhealthyThreshold": 2 - }, - "Instances": [ - { - "InstanceId": "i-207d9717" - }, - { - "InstanceId": "i-afefb49b" - } - ], - "ListenerDescriptions": [ - { - "Listener": { - "InstancePort": 80, - "InstanceProtocol": "HTTP", - "LoadBalancerPort": 80, - "Protocol": "HTTP" - }, - "PolicyNames": [ - ] - }, - { - "Listener": { - "InstancePort": 443, - "InstanceProtocol": "HTTPS", - "LoadBalancerPort": 443, - "Protocol": "HTTPS", - "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" - }, - "PolicyNames": [ - "ELBSecurityPolicy-2015-03" - ] - } - ], - "LoadBalancerName": "my-load-balancer", - "Policies": { - "AppCookieStickinessPolicies": [ - ], - "LBCookieStickinessPolicies": [ - { - "CookieExpirationPeriod": 60, - "PolicyName": "my-duration-cookie-policy" - } - ], - "OtherPolicies": [ - "my-PublicKey-policy", - "my-authentication-policy", - "my-SSLNegotiation-policy", - "my-ProxyProtocol-policy", - "ELBSecurityPolicy-2015-03" - ] - }, - "Scheme": "internet-facing", - "SecurityGroups": [ - "sg-a61988c3" - ], - "SourceSecurityGroup": { - "GroupName": "my-elb-sg", - "OwnerAlias": "123456789012" - }, - "Subnets": [ - "subnet-15aaab61" - ], - "VPCId": "vpc-a01106c2" + "Listener": { + "Protocol": "HTTP", + "LoadBalancerPort": 80, + "InstanceProtocol": "HTTP", + "InstancePort": 80 + }, + "PolicyNames": [] } - ] - }, + ], + "Policies": { + "AppCookieStickinessPolicies": [], + "LBCookieStickinessPolicies": [], + "OtherPolicies": [] + }, + "BackendServerDescriptions": [], + "AvailabilityZones": [ + "us-east-1d", + "us-east-1c", + "us-east-1b", + "us-east-1a" + ], + "Subnets": [ + "subnet-06aa0f60", + "subnet-673a9a46", + "subnet-6a8b635b", + "subnet-aac6b3e7", + "subnet-c21b84cc", + "subnet-e83690b7" + ], + "VPCId": "vpc-99de2fe4", + "Instances": [ + { + "InstanceId": "i-00178f718e021c46b" + } + ], + "HealthCheck": { + "Target": "HTTP:80/index.html", + "Interval": 30, + "Timeout": 5, + "UnhealthyThreshold": 2, + "HealthyThreshold": 10 + }, + "SourceSecurityGroup": { + "OwnerAlias": "111122223333", + "GroupName": "default" + }, + "SecurityGroups": [ + "sg-aa941691" + ], + "CreatedTime": "2020-08-26T02:49:20.250Z", + "Scheme": "internet-facing" + } +] + +const loadBalancersV2 = [ { - "LoadBalancerDescriptions": [ + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:111122223333:loadbalancer/net/networkELB/2ea2eb9b8d381866", + "DNSName": "networkELB-2ea2eb9b8d381866.elb.us-east-1.amazonaws.com", + "CanonicalHostedZoneId": "Z26RNL4JYFTOTI", + "CreatedTime": "2020-08-26T02:50:34.119Z", + "LoadBalancerName": "networkELB", + "Scheme": "internet-facing", + "VpcId": "vpc-99de2fe4", + "State": { + "Code": "active" + }, + "Type": "network", + "AvailabilityZones": [ + { + "ZoneName": "us-east-1b", + "SubnetId": "subnet-673a9a46", + "LoadBalancerAddresses": [] + }, { - "AvailabilityZones": [ - "us-west-2a" - ], - "BackendServerDescriptions": [ - { - "InstancePort": 80, - "PolicyNames": [ - "my-ProxyProtocol-policy" - ] - } - ], - "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", - "CreatedTime": "2020-08-18T23:12:00.954Z", - "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "HealthCheck": { - "HealthyThreshold": 2, - "Interval": 30, - "Target": "HTTP:80/png", - "Timeout": 3, - "UnhealthyThreshold": 2 - }, - "Instances": [ - { - "InstanceId": "i-207d9717" - }, - { - "InstanceId": "i-afefb49b" - } - ], - "ListenerDescriptions": [ - { - "Listener": { - "InstancePort": 80, - "InstanceProtocol": "HTTP", - "LoadBalancerPort": 80, - "Protocol": "HTTP" - }, - "PolicyNames": [ - ] - }, - { - "Listener": { - "InstancePort": 443, - "InstanceProtocol": "HTTPS", - "LoadBalancerPort": 443, - "Protocol": "HTTPS", - "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" - }, - "PolicyNames": [ - "ELBSecurityPolicy-2015-03" - ] - } - ], - "LoadBalancerName": "my-load-balancer", - "Policies": { - "AppCookieStickinessPolicies": [ - ], - "LBCookieStickinessPolicies": [ - { - "CookieExpirationPeriod": 60, - "PolicyName": "my-duration-cookie-policy" - } - ], - "OtherPolicies": [ - "my-PublicKey-policy", - "my-authentication-policy", - "my-SSLNegotiation-policy", - "my-ProxyProtocol-policy", - "ELBSecurityPolicy-2015-03" - ] - }, - "Scheme": "internet-facing", - "SecurityGroups": [ - "sg-a61988c3" - ], - "SourceSecurityGroup": { - "GroupName": "my-elb-sg", - "OwnerAlias": "123456789012" - }, - "Subnets": [ - "subnet-15aaab61" - ], - "VPCId": "vpc-a01106c2" + "ZoneName": "us-east-1a", + "SubnetId": "subnet-06aa0f60", + "LoadBalancerAddresses": [] } - ] - }, -]; + ], + "IpAddressType": "ipv4" + } +] -const createCache = (asgs, elb) => { +const createCache = (asgs, elb, elbv2) => { return { autoscaling: { describeAutoScalingGroups: { @@ -272,11 +248,18 @@ const createCache = (asgs, elb) => { data: asgs }, }, + }, + elb:{ describeLoadBalancers: { 'us-east-1': { - [asgs[0].LoadBalancerNames]: { - data: elb - }, + data: elb + }, + }, + }, + elbv2:{ + describeLoadBalancers: { + 'us-east-1': { + data: elbv2 }, }, }, @@ -289,7 +272,25 @@ const createErrorCache = () => { describeAutoScalingGroups: { 'us-east-1': { err: { - message: 'error describing autoscaling groups' + message: 'error describing AutoScaling groups' + }, + }, + }, + }, + elb: { + describeLoadBalancers: { + 'us-east-1': { + err: { + message: 'error describing classic load balancers' + }, + }, + }, + }, + elbv2: { + describeLoadBalancers: { + 'us-east-1': { + err: { + message: 'error describing application/network load balancers' }, }, }, @@ -304,13 +305,23 @@ const createNullCache = () => { 'us-east-1': null, }, }, + elb: { + describeLoadBalancers: { + 'us-east-1': null, + }, + }, + elbv2: { + describeLoadBalancers: { + 'us-east-1': null, + }, + }, }; }; describe('sameAzElb', function () { describe('run', function () { - it('should PASS if autoscaling group utilizes active load balancer', function (done) { - const cache = createCache([autoScalingGroups[1]], loadBalancers[0]); + it('should PASS if load balancer is in the same Availability Zone as of AutoScaling group', function (done) { + const cache = createCache([autoScalingGroups[0]], [loadBalancers[0]], [loadBalancersV2[0]]); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -318,15 +329,33 @@ describe('sameAzElb', function () { }); }); - it('should FAIL if autoscaling group does not utilizes active load balancer', function (done) { - const cache = createCache([autoScalingGroups[1]], []); + it('should PASS if AutoScaling does not utilizes load balancer as HealthCheckType', function (done) { + const cache = createCache([autoScalingGroups[3]], [loadBalancers[0]], [loadBalancersV2[0]]); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should FAIL if load balancer is not in the same Availability Zone as of AutoScaling group', function (done) { + const cache = createCache([autoScalingGroups[1]], [loadBalancers[0]], [loadBalancersV2[0]]); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); done(); }); }); - + + it('should FAIL if autoscaling group utilizes an inactive load balancer', function (done) { + const cache = createCache([autoScalingGroups[1]], null, null); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + it('should UNKNOWN if unable to describe autoscaling group found', function (done) { const cache = createErrorCache(); sameAzElb.run(cache, {}, (err, results) => { From 89a3f7849888cb57309a87e2ea275fa7bca54201 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Sun, 30 Aug 2020 05:09:51 +0500 Subject: [PATCH 64/71] Updated sameAzElb plugin --- plugins/aws/autoscaling/sameAzElb.js | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index ea120f4c52..5bcd64cbef 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -4,10 +4,10 @@ var helpers = require('../../../helpers/aws'); module.exports = { title: 'AutoScaling Group Missing ELB', category: 'AutoScaling', - description: 'Ensures all autoscaling groups are referencing active load balancers.', - more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', - link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', - recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', + description: 'Ensures all autoscaling groups with attached ELBs are operating in the same availability zone.', + more_info: 'To work properly and prevent orphaned instances, ELBs must be created in the same availability zones as the backend instances in the autoscaling group.', + link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-add-availability-zone.html', + recommended_action: 'Update the ELB to use the same availability zones as the autoscaling group.', apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers', 'ELBv2:describeLoadBalancers'], run: function(cache, settings, callback) { @@ -66,35 +66,33 @@ module.exports = { autoScalingGroups.data.forEach(function(asg) { var asgAvailabilityZones = asg.AvailabilityZones; - var differentAzFound = false; + var distinctAzs = []; var resource = asg.AutoScalingGroupARN; if(asg.HealthCheckType == 'ELB') { - if(!loadBalancers.length) { - helpers.addResult(results, 0, 'No Load Balancer found', region); - return rcb(); - } - if (asg.LoadBalancerNames && asg.LoadBalancerNames.length) { asg.LoadBalancerNames.forEach(function(elbName) { if(elbName in loadBalancers) { var loadBalancer = loadBalancers[elbName]; - differentAzFound = false; var elbAvailabilityZones = loadBalancer.AvailabilityZones; // if Load Balancer is in any AZ different from AutoScaling group's AZs then add an error result elbAvailabilityZones.forEach(function(elbAz) { if(!asgAvailabilityZones.includes(elbAz)) { - differentAzFound = true; + distinctAzs.push(elbAz); } }); - if(differentAzFound) { - helpers.addResult(results, 2, 'Load balancer "' + elbName + '" is not in the same AZ as of AutoScaling group', region, resource); + if(distinctAzs.length) { + helpers.addResult(results, 2, + 'Auto scaling group "' + asg.AutoScalingGroupName + '" has these load balancers in different AZs: ' + distinctAzs.join(', '), + region, resource); } else { - helpers.addResult(results, 0, 'Load balancer "' + elbName + '" is in the same AZ as of AutoScaling group', region, resource); + helpers.addResult(results, 0, + 'Auto scaling group "' + asg.AutoScalingGroupName + '" has all load balancers in same AZ', + region, resource); } } else { From 3cf14f3f9e7a5e799ea3bfcfab7478bb0a93c64f Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:59:28 +0500 Subject: [PATCH 65/71] Added Same Availability Zone in ASG and ELB plugin --- exports.js | 1 + plugins/aws/autoscaling/sameAzElb.js | 89 ++++++ plugins/aws/autoscaling/sameAzElb.spec.js | 348 ++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 plugins/aws/autoscaling/sameAzElb.js create mode 100644 plugins/aws/autoscaling/sameAzElb.spec.js diff --git a/exports.js b/exports.js index 7139c7640e..a6c2b3909d 100644 --- a/exports.js +++ b/exports.js @@ -6,6 +6,7 @@ module.exports = { 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), 'emptyASG' : require(__dirname + '/plugins/aws/autoscaling/emptyASG.js'), + 'sameAzElb' : require(__dirname + '/plugins/aws/autoscaling/sameAzElb.js'), 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), 'workgroupEnforceConfiguration' : require(__dirname + '/plugins/aws/athena/workgroupEnforceConfiguration.js'), 'publicS3Origin' : require(__dirname + '/plugins/aws/cloudfront/publicS3Origin.js'), diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js new file mode 100644 index 0000000000..12150b9994 --- /dev/null +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -0,0 +1,89 @@ +var async = require('async'); +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'AutoScaling Group Missing ELB', + category: 'AutoScaling', + description: 'Ensures all autoscaling groups are referencing active load balancers.', + more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', + link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', + recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', + apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancer'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(settings); + + async.each(regions.autoscaling, function(region, rcb){ + var autoScalingGroups = helpers.addSource(cache, source, + ['autoscaling', 'describeAutoScalingGroups', region]); + + if (!autoScalingGroups) return rcb(); + + if (autoScalingGroups.err || !autoScalingGroups.data) { + helpers.addResult(results, 3, + 'Unable to query for AutoScaling groups: ' + + helpers.addError(autoScalingGroups), region); + return rcb(); + } + + if (!autoScalingGroups.data.length) { + helpers.addResult(results, 0, 'No AutoScaling groups found', region); + return rcb(); + } + + autoScalingGroups.data.forEach(function(asg){ + asgAvailabilityZones = asg.AvailabilityZones; + if(asg.HealthCheckType == "ELB") { + if (asg.LoadBalancerNames) { + // create ELBs and check + asg.LoadBalancerNames.forEach(function(elbName){ + var elasticLoadBalancer = helpers.addSource(cache, source, + ['autoscaling', 'describeLoadBalancers', region, elbName]); + + if(!elasticLoadBalancer || !elasticLoadBalancer.data || !elasticLoadBalancer.data.LoadBalancerDescriptions) { + console.log('Load balancer not found in AutoScaling group ' + elbName); + helpers.addResult(results, 2, + 'Load balancer not found in AutoScaling group', + region, elbName); + } + else { + var differentAzFound = false; + elbAvailabilityZones = elasticLoadBalancer.LoadBalancerDescriptions.AvailabilityZones; + elbAvailabilityZones.foreach(function(elbAz){ + if(!asgAvailabilityZones.includes(elbAz)) { + differentAzFound = true; + } + }); + if(differentAzFound) { + console.log('Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group'); + helpers.addResult(results, 2, 'Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + } + else { + console.log('Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group'); + helpers.addResult(results, 0, 'Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + } + } + }); + } + else { + console.log('Load balancer not found in AutoScaling group'); + helpers.addResult(results, 2, + 'Load balancer not found in AutoScaling group', + region, elbName); + } + } + else { + console.log('AutoScaling group does not utilize a load balancer'); + helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, elbName); + } + + }); + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js new file mode 100644 index 0000000000..699fe97c62 --- /dev/null +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -0,0 +1,348 @@ +var expect = require('chai').expect; +const sameAzElb = require('./sameAzElb'); + +const autoScalingGroups = [ + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": ["my-load-balancer", "my-load-balancer2"], + "TargetGroupARNs": [], + "HealthCheckType": "EC2", + "HealthCheckGracePeriod": 300, + "Instances": [ + { + "InstanceId": "i-093267d7a579c4bee", + "InstanceType": "t2.micro", + "AvailabilityZone": "us-east-1a", + "LifecycleState": "InService", + "HealthStatus": "Healthy", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "1" + }, + "ProtectedFromScaleIn": false + } + ], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": ['my-load-balancer'], + "TargetGroupARNs": [], + "HealthCheckType": "ELB", + "HealthCheckGracePeriod": 300, + "Instances": [], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, +]; + +const loadBalancers = [ + { + "LoadBalancerDescriptions": [ + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + }, + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ + ], + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } + ], + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } + ] + }, + { + "LoadBalancerDescriptions": [ + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + }, + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ + ], + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } + ], + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } + ] + }, +]; + + +const createCache = (asgs, elb) => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': { + data: asgs + }, + }, + describeLoadBalancers: { + 'us-east-1': { + [asgs[0].LoadBalancerNames]: { + data: elb + }, + }, + }, + }, + }; +}; + +const createErrorCache = () => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': { + err: { + message: 'error describing autoscaling groups' + }, + }, + }, + }, + }; +}; + +const createNullCache = () => { + return { + autoscaling: { + describeAutoScalingGroups: { + 'us-east-1': null, + }, + }, + }; +}; + +describe('sameAzElb', function () { + describe('run', function () { + it('should PASS if autoscaling group utilizes active load balancer', function (done) { + const cache = createCache([autoScalingGroups[1]], loadBalancers[0]); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should FAIL if autoscaling group does not utilizes active load balancer', function (done) { + const cache = createCache([autoScalingGroups[1]], []); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + done(); + }); + }); + + it('should UNKNOWN if unable to describe autoscaling group found', function (done) { + const cache = createErrorCache(); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + + it('should not return anything if no autoscaling group found', function (done) { + const cache = createNullCache(); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(0); + done(); + }); + }); + + }); +}); \ No newline at end of file From 4d2cb6c18fa6b05fcc1aad0b2c7e62154b251164 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 22 Aug 2020 07:24:45 +0500 Subject: [PATCH 66/71] Feature/43: Updated describeLoadBalancers api call --- plugins/aws/autoscaling/sameAzElb.js | 70 ++-- plugins/aws/autoscaling/sameAzElb.spec.js | 400 +++++++++++++--------- 2 files changed, 269 insertions(+), 201 deletions(-) diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index 12150b9994..9988c65be0 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -8,7 +8,7 @@ module.exports = { more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', - apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancer'], + apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers'], run: function(cache, settings, callback) { var results = []; @@ -18,13 +18,20 @@ module.exports = { async.each(regions.autoscaling, function(region, rcb){ var autoScalingGroups = helpers.addSource(cache, source, ['autoscaling', 'describeAutoScalingGroups', region]); + + var elasticLoadBalancers = helpers.addSource(cache, source, + ['elb', 'describeLoadBalancers', region]); + + if (!autoScalingGroups || !elasticLoadBalancers) return rcb(); - if (!autoScalingGroups) return rcb(); if (autoScalingGroups.err || !autoScalingGroups.data) { - helpers.addResult(results, 3, - 'Unable to query for AutoScaling groups: ' + - helpers.addError(autoScalingGroups), region); + helpers.addResult(results, 3, 'Unable to query for AutoScaling groups: ' + helpers.addError(autoScalingGroups), region); + return rcb(); + } + + if (elasticLoadBalancers.err || !elasticLoadBalancers.data) { + helpers.addResult(results, 3, 'Unable to query for load balancers: ' + helpers.addError(elasticLoadBalancers), region); return rcb(); } @@ -34,51 +41,48 @@ module.exports = { } autoScalingGroups.data.forEach(function(asg){ - asgAvailabilityZones = asg.AvailabilityZones; - if(asg.HealthCheckType == "ELB") { - if (asg.LoadBalancerNames) { - // create ELBs and check - asg.LoadBalancerNames.forEach(function(elbName){ - var elasticLoadBalancer = helpers.addSource(cache, source, - ['autoscaling', 'describeLoadBalancers', region, elbName]); + var asgAvailabilityZones = asg.AvailabilityZones; + var asgName = asg.AutoScalingGroupName; + var differentAzFound = false; + + if(asg.HealthCheckType == 'ELB') { + if (asg.LoadBalancerNames && asg.LoadBalancerNames.length) { - if(!elasticLoadBalancer || !elasticLoadBalancer.data || !elasticLoadBalancer.data.LoadBalancerDescriptions) { - console.log('Load balancer not found in AutoScaling group ' + elbName); - helpers.addResult(results, 2, - 'Load balancer not found in AutoScaling group', - region, elbName); - } - else { - var differentAzFound = false; - elbAvailabilityZones = elasticLoadBalancer.LoadBalancerDescriptions.AvailabilityZones; - elbAvailabilityZones.foreach(function(elbAz){ + if (!elasticLoadBalancers.data.length) { + helpers.addResult(results, 2, 'No load balancers found', region); + return rcb(); + } + + elasticLoadBalancers.data.forEach(function(elb) { + var elbName = elb.LoadBalancerName; + + if(asg.LoadBalancerNames.includes(elb.LoadBalancerName)){ + differentAzFound = false; + var elbAvailabilityZones = elb.AvailabilityZones; + + //if Load Balancer is in any AZ different from AutoScaling group's AZs then mark ELB as different + elbAvailabilityZones.forEach(function(elbAz){ if(!asgAvailabilityZones.includes(elbAz)) { differentAzFound = true; } }); + if(differentAzFound) { - console.log('Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group'); - helpers.addResult(results, 2, 'Load balancer ' + elbName + ' is not in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + helpers.addResult(results, 2, 'Load balancer "' + elbName + '" is not in the same AZ as of AutoScaling group', region, asgName); } else { - console.log('Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group'); - helpers.addResult(results, 0, 'Load balancer ' + elbName + ' is in the same AZ as of AutoScaling group', region, asg.autoScalingGroupName); + helpers.addResult(results, 0, 'Load balancer "' + elbName + '" is in the same AZ as of AutoScaling group', region, asgName); } } }); } else { - console.log('Load balancer not found in AutoScaling group'); - helpers.addResult(results, 2, - 'Load balancer not found in AutoScaling group', - region, elbName); + helpers.addResult(results, 2, 'AutoScaling group does not have any Load Balancer associated', region, asgName); } } else { - console.log('AutoScaling group does not utilize a load balancer'); - helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, elbName); + helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, asgName); } - }); rcb(); diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js index 699fe97c62..32a43489d4 100644 --- a/plugins/aws/autoscaling/sameAzElb.spec.js +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -17,9 +17,9 @@ const autoScalingGroups = [ "AvailabilityZones": [ "us-east-1a" ], - "LoadBalancerNames": ["my-load-balancer", "my-load-balancer2"], + "LoadBalancerNames": ["my-load-balancer"], "TargetGroupARNs": [], - "HealthCheckType": "EC2", + "HealthCheckType": "ELB", "HealthCheckGracePeriod": 300, "Instances": [ { @@ -78,189 +78,226 @@ const autoScalingGroups = [ "NewInstancesProtectedFromScaleIn": false, "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": [], + "TargetGroupARNs": [], + "HealthCheckType": "EC2", + "HealthCheckGracePeriod": 300, + "Instances": [ + { + "InstanceId": "i-093267d7a579c4bee", + "InstanceType": "t2.micro", + "AvailabilityZone": "us-east-1a", + "LifecycleState": "InService", + "HealthStatus": "Healthy", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "1" + }, + "ProtectedFromScaleIn": false + } + ], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, ]; const loadBalancers = [ { - "LoadBalancerDescriptions": [ + "AvailabilityZones": [ + "us-east-1a" + ], + "BackendServerDescriptions": [ { - "AvailabilityZones": [ - "us-west-2a" - ], - "BackendServerDescriptions": [ - { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { "InstancePort": 80, - "PolicyNames": [ - "my-ProxyProtocol-policy" - ] - } - ], - "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", - "CreatedTime": "2020-08-18T23:12:00.954Z", - "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "HealthCheck": { - "HealthyThreshold": 2, - "Interval": 30, - "Target": "HTTP:80/png", - "Timeout": 3, - "UnhealthyThreshold": 2 + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + ] }, - "Instances": [ - { - "InstanceId": "i-207d9717" + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" }, - { - "InstanceId": "i-afefb49b" - } + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ ], - "ListenerDescriptions": [ - { - "Listener": { - "InstancePort": 80, - "InstanceProtocol": "HTTP", - "LoadBalancerPort": 80, - "Protocol": "HTTP" - }, - "PolicyNames": [ - ] - }, + "LBCookieStickinessPolicies": [ { - "Listener": { - "InstancePort": 443, - "InstanceProtocol": "HTTPS", - "LoadBalancerPort": 443, - "Protocol": "HTTPS", - "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" - }, - "PolicyNames": [ - "ELBSecurityPolicy-2015-03" - ] + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" } ], - "LoadBalancerName": "my-load-balancer", - "Policies": { - "AppCookieStickinessPolicies": [ - ], - "LBCookieStickinessPolicies": [ - { - "CookieExpirationPeriod": 60, - "PolicyName": "my-duration-cookie-policy" - } - ], - "OtherPolicies": [ - "my-PublicKey-policy", - "my-authentication-policy", - "my-SSLNegotiation-policy", - "my-ProxyProtocol-policy", - "ELBSecurityPolicy-2015-03" + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + }, + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2020-08-18T23:12:00.954Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" }, - "Scheme": "internet-facing", - "SecurityGroups": [ - "sg-a61988c3" - ], - "SourceSecurityGroup": { - "GroupName": "my-elb-sg", - "OwnerAlias": "123456789012" - }, - "Subnets": [ - "subnet-15aaab61" - ], - "VPCId": "vpc-a01106c2" + { + "InstanceId": "i-afefb49b" } - ] - }, - { - "LoadBalancerDescriptions": [ + ], + "ListenerDescriptions": [ { - "AvailabilityZones": [ - "us-west-2a" - ], - "BackendServerDescriptions": [ - { + "Listener": { "InstancePort": 80, - "PolicyNames": [ - "my-ProxyProtocol-policy" - ] - } - ], - "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", - "CreatedTime": "2020-08-18T23:12:00.954Z", - "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", - "HealthCheck": { - "HealthyThreshold": 2, - "Interval": 30, - "Target": "HTTP:80/png", - "Timeout": 3, - "UnhealthyThreshold": 2 - }, - "Instances": [ - { - "InstanceId": "i-207d9717" + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" }, - { - "InstanceId": "i-afefb49b" - } - ], - "ListenerDescriptions": [ - { - "Listener": { - "InstancePort": 80, - "InstanceProtocol": "HTTP", - "LoadBalancerPort": 80, - "Protocol": "HTTP" - }, - "PolicyNames": [ - ] + "PolicyNames": [ + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" }, - { - "Listener": { - "InstancePort": 443, - "InstanceProtocol": "HTTPS", - "LoadBalancerPort": 443, - "Protocol": "HTTPS", - "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" - }, - "PolicyNames": [ - "ELBSecurityPolicy-2015-03" - ] - } - ], - "LoadBalancerName": "my-load-balancer", - "Policies": { - "AppCookieStickinessPolicies": [ - ], - "LBCookieStickinessPolicies": [ - { - "CookieExpirationPeriod": 60, - "PolicyName": "my-duration-cookie-policy" - } - ], - "OtherPolicies": [ - "my-PublicKey-policy", - "my-authentication-policy", - "my-SSLNegotiation-policy", - "my-ProxyProtocol-policy", + "PolicyNames": [ "ELBSecurityPolicy-2015-03" ] - }, - "Scheme": "internet-facing", - "SecurityGroups": [ - "sg-a61988c3" + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ ], - "SourceSecurityGroup": { - "GroupName": "my-elb-sg", - "OwnerAlias": "123456789012" - }, - "Subnets": [ - "subnet-15aaab61" + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } ], - "VPCId": "vpc-a01106c2" - } - ] - }, + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } ]; @@ -272,11 +309,11 @@ const createCache = (asgs, elb) => { data: asgs }, }, + }, + elb:{ describeLoadBalancers: { 'us-east-1': { - [asgs[0].LoadBalancerNames]: { - data: elb - }, + data: elb }, }, }, @@ -294,6 +331,15 @@ const createErrorCache = () => { }, }, }, + elb: { + describeLoadBalancers: { + 'us-east-1': { + err: { + message: 'error describing load balancers' + }, + }, + }, + }, }; }; @@ -309,8 +355,17 @@ const createNullCache = () => { describe('sameAzElb', function () { describe('run', function () { - it('should PASS if autoscaling group utilizes active load balancer', function (done) { - const cache = createCache([autoScalingGroups[1]], loadBalancers[0]); + it('should PASS if load balancer is in the same Availability Zone as of AutoScaling group', function (done) { + const cache = createCache([autoScalingGroups[0]], [loadBalancers[0]]); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + done(); + }); + }); + + it('should PASS if AutoScaling does not utilizes load balancer as HealthCheckType', function (done) { + const cache = createCache([autoScalingGroups[2]], [loadBalancers[1]]); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -318,7 +373,16 @@ describe('sameAzElb', function () { }); }); - it('should FAIL if autoscaling group does not utilizes active load balancer', function (done) { + it('should FAIL if load balancer is not in the same Availability Zone as of AutoScaling group', function (done) { + const cache = createCache([autoScalingGroups[1]], [loadBalancers[1]]); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + done(); + }); + }); + + it('should FAIL if autoscaling group utilizes an inactive load balancer', function (done) { const cache = createCache([autoScalingGroups[1]], []); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); From e94a07099fa6caeaeaae6078b07b30f280dab428 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sat, 22 Aug 2020 07:24:45 +0500 Subject: [PATCH 67/71] Feature/43: Updated describeLoadBalancers api call --- plugins/aws/autoscaling/sameAzElb.js | 82 ++++++++++++++- plugins/aws/autoscaling/sameAzElb.spec.js | 122 ++++++++++++++++++++-- 2 files changed, 195 insertions(+), 9 deletions(-) diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index 9988c65be0..0eab8f6d77 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -8,12 +8,17 @@ module.exports = { more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', +<<<<<<< HEAD apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers'], +======= + apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers', 'ELBv2:describeLoadBalancers'], +>>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call run: function(cache, settings, callback) { var results = []; var source = {}; var regions = helpers.regions(settings); + var loadBalancers = {}; async.each(regions.autoscaling, function(region, rcb){ var autoScalingGroups = helpers.addSource(cache, source, @@ -21,9 +26,17 @@ module.exports = { var elasticLoadBalancers = helpers.addSource(cache, source, ['elb', 'describeLoadBalancers', region]); +<<<<<<< HEAD if (!autoScalingGroups || !elasticLoadBalancers) return rcb(); +======= + + var elasticLoadBalancersV2 = helpers.addSource(cache, source, + ['elbv2', 'describeLoadBalancers', region]); + + if (!autoScalingGroups || !elasticLoadBalancers || !elasticLoadBalancersV2) return rcb(); +>>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call if (autoScalingGroups.err || !autoScalingGroups.data) { helpers.addResult(results, 3, 'Unable to query for AutoScaling groups: ' + helpers.addError(autoScalingGroups), region); @@ -31,15 +44,33 @@ module.exports = { } if (elasticLoadBalancers.err || !elasticLoadBalancers.data) { +<<<<<<< HEAD helpers.addResult(results, 3, 'Unable to query for load balancers: ' + helpers.addError(elasticLoadBalancers), region); +======= + helpers.addResult(results, 3, 'Unable to query for Classic load balancers: ' + helpers.addError(elasticLoadBalancers), region); + return rcb(); + } + + if (elasticLoadBalancersV2.err || !elasticLoadBalancersV2.data) { + helpers.addResult(results, 3, 'Unable to query for Application/Network load balancers: ' + helpers.addError(elasticLoadBalancersV2), region); +>>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call return rcb(); } if (!autoScalingGroups.data.length) { - helpers.addResult(results, 0, 'No AutoScaling groups found', region); + helpers.addResult(results, 0, 'No AutoScaling group found', region); return rcb(); } + + if (elasticLoadBalancers.data.length) { + elasticLoadBalancers.data.forEach(function(elb) { + if(elb.LoadBalancerName) { + loadBalancers[elb.LoadBalancerName] = elb; + } + }); + } +<<<<<<< HEAD autoScalingGroups.data.forEach(function(asg){ var asgAvailabilityZones = asg.AvailabilityZones; var asgName = asg.AutoScalingGroupName; @@ -62,26 +93,75 @@ module.exports = { //if Load Balancer is in any AZ different from AutoScaling group's AZs then mark ELB as different elbAvailabilityZones.forEach(function(elbAz){ +======= + if (elasticLoadBalancersV2.data.length) { + elasticLoadBalancersV2.data.forEach(function(elbv2) { + if(elbv2.LoadBalancerName) { + loadBalancers[elbv2.LoadBalancerName] = elbv2; + } + }); + } + + autoScalingGroups.data.forEach(function(asg) { + var asgAvailabilityZones = asg.AvailabilityZones; + var differentAzFound = false; + var resource = asg.AutoScalingGroupARN; + + if(asg.HealthCheckType == 'ELB') { + if(!loadBalancers.length) { + helpers.addResult(results, 0, 'No Load Balancer found', region); + return rcb(); + } + + if (asg.LoadBalancerNames && asg.LoadBalancerNames.length) { + + asg.LoadBalancerNames.forEach(function(elbName) { + if(elbName in loadBalancers) { + var loadBalancer = loadBalancers[elbName]; + differentAzFound = false; + var elbAvailabilityZones = loadBalancer.AvailabilityZones; + + // if Load Balancer is in any AZ different from AutoScaling group's AZs then add an error result + elbAvailabilityZones.forEach(function(elbAz) { +>>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call if(!asgAvailabilityZones.includes(elbAz)) { differentAzFound = true; } }); if(differentAzFound) { +<<<<<<< HEAD helpers.addResult(results, 2, 'Load balancer "' + elbName + '" is not in the same AZ as of AutoScaling group', region, asgName); } else { helpers.addResult(results, 0, 'Load balancer "' + elbName + '" is in the same AZ as of AutoScaling group', region, asgName); +======= + helpers.addResult(results, 2, 'Load balancer "' + elbName + '" is not in the same AZ as of AutoScaling group', region, resource); } + else { + helpers.addResult(results, 0, 'Load balancer "' + elbName + '" is in the same AZ as of AutoScaling group', region, resource); +>>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call + } + } + else { + helpers.addResult(results, 2, 'AutoScaling group utilizes an inactive load balancer "'+ elbName + '"', region, resource); } }); } else { +<<<<<<< HEAD helpers.addResult(results, 2, 'AutoScaling group does not have any Load Balancer associated', region, asgName); } } else { helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, asgName); +======= + helpers.addResult(results, 2, 'AutoScaling group does not have any Load Balancer associated', region, resource); + } + } + else { + helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, resource); +>>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call } }); diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js index 32a43489d4..a9cd5c7726 100644 --- a/plugins/aws/autoscaling/sameAzElb.spec.js +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -4,7 +4,7 @@ const sameAzElb = require('./sameAzElb'); const autoScalingGroups = [ { "AutoScalingGroupName": "auto-scaling-test-group", - "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:111122223333:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", "LaunchTemplate": { "LaunchTemplateId": "lt-0f1f6b356026abc86", "LaunchTemplateName": "auto-scaling-template", @@ -15,6 +15,11 @@ const autoScalingGroups = [ "DesiredCapacity": 1, "DefaultCooldown": 300, "AvailabilityZones": [ + "us-east-1f", + "us-east-1e", + "us-east-1d", + "us-east-1c", + "us-east-1b", "us-east-1a" ], "LoadBalancerNames": ["my-load-balancer"], @@ -45,11 +50,43 @@ const autoScalingGroups = [ "Default" ], "NewInstancesProtectedFromScaleIn": false, - "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + "ServiceLinkedRoleARN": "arn:aws:iam::111122223333:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" }, { "AutoScalingGroupName": "auto-scaling-test-group", - "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:560213429563:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:111122223333:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a", + "us-west-1a" + ], + "LoadBalancerNames": ["my-load-balancer2"], + "TargetGroupARNs": [], + "HealthCheckType": "ELB", + "HealthCheckGracePeriod": 300, + "Instances": [], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::111122223333:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:111122223333:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", "LaunchTemplate": { "LaunchTemplateId": "lt-0f1f6b356026abc86", "LaunchTemplateName": "auto-scaling-template", @@ -62,7 +99,7 @@ const autoScalingGroups = [ "AvailabilityZones": [ "us-east-1a" ], - "LoadBalancerNames": ['my-load-balancer'], + "LoadBalancerNames": [], "TargetGroupARNs": [], "HealthCheckType": "ELB", "HealthCheckGracePeriod": 300, @@ -76,7 +113,38 @@ const autoScalingGroups = [ "Default" ], "NewInstancesProtectedFromScaleIn": false, - "ServiceLinkedRoleARN": "arn:aws:iam::560213429563:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + "ServiceLinkedRoleARN": "arn:aws:iam::111122223333:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" + }, + { + "AutoScalingGroupName": "auto-scaling-test-group", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:111122223333:autoScalingGroup:e83ceb12-2760-4a92-a374-3df611331bdc:autoScalingGroupName/auto-scaling-test-group", + "LaunchTemplate": { + "LaunchTemplateId": "lt-0f1f6b356026abc86", + "LaunchTemplateName": "auto-scaling-template", + "Version": "$Default" + }, + "MinSize": 1, + "MaxSize": 1, + "DesiredCapacity": 1, + "DefaultCooldown": 300, + "AvailabilityZones": [ + "us-east-1a" + ], + "LoadBalancerNames": [], + "TargetGroupARNs": [], + "HealthCheckType": "EC2", + "HealthCheckGracePeriod": 300, + "Instances": [], + "CreatedTime": "2020-08-18T23:12:00.954Z", + "SuspendedProcesses": [], + "VPCZoneIdentifier": "subnet-06aa0f60", + "EnabledMetrics": [], + "Tags": [], + "TerminationPolicies": [ + "Default" + ], + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::111122223333:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" }, { "AutoScalingGroupName": "auto-scaling-test-group", @@ -300,8 +368,9 @@ const loadBalancers = [ } ]; +] -const createCache = (asgs, elb) => { +const createCache = (asgs, elb, elbv2) => { return { autoscaling: { describeAutoScalingGroups: { @@ -326,7 +395,25 @@ const createErrorCache = () => { describeAutoScalingGroups: { 'us-east-1': { err: { - message: 'error describing autoscaling groups' + message: 'error describing AutoScaling groups' + }, + }, + }, + }, + elb: { + describeLoadBalancers: { + 'us-east-1': { + err: { + message: 'error describing classic load balancers' + }, + }, + }, + }, + elbv2: { + describeLoadBalancers: { + 'us-east-1': { + err: { + message: 'error describing application/network load balancers' }, }, }, @@ -350,6 +437,16 @@ const createNullCache = () => { 'us-east-1': null, }, }, + elb: { + describeLoadBalancers: { + 'us-east-1': null, + }, + }, + elbv2: { + describeLoadBalancers: { + 'us-east-1': null, + }, + }, }; }; @@ -390,7 +487,16 @@ describe('sameAzElb', function () { done(); }); }); - + + it('should FAIL if autoscaling group utilizes an inactive load balancer', function (done) { + const cache = createCache([autoScalingGroups[1]], null, null); + sameAzElb.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + done(); + }); + }); + it('should UNKNOWN if unable to describe autoscaling group found', function (done) { const cache = createErrorCache(); sameAzElb.run(cache, {}, (err, results) => { From 7ad105ccaff31c23149ac69f8f6487225d82f5b4 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Sun, 30 Aug 2020 05:09:51 +0500 Subject: [PATCH 68/71] Updated sameAzElb plugin --- plugins/aws/autoscaling/sameAzElb.js | 29 ++++++++++++++++------- plugins/aws/autoscaling/sameAzElb.spec.js | 2 -- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index 0eab8f6d77..6975cd043d 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -4,6 +4,7 @@ var helpers = require('../../../helpers/aws'); module.exports = { title: 'AutoScaling Group Missing ELB', category: 'AutoScaling', +<<<<<<< HEAD description: 'Ensures all autoscaling groups are referencing active load balancers.', more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', @@ -11,6 +12,12 @@ module.exports = { <<<<<<< HEAD apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers'], ======= +======= + description: 'Ensures all autoscaling groups with attached ELBs are operating in the same availability zone.', + more_info: 'To work properly and prevent orphaned instances, ELBs must be created in the same availability zones as the backend instances in the autoscaling group.', + link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-add-availability-zone.html', + recommended_action: 'Update the ELB to use the same availability zones as the autoscaling group.', +>>>>>>> 89a3f78... Updated sameAzElb plugin apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers', 'ELBv2:describeLoadBalancers'], >>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call @@ -104,31 +111,26 @@ module.exports = { autoScalingGroups.data.forEach(function(asg) { var asgAvailabilityZones = asg.AvailabilityZones; - var differentAzFound = false; + var distinctAzs = []; var resource = asg.AutoScalingGroupARN; if(asg.HealthCheckType == 'ELB') { - if(!loadBalancers.length) { - helpers.addResult(results, 0, 'No Load Balancer found', region); - return rcb(); - } - if (asg.LoadBalancerNames && asg.LoadBalancerNames.length) { asg.LoadBalancerNames.forEach(function(elbName) { if(elbName in loadBalancers) { var loadBalancer = loadBalancers[elbName]; - differentAzFound = false; var elbAvailabilityZones = loadBalancer.AvailabilityZones; // if Load Balancer is in any AZ different from AutoScaling group's AZs then add an error result elbAvailabilityZones.forEach(function(elbAz) { >>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call if(!asgAvailabilityZones.includes(elbAz)) { - differentAzFound = true; + distinctAzs.push(elbAz); } }); +<<<<<<< HEAD if(differentAzFound) { <<<<<<< HEAD helpers.addResult(results, 2, 'Load balancer "' + elbName + '" is not in the same AZ as of AutoScaling group', region, asgName); @@ -141,6 +143,17 @@ module.exports = { else { helpers.addResult(results, 0, 'Load balancer "' + elbName + '" is in the same AZ as of AutoScaling group', region, resource); >>>>>>> 9c56541... Feature/43: Updated describeLoadBalancers api call +======= + if(distinctAzs.length) { + helpers.addResult(results, 2, + 'Auto scaling group "' + asg.AutoScalingGroupName + '" has these load balancers in different AZs: ' + distinctAzs.join(', '), + region, resource); + } + else { + helpers.addResult(results, 0, + 'Auto scaling group "' + asg.AutoScalingGroupName + '" has all load balancers in same AZ', + region, resource); +>>>>>>> 89a3f78... Updated sameAzElb plugin } } else { diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js index a9cd5c7726..227801a1f9 100644 --- a/plugins/aws/autoscaling/sameAzElb.spec.js +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -368,8 +368,6 @@ const loadBalancers = [ } ]; -] - const createCache = (asgs, elb, elbv2) => { return { autoscaling: { From 02564d7c78e1cebbaaa866a0cfa7c1f03841d10b Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Sun, 30 Aug 2020 05:48:16 +0500 Subject: [PATCH 69/71] Resolved merge conflicts --- exports.js | 1 - plugins/aws/autoscaling/sameAzElb.js | 78 +++++++++++++---------- plugins/aws/autoscaling/sameAzElb.spec.js | 42 ++++++------ 3 files changed, 67 insertions(+), 54 deletions(-) diff --git a/exports.js b/exports.js index ee2d8823a2..a6c2b3909d 100644 --- a/exports.js +++ b/exports.js @@ -5,7 +5,6 @@ module.exports = { 'acmValidation' : require(__dirname + '/plugins/aws/acm/acmValidation.js'), 'acmCertificateExpiry' : require(__dirname + '/plugins/aws/acm/acmCertificateExpiry.js'), 'asgMultiAz' : require(__dirname + '/plugins/aws/autoscaling/asgMultiAz.js'), - 'sameAzElb' : require(__dirname + '/plugins/aws/autoscaling/sameAzElb.js'), 'emptyASG' : require(__dirname + '/plugins/aws/autoscaling/emptyASG.js'), 'sameAzElb' : require(__dirname + '/plugins/aws/autoscaling/sameAzElb.js'), 'workgroupEncrypted' : require(__dirname + '/plugins/aws/athena/workgroupEncrypted.js'), diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index ed17b187e6..ce6942eb6d 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -2,13 +2,13 @@ var async = require('async'); var helpers = require('../../../helpers/aws'); module.exports = { - title: 'AutoScaling Group Missing ELB', + title: 'Same Availability Zone in ASG and ELB', category: 'AutoScaling', - description: 'Ensures all autoscaling groups are referencing active load balancers.', - more_info: 'Each autoscaling group should with a load balancer configured should reference an active ELB.', - link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/attach-load-balancer-asg.html', - recommended_action: 'Ensure that the autoscaling group load balancer has not been deleted. If so, remove it from the ASG.', - apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers'], + description: 'Ensures all autoscaling groups with attached ELBs are operating in the same availability zone.', + more_info: 'To work properly and prevent orphaned instances, ELBs must be created in the same availability zones as the backend instances in the autoscaling group.', + link: 'https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-add-availability-zone.html', + recommended_action: 'Update the ELB to use the same availability zones as the autoscaling group.', + apis: ['AutoScaling:describeAutoScalingGroups', 'ELB:describeLoadBalancers', 'ELBv2:describeLoadBalancers'], run: function(cache, settings, callback) { var results = []; @@ -23,8 +23,10 @@ module.exports = { var elasticLoadBalancers = helpers.addSource(cache, source, ['elb', 'describeLoadBalancers', region]); - if (!autoScalingGroups || !elasticLoadBalancers) return rcb(); + var elasticLoadBalancersV2 = helpers.addSource(cache, source, + ['elbv2', 'describeLoadBalancers', region]); + if (!autoScalingGroups || !elasticLoadBalancers || !elasticLoadBalancersV2) return rcb(); if (autoScalingGroups.err || !autoScalingGroups.data) { helpers.addResult(results, 3, 'Unable to query for AutoScaling groups: ' + helpers.addError(autoScalingGroups), region); @@ -32,7 +34,12 @@ module.exports = { } if (elasticLoadBalancers.err || !elasticLoadBalancers.data) { - helpers.addResult(results, 3, 'Unable to query for load balancers: ' + helpers.addError(elasticLoadBalancers), region); + helpers.addResult(results, 3, 'Unable to query for Classic load balancers: ' + helpers.addError(elasticLoadBalancers), region); + return rcb(); + } + + if (elasticLoadBalancersV2.err || !elasticLoadBalancersV2.data) { + helpers.addResult(results, 3, 'Unable to query for Application/Network load balancers: ' + helpers.addError(elasticLoadBalancersV2), region); return rcb(); } @@ -49,51 +56,56 @@ module.exports = { }); } - autoScalingGroups.data.forEach(function(asg){ + if (elasticLoadBalancersV2.data.length) { + elasticLoadBalancersV2.data.forEach(function(elbv2) { + if(elbv2.LoadBalancerName) { + loadBalancers[elbv2.LoadBalancerName] = elbv2; + } + }); + } + + autoScalingGroups.data.forEach(function(asg) { var asgAvailabilityZones = asg.AvailabilityZones; - var asgName = asg.AutoScalingGroupName; - var differentAzFound = false; + var distinctAzs = []; + var resource = asg.AutoScalingGroupARN; if(asg.HealthCheckType == 'ELB') { if (asg.LoadBalancerNames && asg.LoadBalancerNames.length) { - if (!elasticLoadBalancers.data.length) { - helpers.addResult(results, 2, 'No load balancers found', region); - return rcb(); - } - - elasticLoadBalancers.data.forEach(function(elb) { - var elbName = elb.LoadBalancerName; + asg.LoadBalancerNames.forEach(function(elbName) { + if(elbName in loadBalancers) { + var loadBalancer = loadBalancers[elbName]; + var elbAvailabilityZones = loadBalancer.AvailabilityZones; - if(asg.LoadBalancerNames.includes(elb.LoadBalancerName)){ - differentAzFound = false; - var elbAvailabilityZones = elb.AvailabilityZones; - - //if Load Balancer is in any AZ different from AutoScaling group's AZs then mark ELB as different - elbAvailabilityZones.forEach(function(elbAz){ + elbAvailabilityZones.forEach(function(elbAz) { if(!asgAvailabilityZones.includes(elbAz)) { - differentAzFound = true; + distinctAzs.push(elbAz); } }); - if(differentAzFound) { - helpers.addResult(results, 2, 'Load balancer "' + elbName + '" is not in the same AZ as of AutoScaling group', region, asgName); + if(distinctAzs.length) { + helpers.addResult(results, 2, + 'Auto scaling group "' + asg.AutoScalingGroupName + '" has load balancers in these different availability zones: ' + distinctAzs.join(', '), + region, resource); } else { - helpers.addResult(results, 0, 'Load balancer "' + elbName + '" is in the same AZ as of AutoScaling group', region, asgName); + helpers.addResult(results, 0, + 'Auto scaling group "' + asg.AutoScalingGroupName + '" has all load balancers in same availability zones', + region, resource); } - } - else { - helpers.addResult(results, 2, 'AutoScaling group utilizes an inactive load balancer "'+ elbName + '"', region, resource); + } else { + helpers.addResult(results, 2, + 'AutoScaling group "' + asg.AutoScalingGroupName + '" utilizes inactive load balancers', + region, resource); } }); } else { - helpers.addResult(results, 2, 'AutoScaling group does not have any Load Balancer associated', region, asgName); + helpers.addResult(results, 0, 'AutoScaling group does not have any Load Balancer associated', region, resource); } } else { - helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, asgName); + helpers.addResult(results, 0, 'AutoScaling group does not utilize a load balancer', region, resource); } }); diff --git a/plugins/aws/autoscaling/sameAzElb.spec.js b/plugins/aws/autoscaling/sameAzElb.spec.js index 227801a1f9..cf6f50d3d3 100644 --- a/plugins/aws/autoscaling/sameAzElb.spec.js +++ b/plugins/aws/autoscaling/sameAzElb.spec.js @@ -68,7 +68,7 @@ const autoScalingGroups = [ "us-east-1a", "us-west-1a" ], - "LoadBalancerNames": ["my-load-balancer2"], + "LoadBalancerNames": ["my-load-balancer3"], "TargetGroupARNs": [], "HealthCheckType": "ELB", "HealthCheckGracePeriod": 300, @@ -196,6 +196,11 @@ const autoScalingGroups = [ const loadBalancers = [ { "AvailabilityZones": [ + "us-east-1f", + "us-east-1e", + "us-east-1d", + "us-east-1c", + "us-east-1b", "us-east-1a" ], "BackendServerDescriptions": [ @@ -335,7 +340,7 @@ const loadBalancers = [ ] } ], - "LoadBalancerName": "my-load-balancer", + "LoadBalancerName": "my-load-balancer2", "Policies": { "AppCookieStickinessPolicies": [ ], @@ -384,6 +389,13 @@ const createCache = (asgs, elb, elbv2) => { }, }, }, + elbv2: { + describeLoadBalancers: { + 'us-east-1': { + data: elbv2 + } + } + } }; }; @@ -415,16 +427,7 @@ const createErrorCache = () => { }, }, }, - }, - elb: { - describeLoadBalancers: { - 'us-east-1': { - err: { - message: 'error describing load balancers' - }, - }, - }, - }, + } }; }; @@ -451,7 +454,7 @@ const createNullCache = () => { describe('sameAzElb', function () { describe('run', function () { it('should PASS if load balancer is in the same Availability Zone as of AutoScaling group', function (done) { - const cache = createCache([autoScalingGroups[0]], [loadBalancers[0]]); + const cache = createCache([autoScalingGroups[0]], [loadBalancers[0]], []); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -460,7 +463,7 @@ describe('sameAzElb', function () { }); it('should PASS if AutoScaling does not utilizes load balancer as HealthCheckType', function (done) { - const cache = createCache([autoScalingGroups[2]], [loadBalancers[1]]); + const cache = createCache([autoScalingGroups[2]], [], []); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(0); @@ -469,7 +472,7 @@ describe('sameAzElb', function () { }); it('should FAIL if load balancer is not in the same Availability Zone as of AutoScaling group', function (done) { - const cache = createCache([autoScalingGroups[1]], [loadBalancers[1]]); + const cache = createCache([autoScalingGroups[1]], [loadBalancers[1]],[]); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); @@ -478,7 +481,7 @@ describe('sameAzElb', function () { }); it('should FAIL if autoscaling group utilizes an inactive load balancer', function (done) { - const cache = createCache([autoScalingGroups[1]], []); + const cache = createCache([autoScalingGroups[1]], [], []); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); expect(results[0].status).to.equal(2); @@ -486,7 +489,7 @@ describe('sameAzElb', function () { }); }); - it('should FAIL if autoscaling group utilizes an inactive load balancer', function (done) { + it('should UNKOWN if unable to query for load balancers', function (done) { const cache = createCache([autoScalingGroups[1]], null, null); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); @@ -494,8 +497,8 @@ describe('sameAzElb', function () { done(); }); }); - - it('should UNKNOWN if unable to describe autoscaling group found', function (done) { + + it('should UNKNOWN if unable to describe autoscaling groups', function (done) { const cache = createErrorCache(); sameAzElb.run(cache, {}, (err, results) => { expect(results.length).to.equal(1); @@ -511,6 +514,5 @@ describe('sameAzElb', function () { done(); }); }); - }); }); \ No newline at end of file From fab662a965fbb14082b3b3805a69b7c838d99bcc Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Sun, 6 Sep 2020 18:32:06 +0500 Subject: [PATCH 70/71] feature/43: accommodated PR changes --- plugins/aws/autoscaling/sameAzElb.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index ce6942eb6d..1d1c50a7a1 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -73,15 +73,17 @@ module.exports = { if (asg.LoadBalancerNames && asg.LoadBalancerNames.length) { asg.LoadBalancerNames.forEach(function(elbName) { - if(elbName in loadBalancers) { + if(loadBalancers[elbName]) { var loadBalancer = loadBalancers[elbName]; var elbAvailabilityZones = loadBalancer.AvailabilityZones; - elbAvailabilityZones.forEach(function(elbAz) { - if(!asgAvailabilityZones.includes(elbAz)) { - distinctAzs.push(elbAz); - } - }); + if (elbAvailabilityZones && elbAvailabilityZones.length) { + elbAvailabilityZones.forEach(function(elbAz) { + if(asgAvailabilityZones && asgAvailabilityZones.length && !asgAvailabilityZones.includes(elbAz)) { + distinctAzs.push(elbAz); + } + }); + } if(distinctAzs.length) { helpers.addResult(results, 2, From ccae677f8270933dd27ca4e38fd12a6e8b0b2e5a Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Mon, 14 Sep 2020 02:19:50 +0500 Subject: [PATCH 71/71] feature/43: Updated plugin title --- plugins/aws/autoscaling/sameAzElb.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/autoscaling/sameAzElb.js b/plugins/aws/autoscaling/sameAzElb.js index 1d1c50a7a1..58c6f93754 100644 --- a/plugins/aws/autoscaling/sameAzElb.js +++ b/plugins/aws/autoscaling/sameAzElb.js @@ -2,7 +2,7 @@ var async = require('async'); var helpers = require('../../../helpers/aws'); module.exports = { - title: 'Same Availability Zone in ASG and ELB', + title: 'AutoScaling ELB Same Availability Zone', category: 'AutoScaling', description: 'Ensures all autoscaling groups with attached ELBs are operating in the same availability zone.', more_info: 'To work properly and prevent orphaned instances, ELBs must be created in the same availability zones as the backend instances in the autoscaling group.',