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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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 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 18/68] 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 19/68] 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 20/68] 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 21/68] 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 22/68] 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 23/68] 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 24/68] 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 25/68] 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 26/68] 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 27/68] 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 28/68] 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 29/68] 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 30/68] 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 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 31/68] 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 32/68] 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 33/68] 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 34/68] 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 35/68] 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 36/68] 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 37/68] 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 38/68] 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 39/68] 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 40/68] 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 41/68] 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 42/68] 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 43/68] 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 44/68] 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 45/68] 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 46/68] 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 47/68] 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 48/68] 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 49/68] 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 50/68] 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 51/68] 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 52/68] 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 53/68] 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 54/68] 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 55/68] 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 56/68] 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 57/68] 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 58/68] 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 156d027c478cec36931628878e885b85dc0891a0 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 59/68] 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 4c73161cd1bdb1c5a5ab38709d21443797a87a36 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Tue, 26 Jan 2021 13:20:58 +0500 Subject: [PATCH 60/68] Added superlinter --- .github/workflows/superlinter.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/superlinter.yml diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml new file mode 100644 index 0000000000..a54f356670 --- /dev/null +++ b/.github/workflows/superlinter.yml @@ -0,0 +1,25 @@ +name: Super-Linter + +# Run this workflow every time a new commit pushed to your repository +on: push + +jobs: + # Set the job key. The key is displayed as the job name + # when a job name is not provided + super-lint: + # Name the Job + name: Lint code base + # Set the type of machine to run on + runs-on: ubuntu-latest + + steps: + # Checks out a copy of your repository on the ubuntu-latest machine + - name: Checkout code + uses: actions/checkout@v2 + + # Runs the Super-Linter action + - name: Run Super-Linter + uses: github/super-linter@v3 + env: + DEFAULT_BRANCH: master + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 6cda1ef5a2319c8f52ce08023af97f9018d493d7 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 27 Jan 2021 14:19:02 +0500 Subject: [PATCH 61/68] Added scans ci --- .github/workflows/scan_ci.yml | 23 +++++++++++++++++++++++ .github/workflows/superlinter.yml | 25 ------------------------- 2 files changed, 23 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/scan_ci.yml delete mode 100644 .github/workflows/superlinter.yml diff --git a/.github/workflows/scan_ci.yml b/.github/workflows/scan_ci.yml new file mode 100644 index 0000000000..be35f61457 --- /dev/null +++ b/.github/workflows/scan_ci.yml @@ -0,0 +1,23 @@ +name: +on: [push, pull_request, create, delete] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: 'stable' + - run: npm install + + - name: NPM test + - run: npm test + + - name: Lint + - run: npm run lint + + - name: Spell-check + - run: npm run spellcheck \ No newline at end of file diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml deleted file mode 100644 index a54f356670..0000000000 --- a/.github/workflows/superlinter.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Super-Linter - -# Run this workflow every time a new commit pushed to your repository -on: push - -jobs: - # Set the job key. The key is displayed as the job name - # when a job name is not provided - super-lint: - # Name the Job - name: Lint code base - # Set the type of machine to run on - runs-on: ubuntu-latest - - steps: - # Checks out a copy of your repository on the ubuntu-latest machine - - name: Checkout code - uses: actions/checkout@v2 - - # Runs the Super-Linter action - - name: Run Super-Linter - uses: github/super-linter@v3 - env: - DEFAULT_BRANCH: master - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 072a98a7887b3b74c41a16b2e1616f409448db31 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 27 Jan 2021 14:21:38 +0500 Subject: [PATCH 62/68] Updated Ci file --- .github/workflows/scan_ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/scan_ci.yml b/.github/workflows/scan_ci.yml index be35f61457..c302c43ad4 100644 --- a/.github/workflows/scan_ci.yml +++ b/.github/workflows/scan_ci.yml @@ -12,12 +12,6 @@ jobs: with: node-version: 'stable' - run: npm install - - - name: NPM test - run: npm test - - - name: Lint - run: npm run lint - - - name: Spell-check - run: npm run spellcheck \ No newline at end of file From cb40648178a99af6b32e7bec078a15433b3ce8a6 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 27 Jan 2021 14:23:13 +0500 Subject: [PATCH 63/68] Updated Node version in CI file --- .github/workflows/scan_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scan_ci.yml b/.github/workflows/scan_ci.yml index c302c43ad4..87cc5ce349 100644 --- a/.github/workflows/scan_ci.yml +++ b/.github/workflows/scan_ci.yml @@ -10,7 +10,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v1 with: - node-version: 'stable' + node-version: '14.x' - run: npm install - run: npm test - run: npm run lint From b0138cbe60c4acd887ab9eb2aa6bf28a5b1f8ae8 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 27 Jan 2021 14:27:40 +0500 Subject: [PATCH 64/68] removed spech check command --- .github/workflows/scan_ci.yml | 11 +-- package-lock.json | 122 ++++++++++++++++++++++++---------- package.json | 6 +- 3 files changed, 98 insertions(+), 41 deletions(-) diff --git a/.github/workflows/scan_ci.yml b/.github/workflows/scan_ci.yml index 87cc5ce349..201d2ce74c 100644 --- a/.github/workflows/scan_ci.yml +++ b/.github/workflows/scan_ci.yml @@ -1,5 +1,5 @@ name: -on: [push, pull_request, create, delete] +on: [push, pull_request, create, delete, issue_comment, repository_dispatch] jobs: build: @@ -12,6 +12,9 @@ jobs: with: node-version: '14.x' - run: npm install - - run: npm test - - run: npm run lint - - run: npm run spellcheck \ No newline at end of file + + - name: NPM Test + run: npm test + + - name: Lint + run: npm run lint \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4c5ad9591f..f1188339ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -144,11 +144,22 @@ } }, "@octokit/auth-token": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", - "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", + "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", "requires": { - "@octokit/types": "^2.0.0" + "@octokit/types": "^6.0.3" + }, + "dependencies": { + "@octokit/types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.6.0.tgz", + "integrity": "sha512-nmFoU3HCbw1AmnZU/eto2VvzV06+N7oAqXwMmAHGlNDF+KFykksh/VlAl85xc1P5T7Mw8fKYvXNaImNHCCH/rg==", + "requires": { + "@octokit/openapi-types": "^3.3.0", + "@types/node": ">= 8" + } + } } }, "@octokit/endpoint": { @@ -161,6 +172,11 @@ "universal-user-agent": "^5.0.0" } }, + "@octokit/openapi-types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-3.3.0.tgz", + "integrity": "sha512-s3dd32gagPmKaSLNJ9aPNok7U+tl69YLESf6DgQz5Ml/iipPZtif3GLvWpNXoA6qspFm1LFUZX+C3SqWX/Y/TQ==" + }, "@octokit/plugin-paginate-rest": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", @@ -170,9 +186,9 @@ } }, "@octokit/plugin-request-log": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", - "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz", + "integrity": "sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==" }, "@octokit/plugin-rest-endpoint-methods": { "version": "2.4.0", @@ -242,9 +258,9 @@ } }, "@octokit/rest": { - "version": "16.43.1", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz", - "integrity": "sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==", + "version": "16.43.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.2.tgz", + "integrity": "sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ==", "requires": { "@octokit/auth-token": "^2.4.0", "@octokit/plugin-paginate-rest": "^1.1.1", @@ -264,38 +280,52 @@ "universal-user-agent": "^4.0.0" }, "dependencies": { + "@octokit/endpoint": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz", + "integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + } + } + }, "@octokit/request": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.4.tgz", - "integrity": "sha512-qyj8G8BxQyXjt9Xu6NvfvOr1E0l35lsXtwm3SopsYg/JWXjlsnwqLc8rsD2OLguEL/JjLfBvrXr4az7z8Lch2A==", + "version": "5.4.13", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.13.tgz", + "integrity": "sha512-WcNRH5XPPtg7i1g9Da5U9dvZ6YbTffw9BN2rVezYiE7couoSyaRsw0e+Tl8uk1fArHE7Dn14U7YqUDy59WaqEw==", "requires": { - "@octokit/endpoint": "^6.0.0", + "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.0.0", - "@octokit/types": "^2.0.0", + "@octokit/types": "^6.0.3", "deprecation": "^2.0.0", - "is-plain-object": "^3.0.0", - "node-fetch": "^2.3.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", "once": "^1.4.0", - "universal-user-agent": "^5.0.0" + "universal-user-agent": "^6.0.0" }, "dependencies": { "@octokit/request-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz", - "integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz", + "integrity": "sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==", "requires": { - "@octokit/types": "^2.0.0", + "@octokit/types": "^6.0.3", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "universal-user-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz", - "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==", - "requires": { - "os-name": "^3.1.0" - } + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" } } }, @@ -307,8 +337,32 @@ "@octokit/types": "^2.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.6.0.tgz", + "integrity": "sha512-nmFoU3HCbw1AmnZU/eto2VvzV06+N7oAqXwMmAHGlNDF+KFykksh/VlAl85xc1P5T7Mw8fKYvXNaImNHCCH/rg==", + "requires": { + "@octokit/openapi-types": "^3.3.0", + "@types/node": ">= 8" } }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, "universal-user-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz", @@ -489,9 +543,9 @@ "dev": true }, "argparse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.0.tgz", - "integrity": "sha512-mEKF1/WpTsblaqx7NIkcsTxwDzvJuGH5sdUqDNcJS+vXCWe+yM/o4cs/Q2/GFESAYg+O7UouEmz+iBqmKofI/Q==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "arr-diff": { "version": "4.0.0", @@ -593,9 +647,9 @@ "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" }, "aws-sdk": { - "version": "2.740.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.740.0.tgz", - "integrity": "sha512-cSedIe7g5/S5o23jHvm9+vWhcYysmuKrmbML1Z0pO9KxlqOA9s4Z5f6Il7ZmvAktfmrxu1SyQu4YEoP5DL4/zw==", + "version": "2.831.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.831.0.tgz", + "integrity": "sha512-lrOjbGFpjk2xpESyUx2PGsTZgptCy5xycZazPeakNbFO19cOoxjHx3xyxOHsMCYb3pQwns35UvChQT60B4u6cw==", "requires": { "buffer": "4.9.2", "events": "1.1.1", diff --git a/package.json b/package.json index e206ef6a32..84933aab13 100644 --- a/package.json +++ b/package.json @@ -41,10 +41,10 @@ "dependencies": { "@octokit/app": "^3.0.0", "@octokit/request": "^3.0.3", - "@octokit/rest": "^16.3.2", - "argparse": "^2.0.0", + "@octokit/rest": "^16.43.2", + "argparse": "^2.0.1", "async": "^2.6.1", - "aws-sdk": "^2.740.0", + "aws-sdk": "^2.831.0", "azure-storage": "^2.10.3", "csv-write-stream": "^2.0.0", "fast-safe-stringify": "^2.0.6", From 239b8a635edd28126f446c9ef107760909f0d816 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Wed, 27 Jan 2021 17:07:26 +0500 Subject: [PATCH 65/68] Delete scan_ci.yml --- .github/workflows/scan_ci.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .github/workflows/scan_ci.yml diff --git a/.github/workflows/scan_ci.yml b/.github/workflows/scan_ci.yml deleted file mode 100644 index 201d2ce74c..0000000000 --- a/.github/workflows/scan_ci.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: -on: [push, pull_request, create, delete, issue_comment, repository_dispatch] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js - uses: actions/setup-node@v1 - with: - node-version: '14.x' - - run: npm install - - - name: NPM Test - run: npm test - - - name: Lint - run: npm run lint \ No newline at end of file From 46cfe9df6ad915fcea0dd06c061b874193613767 Mon Sep 17 00:00:00 2001 From: AkhtarAmir Date: Mon, 1 Feb 2021 23:23:55 +0500 Subject: [PATCH 66/68] Added spellcheck --- .github/workflows/scans_ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/scans_ci.yml b/.github/workflows/scans_ci.yml index 7fd69312b9..2e1ac859a7 100644 --- a/.github/workflows/scans_ci.yml +++ b/.github/workflows/scans_ci.yml @@ -11,6 +11,11 @@ jobs: uses: actions/setup-node@v1 with: node-version: '12.x' + - uses: codespell-project/actions-codespell@master + with: + check_filenames: true + skip: ./.github/*,.git,./package.json,./node_modules,./tests,./config,*.png,Dockerfile,./scripts,*.spec.js,./plugins/azure/storageaccounts/storageAccountsAADEnabled.js,./plugins/aws/cloudtrail/cloudtrailBucketAccessLogging.js,./helpers/google/index.js,*zip + ignore_words_list: iam,\"tRe\",AKS,aks,optin,callInt,callInt - run: npm install - name: NPM Test @@ -19,4 +24,5 @@ jobs: - name: Lint run: npm run lint + \ No newline at end of file From a3f0063ad84cf35337351d4f20cc2cbe5a2dadb6 Mon Sep 17 00:00:00 2001 From: AkhtarAmir <31914988+AkhtarAmir@users.noreply.github.com> Date: Thu, 4 Mar 2021 22:41:22 +0500 Subject: [PATCH 67/68] Fixed failing test in IAM Access Keys Rotated plugin (#562) --- plugins/aws/iam/accessKeysRotated.spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/aws/iam/accessKeysRotated.spec.js b/plugins/aws/iam/accessKeysRotated.spec.js index b46865cd36..cfcb22d6e3 100644 --- a/plugins/aws/iam/accessKeysRotated.spec.js +++ b/plugins/aws/iam/accessKeysRotated.spec.js @@ -148,8 +148,8 @@ describe('accessKeysRotated', function () { it('should PASS if the user access key was last rotated within the pass limit', function (done) { const cache = createCache([generateCredentialReport[0], generateCredentialReport[2]]); var settings = { - access_keys_last_rotated_fail: 180, - access_keys_last_rotated_warn: 90 + access_keys_rotated_fail: 180, + access_keys_rotated_warn: 90 }; accessKeysRotated.run(cache, settings, (err, results) => { expect(results.length).to.equal(2); @@ -162,8 +162,8 @@ describe('accessKeysRotated', function () { it('should WARN if the user access key was last rotated within the warn limit', function (done) { const cache = createCache([generateCredentialReport[0], generateCredentialReport[1]]); var settings = { - access_keys_last_rotated_fail: 180, - access_keys_last_rotated_warn: 100 + access_keys_rotated_fail: 180, + access_keys_rotated_warn: 80 }; accessKeysRotated.run(cache, settings, (err, results) => { expect(results.length).to.equal(2); @@ -176,8 +176,8 @@ describe('accessKeysRotated', function () { it('should FAIL if the user access key was last rotated more than the fail limit', function (done) { const cache = createCache([generateCredentialReport[0],generateCredentialReport[0],generateCredentialReport[3]]); var settings = { - access_keys_last_rotated_fail: 180, - access_keys_last_rotated_warn: 90 + access_keys_rotated_fail: 180, + access_keys_rotated_warn: 90 }; accessKeysRotated.run(cache, settings, (err, results) => { expect(results.length).to.equal(2); From 43b32ab9abe4b8d2fcae5034625c34f451155067 Mon Sep 17 00:00:00 2001 From: Gio Rodriguez Date: Thu, 4 Mar 2021 15:45:51 -0800 Subject: [PATCH 68/68] Repo sync 3 4 (#571) * syncing master to open-source * adding fixes Co-authored-by: Gio Rodriguez --- collectors/azure/collector.js | 11 +- collectors/oracle/collector.js | 10 +- engine.js | 10 +- helpers/asl.js | 282 ++++++++++++++++++ helpers/aws/regions.js | 4 +- helpers/aws/resources.js | 16 + helpers/azure/auth.js | 2 +- helpers/gsl.js | 188 ------------ helpers/oracle/regions.js | 8 +- index.js | 7 +- .../aws/cloudformation/plainTextParameters.js | 2 +- .../cloudfront/cloudfrontLoggingEnabled.js | 24 +- plugins/aws/ec2/vpcEndpointAcceptance.js | 6 +- plugins/aws/ec2/vpcEndpointAcceptance.spec.js | 26 +- plugins/aws/iam/accessKeysExtra.js | 18 +- plugins/aws/iam/accessKeysLastUsed.js | 12 + plugins/aws/iam/accessKeysRotated.js | 12 + plugins/aws/iam/accessKeysRotated.spec.js | 2 +- plugins/aws/iam/canaryKeysUsed.js | 15 +- plugins/aws/iam/certificateExpiry.js | 12 + plugins/aws/iam/emptyGroups.js | 12 + plugins/aws/iam/iamRoleLastUsed.js | 12 + plugins/aws/iam/iamUserNameRegex.js | 12 + plugins/aws/iam/maxPasswordAge.js | 13 +- plugins/aws/iam/minPasswordLength.js | 12 + plugins/aws/iam/passwordExpiration.js | 12 + plugins/aws/iam/usersMfaEnabled.js | 12 + plugins/aws/kms/kmsScheduledDeletion.js | 12 + plugins/aws/s3/bucketLogging.js | 13 +- plugins/aws/s3/bucketVersioning.js | 29 ++ plugins/aws/sns/topicEncrypted.js | 12 + .../cdnprofiles/detectInsecureCustomOrigin.js | 2 +- plugins/google/storage/bucketLogging.js | 12 +- plugins/oracle/audit/logRetentionPeriod.js | 59 ++-- .../blockstorage/blockPolicyProtection.js | 155 +++++----- .../compute/instancePolicyProtection.js | 147 +++++---- plugins/oracle/database/dbPolicyProtection.js | 146 +++++---- .../oracle/filestorage/nfsPolicyProtection.js | 152 +++++----- plugins/oracle/identity/excessivePolicies.js | 55 ++-- .../identity/excessivePolicyStatements.js | 62 ++-- plugins/oracle/identity/minPasswordLength.js | 12 +- .../identity/passwordRequiresLowercase.js | 13 +- .../identity/passwordRequiresNumbers.js | 13 +- .../identity/passwordRequiresSymbols.js | 13 +- .../identity/passwordRequiresUppercase.js | 13 +- .../oracle/identity/policyLeastPrivilege.js | 150 +++++----- plugins/oracle/identity/usersMfaEnabled.js | 17 +- .../objectstore/objectPolicyProtection.js | 149 +++++---- 48 files changed, 1111 insertions(+), 877 deletions(-) create mode 100644 helpers/asl.js delete mode 100644 helpers/gsl.js diff --git a/collectors/azure/collector.js b/collectors/azure/collector.js index 3da6ce0db1..cefaa4e925 100644 --- a/collectors/azure/collector.js +++ b/collectors/azure/collector.js @@ -109,6 +109,7 @@ var calls = { securityContacts: { list: { url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Security/securityContacts?api-version=2017-08-01-preview', + ignoreLocation: true } }, subscriptions: { @@ -444,10 +445,10 @@ var collect = function(AzureConfig, settings, callback) { var regionsToLoop = parseCollection(subCallObj.reliesOnPath, collection); if (regionsToLoop && Object.keys(regionsToLoop).length) { // Loop through regions - async.eachOf(regionsToLoop, function(regionObj, region, regionCb) { + async.eachOfLimit(regionsToLoop, 5, function(regionObj, region, regionCb) { if (regionObj && regionObj.data && regionObj.data.length) { if (!collectionObj[region]) collectionObj[region] = {}; - async.each(regionObj.data, function(regionData, regionDataCb) { + async.eachLimit(regionObj.data, 10, function(regionData, regionDataCb) { var localReq = { url: subCallObj.url, post: subCallObj.post, @@ -523,7 +524,7 @@ var collect = function(AzureConfig, settings, callback) { async.eachOf(regionsToLoop, function(regionObj, region, regionCb) { if (!collectionObj[region]) collectionObj[region] = {}; // Loop through the resources - async.eachOf(regionObj, function(resourceObj, resourceId, resourceCb){ + async.eachOfLimit(regionObj, 5, function(resourceObj, resourceId, resourceCb){ function processResource(resourceData, resourceDataCb) { var localReq = { url: subCallObj.url, @@ -561,14 +562,14 @@ var collect = function(AzureConfig, settings, callback) { } if (Array.isArray(resourceObj)) { - async.each(resourceObj, function(resourceData, resourceDataCb) { + async.eachLimit(resourceObj, 10, function(resourceData, resourceDataCb) { processResource(resourceData, resourceDataCb); }, function(){ resourceCb(); }); } else { if (resourceObj && resourceObj.data && resourceObj.data.length) { - async.each(resourceObj.data, function(resourceData, resourceDataCb) { + async.eachLimit(resourceObj.data, 10, function(resourceData, resourceDataCb) { processResource(resourceData, resourceDataCb); }, function() { resourceCb(); diff --git a/collectors/oracle/collector.js b/collectors/oracle/collector.js index 8996ba8270..bac378b73d 100644 --- a/collectors/oracle/collector.js +++ b/collectors/oracle/collector.js @@ -20,7 +20,7 @@ var async = require('async'); var helpers = require(__dirname + '/../../helpers/oracle'); -const regionSubscriptionService = {name: 'regionSubscription', call: 'list', region: helpers.regions(false).default}; +var regionSubscriptionService; var globalServices = [ 'core' @@ -396,6 +396,10 @@ var processCall = function(OracleConfig, collection, settings, regions, call, se if (!collection[service][callKey]) collection[service][callKey] = {}; async.eachLimit(regions[service], helpers.MAX_REGIONS_AT_A_TIME, function(region, regionCb) { + if (region === 'default') { + region = OracleConfig.region ? OracleConfig.region : 'us-ashburn-1'; + } + if (settings.skip_regions && settings.skip_regions.indexOf(region) > -1 && globalServices.indexOf(service) === -1) return regionCb(); @@ -458,7 +462,6 @@ var processCall = function(OracleConfig, collection, settings, regions, call, se var getRegionSubscription = function(OracleConfig, collection, settings, calls, service, callKey, region, serviceCb) { var LocalOracleConfig = JSON.parse(JSON.stringify(OracleConfig)); - LocalOracleConfig.region = region; LocalOracleConfig.service = service; if (!collection[service]) collection[service] = {}; @@ -482,9 +485,10 @@ var getRegionSubscription = function(OracleConfig, collection, settings, calls, // Loop through all of the top-level collectors for each service var collect = function(OracleConfig, settings, callback) { var collection = {}; - + OracleConfig.region = OracleConfig.region ? OracleConfig.region : 'us-ashburn-1'; OracleConfig.maxRetries = 5; OracleConfig.retryDelayOptions = {base: 300}; + regionSubscriptionService = {name: 'regionSubscription', call: 'list', region: OracleConfig.region}; var regions = helpers.regions(settings.govcloud); diff --git a/engine.js b/engine.js index 5eb363a98d..d5d6df7ddc 100644 --- a/engine.js +++ b/engine.js @@ -2,7 +2,7 @@ var async = require('async'); var exports = require('./exports.js'); var suppress = require('./postprocess/suppress.js'); var output = require('./postprocess/output.js'); -var gslRunner = require('./helpers/gsl.js'); +var aslRunner = require('./helpers/asl.js'); /** * The main function to execute CloudSploit scans. @@ -193,11 +193,11 @@ var engine = function(cloudConfig, settings) { setTimeout(function() { pluginDone(err, maximumStatus); }, 0); }; - if (plugin.gsl) { - console.log(`INFO: Using custom GSL for plugin: ${plugin.title}`); + if (plugin.asl) { + console.log(`INFO: Using custom ASL for plugin: ${plugin.title}`); // Inject APIs and resource maps - plugin.gsl.apis = plugin.apis; - gslRunner(collection, plugin.gsl, resourceMap, postRun); + plugin.asl.apis = plugin.apis; + aslRunner(collection, plugin.asl, resourceMap, postRun); } else { plugin.run(collection, settings, postRun); } diff --git a/helpers/asl.js b/helpers/asl.js new file mode 100644 index 0000000000..d6ca853b2b --- /dev/null +++ b/helpers/asl.js @@ -0,0 +1,282 @@ +var parse = function(obj, path) { + if (typeof path == 'string') path = path.split('.'); + if (Array.isArray(path) && path.length) { + var localPath = path.shift(); + if (obj[localPath] || typeof obj[localPath] === 'boolean') { + return parse(obj[localPath], path); + } else { + return 'not set'; + } + } else { + return obj; + } +}; + +var transform = function(val, transformation) { + if (transformation == 'DATE') { + return new Date(val); + } else if (transformation == 'INTEGER') { + return parseInt(val); + } else if (transformation == 'STRING') { + return val.toString(); + } else if (transformation == 'DAYSFROM') { + // Return the number of days between the date and now + var now = new Date(); + var then = new Date(val); + var timeDiff = then.getTime() - now.getTime(); + var diff = Math.round(timeDiff / (1000 * 3600 * 24)); + return diff; + } else if (transformation == 'COUNT') { + return val.length; + } else if (transformation == 'EACH') { + return val; + } else if (transformation == 'TOLOWERCASE') { + return val.toLowerCase(); + } else { + return val; + } +}; + +function evaluateConditions(obj, conditions){ + var preVal = {}; // place to hold the result evaluated till now + for (let i in conditions){ + let condition = conditions[i]; + let value = validate(obj,condition); + + if (!condition.logical){ + // no logical operation means + // first element of the conditions + if ( i == 0){ + // first element in the condition list,so set the preVal + preVal = value; + } + // error as no logical condition and not the first element + else{ + //err + console.error('No logical operator found and this is not the first element'); + } + } + else{ + if ( i == 0){ + // first element in the condition list,and with logical operation. Error case + console.error('logical operator found in first element'); + } + else{ + // this is not first element in array and also logical op is here. use the preVal and evaluate it + if(preVal){ + //as this is not first element in array preVal should be set. + //other wise the condition is not properly formed as AND OR are binary ops + if( condition.logical === 'OR'){ + preVal.status = preVal.status || value.status; + } + else if (condition.logical === 'AND'){ + preVal.status = preVal.status && value.status; + } + else{ + // unsupported operator. + console.error('wrong logical operator mentioned'); + } + preVal.message = preVal.message.concat(', ',value.message); + } + else{ + //condition is not properly formed as AND OR are binary ops + console.error('condition is malformed'); + } + } + } + } + + return preVal; +} + +var validate = function(obj, condition) { + var result = 0; + var message = []; + var override = false; + + // Extract the values for the conditions + + if (condition.property) { + var conditionResult = 0; + condition.parsed = parse(obj, condition.property); + + // Transform the property if required + if (condition.transform) { + condition.parsed = transform(condition.parsed, condition.transform); + } + + // Compare the property with the operator + if (condition.parsed === 'not set'){ + conditionResult = 2; + message.push(`${condition.property}: not set to any value`); + } + else if (condition.op) { + if (condition.op == 'EQ') { + if (condition.parsed == condition.value) { + message.push(`${condition.property}: ${condition.parsed} matched: ${condition.value}`); + } else { + conditionResult = 2; + message.push(`${condition.property}: ${condition.parsed} did not match: ${condition.value}`); + } + } else if (condition.op == 'GT') { + if (condition.parsed > condition.value) { + message.push(`${condition.property}: count of ${condition.parsed} was greater than: ${condition.value}`); + } else { + conditionResult = 2; + message.push(`${condition.property}: count of ${condition.parsed} was not greater than: ${condition.value}`); + } + } else if (condition.op == 'NE') { + if (condition.parsed !== condition.value) { + message.push(`${condition.property}: ${condition.parsed} is not: ${condition.value}`); + } else { + conditionResult = 2; + message.push(`${condition.property}: ${condition.parsed} is: ${condition.value}`); + } + } else if (condition.op == 'MATCHES') { + var userRegex = RegExp(condition.value); + if (userRegex.test(condition.parsed)) { + message.push(`${condition.property}: ${condition.parsed} matches the regex: ${condition.value}`); + } else { + conditionResult = 2; + message.push(`${condition.property}: ${condition.parsed} does not match the regex: ${condition.value}`); + } + } else if (condition.op == 'EXISTS') { + if (condition.parsed !== 'not set') { + message.push(`${condition.property}: set to ${condition.parsed}`); + } else { + conditionResult = 2; + message.push(`${condition.property}: ${condition.parsed}`); + } + } else if (condition.op == 'ISTRUE') { + if (condition.parsed) { + message.push(`${condition.property} is true`); + } else { + conditionResult = 2; + message.push(`${condition.property} is false`); + } + } else if (condition.op == 'ISFALSE') { + if (!condition.parsed) { + message.push(`${condition.property} is false`); + } else { + conditionResult = 2; + message.push(`${condition.property} is true`); + } + } else if (condition.op == 'CONTAINS') { + if (condition.parsed.includes(condition.value)) { + message.push(`${condition.property}: ${condition.value} found in ${condition.parsed}`); + } else { + conditionResult = 2; + message.push(`${condition.value} not found in ${condition.parsed}`); + } + } + } else if (condition.transform && condition.transform == 'EACH' && condition.conditions) { + // Recurse into the same function + var subProcessed = []; + condition.parsed.forEach(function(parsed) { + subProcessed.push(validate(parsed, condition.conditions)); + }); + subProcessed.forEach(function(sub) { + if (sub.status) conditionResult = sub.status; + if (sub.message) message.push(sub.message); + }); + } + if (condition.invert) conditionResult = (conditionResult ? 0 : 2); + + if (condition.override && !conditionResult) override = true; + if (conditionResult) result = conditionResult; + } + + + if (result && override) result = 0; + + if (!message.length) { + message = ['The resource matched all required conditions']; + } + + return { + status: result, + message: message.join(', ') + }; +}; + +var asl = function(source, input, resourceMap, callback) { + if (!source || !input) return callback('No source or input provided'); + if (!input.apis || !input.apis[0]) return callback('No APIs provided for input'); + if (!input.conditions || !input.conditions.length) return callback('No conditions provided for input'); + + // Split apis into service:api + // TODO: support conditions that use different APIs + var service = input.conditions[0].service; + var api = input.conditions[0].api; + var resourcePath; + if (resourceMap && + resourceMap[service] && + resourceMap[service][api]) { + resourcePath = resourceMap[service][api]; + } + + if (!source[service]) return callback(`Source data did not contain service: ${service}`); + if (!source[service][api]) return callback(`Source data did not contain API: ${api}`); + + var results = []; + + for (var region in source[service][api]) { + var regionVal = source[service][api][region]; + if (typeof regionVal !== 'object') continue; + if (regionVal.err) { + results.push({ + status: 3, + message: regionVal.err.message || 'Error', + region: region + }); + } else if (regionVal.data) { + // It's an array, loop + regionVal.data.forEach(function(regionData) { + var validated = evaluateConditions(regionData, input.conditions); + var parsedResource = parse(regionData, resourcePath); + if (typeof parsedResource !== 'string') parsedResource = null; + + results.push({ + status: validated.status, + resource: parsedResource, + message: validated.message, + region: region + }); + }); + } else { + for (var resourceName in regionVal) { + var resourceObj = regionVal[resourceName]; + if (resourceObj.err) { + results.push({ + status: 3, + resource: resourceName, + message: resourceObj.err.message || 'Error', + region: region + }); + } else if (!resourceObj.data) { + results.push({ + status: 3, + resource: resourceName, + message: 'No data returned', + region: region + }); + } else { + var validated = evaluateConditions(resourceObj.data, input.conditions); + var parsedResource = parse(resourceObj.data, resourcePath); + if (typeof parsedResource !== 'string') parsedResource = null; + + results.push({ + status: validated.status, + resource: parsedResource ? parsedResource : resourceName, + message: validated.message, + region: region + }); + } + } + } + } + + callback(null, results, source[service][api]); +}; + +module.exports = asl; \ No newline at end of file diff --git a/helpers/aws/regions.js b/helpers/aws/regions.js index 2e93ae9453..3ff6c650f8 100644 --- a/helpers/aws/regions.js +++ b/helpers/aws/regions.js @@ -75,9 +75,7 @@ module.exports = { vpc: regions, flowlogs: regions, rds: regions, - redshift: ['us-east-1', 'us-east-2', 'us-west-2', 'us-west-1', 'ca-central-1', - 'eu-central-1', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'eu-north-1', - 'ap-northeast-1','ap-northeast-2', 'ap-southeast-1','ap-southeast-2','ap-south-1','sa-east-1'], + redshift: regions, cloudwatch: regions, ecs: regions, resourcegroupstaggingapi: regions, diff --git a/helpers/aws/resources.js b/helpers/aws/resources.js index d09a28c771..6d5778c869 100644 --- a/helpers/aws/resources.js +++ b/helpers/aws/resources.js @@ -5,5 +5,21 @@ module.exports = { cloudfront: { listDistributions: 'ARN', getDistribution: 'Distribution.ARN' + }, + s3: { + listBuckets: 'name', + }, + sns:{ + listTopics: 'describeKey', + getTopicAttributes: 'Attributes.TopicArn' + }, + kms:{ + describeKey: 'KeyMetadata.Arn' + }, + iam:{ + generateCredentialReport: 'arn', + listServerCertificates: 'Arn', + getGroup: 'Arn', + getRole: 'Role.Arn' } }; \ No newline at end of file diff --git a/helpers/azure/auth.js b/helpers/azure/auth.js index 64c9686ad9..db4584f883 100644 --- a/helpers/azure/auth.js +++ b/helpers/azure/auth.js @@ -160,7 +160,7 @@ module.exports = { if (dv.location && dv.location.toLowerCase().replace(/ /g, '') == location.toLowerCase()) { return true; - } else if (location.toLowerCase() == 'global' && !dv.location) { + } else if (location.toLowerCase() == 'global' && (!dv.location || obj.ignoreLocation)) { return true; } return false; diff --git a/helpers/gsl.js b/helpers/gsl.js deleted file mode 100644 index 186dcd8c17..0000000000 --- a/helpers/gsl.js +++ /dev/null @@ -1,188 +0,0 @@ -var parse = function(obj, path) { - if (typeof path == 'string') path = path.split('.'); - if (Array.isArray(path) && path.length) { - var localPath = path.shift(); - if (obj[localPath]) { - return parse(obj[localPath], path); - } else { - return 'not set'; - } - } else { - return obj; - } -}; - -var transform = function(val, transformation) { - if (transformation == 'DATE') { - return new Date(val); - } else if (transformation == 'INTEGER') { - return parseInt(val); - } else if (transformation == 'STRING') { - return val.toString(); - } else if (transformation == 'DAYSFROM') { - // Return the number of days between the date and now - var now = new Date(); - var then = new Date(val); - var timeDiff = then.getTime() - now.getTime(); - var diff = Math.round(timeDiff / (1000 * 3600 * 24)); - return diff; - } else if (transformation == 'COUNT') { - return val.length; - } else if (transformation == 'EACH') { - return val; - } else { - return val; - } -}; - -var validate = function(obj, conditions) { - var result = 0; - var message = []; - var override = false; - - // Extract the values for the conditions - conditions.forEach(function(condition) { - if (condition.property) { - var conditionResult = 0; - condition.parsed = parse(obj, condition.property); - - // Transform the property if required - if (condition.transform) { - condition.parsed = transform(condition.parsed, condition.transform); - } - - // Compare the property with the operator - if (condition.op) { - if (condition.op == 'EQ') { - if (condition.parsed == condition.value) { - message.push(`${condition.property}: ${condition.parsed} matched: ${condition.value}`); - } else { - conditionResult = 2; - message.push(`${condition.property}: ${condition.parsed} did not match: ${condition.value}`); - } - } else if (condition.op == 'GT') { - if (condition.parsed > condition.value) { - message.push(`${condition.property}: count of ${condition.parsed} was greater than: ${condition.value}`); - } else { - conditionResult = 2; - message.push(`${condition.property}: count of ${condition.parsed} was not greater than: ${condition.value}`); - } - } else if (condition.op == 'NE') { - if (condition.parsed !== condition.value) { - message.push(`${condition.property}: ${condition.parsed} is not: ${condition.value}`); - } else { - conditionResult = 2; - message.push(`${condition.property}: ${condition.parsed} is: ${condition.value}`); - } - } - } else if (condition.transform && condition.transform == 'EACH' && condition.conditions) { - // Recurse into the same function - var subProcessed = []; - condition.parsed.forEach(function(parsed) { - subProcessed.push(validate(parsed, condition.conditions)); - }); - subProcessed.forEach(function(sub) { - if (sub.status) conditionResult = sub.status; - if (sub.message) message.push(sub.message); - }); - } - if (condition.invert) conditionResult = (conditionResult ? 0 : 2); - - if (condition.override && !conditionResult) override = true; - if (conditionResult) result = conditionResult; - } - }); - - if (result && override) result = 0; - - if (!message.length) { - message = ['The resource matched all required conditions']; - } - - return { - status: result, - message: message.join(', ') - }; -}; - -var gsl = function(source, input, resourceMap, callback) { - if (!source || !input) return callback('No source or input provided'); - if (!input.apis || !input.apis[0]) return callback('No APIs provided for input'); - if (!input.conditions || !input.conditions.length) return callback('No conditions provided for input'); - - // Split apis into service:api - // TODO: support conditions that use different APIs - var service = input.conditions[0].service; - var api = input.conditions[0].api; - var resourcePath; - if (resourceMap && - resourceMap[service] && - resourceMap[service][api]) { - resourcePath = resourceMap[service][api]; - } - - if (!source[service]) return callback(`Source data did not contain service: ${service}`); - if (!source[service][api]) return callback(`Source data did not contain API: ${api}`); - - var results = []; - - for (var region in source[service][api]) { - var regionVal = source[service][api][region]; - if (typeof regionVal !== 'object') continue; - if (regionVal.err) { - results.push({ - status: 3, - message: regionVal.err.message || 'Error', - region: region - }); - } else if (regionVal.data) { - // It's an array, loop - regionVal.data.forEach(function(regionData) { - var validated = validate(regionData, input.conditions); - var parsedResource = parse(regionData, resourcePath); - if (typeof parsedResource !== 'string') parsedResource = null; - - results.push({ - status: validated.status, - resource: parsedResource, - message: validated.message, - region: region - }); - }); - } else { - for (var resourceName in regionVal) { - var resourceObj = regionVal[resourceName]; - if (resourceObj.err) { - results.push({ - status: 3, - resource: resourceName, - message: resourceObj.err.message || 'Error', - region: region - }); - } else if (!resourceObj.data) { - results.push({ - status: 3, - resource: resourceName, - message: 'No data returned', - region: region - }); - } else { - var validated = validate(resourceObj.data, input.conditions); - var parsedResource = parse(resourceObj.data, resourcePath); - if (typeof parsedResource !== 'string') parsedResource = null; - - results.push({ - status: validated.status, - resource: parsedResource, - message: validated.message, - region: region - }); - } - } - } - } - - callback(null, results, source[service][api]); -}; - -module.exports = gsl; diff --git a/helpers/oracle/regions.js b/helpers/oracle/regions.js index a83e52e5b6..00bc5e705e 100644 --- a/helpers/oracle/regions.js +++ b/helpers/oracle/regions.js @@ -16,14 +16,14 @@ var regions = [ ]; module.exports = { - default: ['us-ashburn-1'], + default: ['default'], all: regions, vcn: regions, group: regions, publicIp: regions, securityList: regions, loadBalancer: regions, - user: ['us-ashburn-1'], + user: ['default'], userGroupMembership: regions, authenticationPolicy: regions, exprt: regions, @@ -45,13 +45,13 @@ module.exports = { volume: regions, volumeBackup: regions, bootVolumeAttachment: regions, - availabilityDomain: ['us-ashburn-1'], + availabilityDomain: ['default'], volumeGroup: regions, volumeGroupBackup: regions, volumeBackupPolicy: regions, volumeBackupPolicyAssignment: regions, preAuthenticatedRequest: regions, - configuration: ['us-ashburn-1'], + configuration: ['default'], dbSystem: regions, mountTarget: regions, namespace: regions diff --git a/index.js b/index.js index 7e57be6716..7ef53f670c 100755 --- a/index.js +++ b/index.js @@ -169,13 +169,12 @@ if (config.credentials.aws.credential_file) { } else if (config.credentials.oracle.credential_file) { settings.cloud = 'oracle'; cloudConfig = loadHelperFile(config.credentials.oracle.credential_file); - if (!cloudConfig || !cloudConfig.tenancyId || !cloudConfig.compartmentId || !cloudConfig.userId || !cloudConfig.keyValue) { - console.error('ERROR: Oracle credential file does not have tenancyId, compartmentId, userId, or keyValue'); + if (!cloudConfig || !cloudConfig.tenancyId || !cloudConfig.compartmentId || !cloudConfig.userId || !cloudConfig.keyValue || !cloudConfig.region) { + console.error('ERROR: Oracle credential file does not have tenancyId, compartmentId, userId, region, or keyValue'); process.exit(1); } cloudConfig.RESTversion = '/20160918'; - cloudConfig.region = 'us-ashburn-1'; } else if (config.credentials.oracle.tenancy_id) { settings.cloud = 'oracle'; checkRequiredKeys(config.credentials.oracle, ['compartment_id', 'user_id', 'key_fingerprint', 'key_value']); @@ -186,7 +185,7 @@ if (config.credentials.aws.credential_file) { userId: config.credentials.oracle.user_id, keyFingerprint: config.credentials.oracle.key_fingerprint, keyValue: config.credentials.oracle.key_value, - region: 'us-ashburn-1', + region: config.credentials.oracle.region, }; } else if (config.credentials.github.credential_file) { settings.cloud = 'github'; diff --git a/plugins/aws/cloudformation/plainTextParameters.js b/plugins/aws/cloudformation/plainTextParameters.js index 7de7e11174..035c2d760d 100644 --- a/plugins/aws/cloudformation/plainTextParameters.js +++ b/plugins/aws/cloudformation/plainTextParameters.js @@ -60,7 +60,7 @@ module.exports = { if (!stackDetails.Parameters || !stackDetails.Parameters.length) { helpers.addResult(results, 0, 'Template does not contain any parameters', region, resource); - return; + continue; } for (var parameter of stackDetails.Parameters) { diff --git a/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js b/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js index 6a849aaafe..93d96c595f 100644 --- a/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js +++ b/plugins/aws/cloudfront/cloudfrontLoggingEnabled.js @@ -20,18 +20,18 @@ module.exports = { 'for PCI. Enable CloudFront logging to log requests sent to ' + 'applications in a PCI environment.' }, - // gsl: { - // conditions: [ - // { - // service: 'cloudfront', - // api: 'getDistribution', - // property: 'Distribution.DistributionConfig.Logging.Enabled', - // transform: 'STRING', - // op: 'EQ', - // value: 'true' - // } - // ] - // }, + asl: { + conditions: [ + { + service: 'cloudfront', + api: 'getDistribution', + property: 'Distribution.DistributionConfig.Logging.Enabled', + transform: 'STRING', + op: 'EQ', + value: 'true' + } + ] + }, run: function(cache, settings, callback) { diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.js b/plugins/aws/ec2/vpcEndpointAcceptance.js index 03204c4d90..367689e2aa 100644 --- a/plugins/aws/ec2/vpcEndpointAcceptance.js +++ b/plugins/aws/ec2/vpcEndpointAcceptance.js @@ -43,7 +43,7 @@ module.exports = { } 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); @@ -54,7 +54,7 @@ module.exports = { if (!service.ServiceId) continue; var resource = `arn:${defaultPartition}:ec2:${region}:${service.Owner}:vpc-endpoint-service/${service.ServiceId}`; - + if (service.AcceptanceRequired) { helpers.addResult(results, 0, `VPC endpoint service ${service.ServiceId} requires acceptance by the service owner`, @@ -77,7 +77,7 @@ module.exports = { !describeVpcEndpointServicePermissions.data.AllowedPrincipals.length) { helpers.addResult(results, 0, `VPC endpoint service ${service.ServiceId} does not require acceptance by the service owner but no allowed principals found`, - region, resource); + region, resource); } else { helpers.addResult(results, 2, `VPC endpoint service ${service.ServiceId} does not require acceptance by the service owner for allowed principals`, diff --git a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js index 116f1ec0b3..b6eb54ec00 100644 --- a/plugins/aws/ec2/vpcEndpointAcceptance.spec.js +++ b/plugins/aws/ec2/vpcEndpointAcceptance.spec.js @@ -6,17 +6,17 @@ const vpcEndpointServices = [ "ServiceName": "com.amazonaws.vpce.us-east-1.vpce-svc-09d3a6a098dce6e8c", "ServiceId": "vpce-svc-09d3a6a098dce6e8c", "ServiceType": [ - { - "ServiceType": "Interface" - } + { + "ServiceType": "Interface" + } ], "AvailabilityZones": [ - "us-east-1a", - "us-east-1b" + "us-east-1a", + "us-east-1b" ], "Owner": "123456654321", "BaseEndpointDnsNames": [ - "vpce-svc-09d3a6a098dce6e8c.us-east-1.vpce.amazonaws.com" + "vpce-svc-09d3a6a098dce6e8c.us-east-1.vpce.amazonaws.com" ], "VpcEndpointPolicySupported": false, "AcceptanceRequired": true, @@ -27,18 +27,18 @@ const vpcEndpointServices = [ "ServiceName": "com.amazonaws.vpce.us-east-1.vpce-svc-09145867a106679a3", "ServiceId": "vpce-svc-09145867a106679a3", "ServiceType": [ - { - "ServiceType": "Interface" - } + { + "ServiceType": "Interface" + } ], "AvailabilityZones": [ - "us-east-1a", - "us-east-1b", - "us-east-1c" + "us-east-1a", + "us-east-1b", + "us-east-1c" ], "Owner": "123456654321", "BaseEndpointDnsNames": [ - "vpce-svc-09145867a106679a3.us-east-1.vpce.amazonaws.com" + "vpce-svc-09145867a106679a3.us-east-1.vpce.amazonaws.com" ], "VpcEndpointPolicySupported": false, "AcceptanceRequired": false, diff --git a/plugins/aws/iam/accessKeysExtra.js b/plugins/aws/iam/accessKeysExtra.js index d643166c7f..675c6241df 100644 --- a/plugins/aws/iam/accessKeysExtra.js +++ b/plugins/aws/iam/accessKeysExtra.js @@ -9,7 +9,23 @@ module.exports = { link: 'http://docs.aws.amazon.com/IAM/latest/UserGuide/ManagingCredentials.html', recommended_action: 'Remove the extra access key for the specified user.', apis: ['IAM:generateCredentialReport'], - + asl: { + conditions: [ + { + service: 'iam', + api: 'generateCredentialReport', + property: 'access_key_1_active', + op: 'ISFALSE' + }, + { + service: 'iam', + api: 'generateCredentialReport', + property: 'access_key_2_active', + op: 'ISFALSE', + logical: 'OR' + } + ] + }, run: function(cache, settings, callback) { var results = []; diff --git a/plugins/aws/iam/accessKeysLastUsed.js b/plugins/aws/iam/accessKeysLastUsed.js index b95d0c8b51..a349847e3d 100644 --- a/plugins/aws/iam/accessKeysLastUsed.js +++ b/plugins/aws/iam/accessKeysLastUsed.js @@ -28,6 +28,18 @@ module.exports = { default: 90 } }, + asl: { + conditions: [ + { + service: 'iam', + api: 'generateCredentialReport', + property: 'access_key_1_last_used_date', + transform: 'DAYSFROM', + op: 'GT', + value: 90 + } + ] + }, run: function(cache, settings, callback) { var config = { diff --git a/plugins/aws/iam/accessKeysRotated.js b/plugins/aws/iam/accessKeysRotated.js index 846b82e82c..d72395a1f1 100644 --- a/plugins/aws/iam/accessKeysRotated.js +++ b/plugins/aws/iam/accessKeysRotated.js @@ -32,6 +32,18 @@ module.exports = { default: 90 } }, + asl: { + conditions: [ + { + service: 'iam', + api: 'generateCredentialReport', + property: 'access_key_1_last_rotated', + transform: 'DAYSFROM', + op: 'GT', + value: 90 + } + ] + }, run: function(cache, settings, callback) { var config = { diff --git a/plugins/aws/iam/accessKeysRotated.spec.js b/plugins/aws/iam/accessKeysRotated.spec.js index cfcb22d6e3..726e449d55 100644 --- a/plugins/aws/iam/accessKeysRotated.spec.js +++ b/plugins/aws/iam/accessKeysRotated.spec.js @@ -2,7 +2,7 @@ var expect = require('chai').expect; const accessKeysRotated = require('./accessKeysRotated'); var warnDate = new Date(); -warnDate.setMonth(warnDate.getMonth() - 3); +warnDate.setMonth(warnDate.getMonth() - 4); var passDate = new Date(); passDate.setMonth(passDate.getMonth() - 2); var failDate = new Date(); diff --git a/plugins/aws/iam/canaryKeysUsed.js b/plugins/aws/iam/canaryKeysUsed.js index 6f07bec84d..1e3cb681bd 100644 --- a/plugins/aws/iam/canaryKeysUsed.js +++ b/plugins/aws/iam/canaryKeysUsed.js @@ -17,7 +17,20 @@ module.exports = { default: '' } }, - + //we need to check if (canary_user && access_key_1_last_used_date) || (canary_user && access_key_2_last_used_date) [SOP] + // canary_user && (access_key_1_last_used_date || access_key_2_last_used_date) + asl: { + conditions: [ + { + service: 'iam', + api: 'generateCredentialReport', + property: 'access_key_1_last_rotated', + transform: 'DAYSFROM', + op: 'GT', + value: 90 + } + ] + }, run: function(cache, settings, callback) { var config = { canary_user: settings.canary_user || this.settings.canary_user.default diff --git a/plugins/aws/iam/certificateExpiry.js b/plugins/aws/iam/certificateExpiry.js index 46b9eb8327..93aa0311c5 100644 --- a/plugins/aws/iam/certificateExpiry.js +++ b/plugins/aws/iam/certificateExpiry.js @@ -22,6 +22,18 @@ module.exports = { default: 30 } }, + asl: { + conditions: [ + { + service: 'iam', + api: 'listServerCertificates', + property: 'Expiration', + transform: 'DAYSFROM', + op: 'GT', + value: 90 + } + ] + }, run: function(cache, settings, callback) { var config = { diff --git a/plugins/aws/iam/emptyGroups.js b/plugins/aws/iam/emptyGroups.js index 00844bbdf5..3fc4381413 100644 --- a/plugins/aws/iam/emptyGroups.js +++ b/plugins/aws/iam/emptyGroups.js @@ -9,6 +9,18 @@ module.exports = { link: 'http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_WorkingWithGroupsAndUsers.html', recommended_action: 'Remove unused groups without users', apis: ['IAM:listGroups', 'IAM:getGroup'], + asl: { + conditions: [ + { + service: 'iam', + api: 'getGroup', + property: 'Users', + transform: 'COUNT', + op: 'GT', + value: 0 + } + ] + }, run: function(cache, settings, callback) { var results = []; diff --git a/plugins/aws/iam/iamRoleLastUsed.js b/plugins/aws/iam/iamRoleLastUsed.js index 22f796bc2f..77a0374f2d 100644 --- a/plugins/aws/iam/iamRoleLastUsed.js +++ b/plugins/aws/iam/iamRoleLastUsed.js @@ -35,6 +35,18 @@ module.exports = { default: 'true' } }, + asl: { + conditions: [ + { + service: 'iam', + api: 'getRole', + property: 'Role.RoleLastUsed.LastUsedDate', + transform: 'DAYSFROM', + op: 'GT', + value: 90 + } + ] + }, run: function(cache, settings, callback) { var config = { diff --git a/plugins/aws/iam/iamUserNameRegex.js b/plugins/aws/iam/iamUserNameRegex.js index 4545732c8f..865cea3603 100644 --- a/plugins/aws/iam/iamUserNameRegex.js +++ b/plugins/aws/iam/iamUserNameRegex.js @@ -17,6 +17,18 @@ module.exports = { default: '^.*$', }, }, + asl: { + conditions: [ + { + service: 'iam', + api: 'generateCredentialReport', + property: 'user', + transform: 'STRING', + op: 'MATCHES', + value: '^.*$' + } + ] + }, run: function(cache, settings, callback) { var results = []; diff --git a/plugins/aws/iam/maxPasswordAge.js b/plugins/aws/iam/maxPasswordAge.js index e1162152ba..195f9af205 100644 --- a/plugins/aws/iam/maxPasswordAge.js +++ b/plugins/aws/iam/maxPasswordAge.js @@ -45,7 +45,18 @@ module.exports = { default: 180 } }, - + asl: { + conditions: [ + { + service: 'iam', + api: 'getAccountPasswordPolicy', + property: 'MaxPasswordAge', + transform: 'INTEGER', + op: 'GT', + value: 90 + } + ] + }, run: function(cache, settings, callback) { var config = { max_password_age_fail: settings.max_password_age_fail || this.settings.max_password_age_fail.default, diff --git a/plugins/aws/iam/minPasswordLength.js b/plugins/aws/iam/minPasswordLength.js index f4c60db430..dd90b31999 100644 --- a/plugins/aws/iam/minPasswordLength.js +++ b/plugins/aws/iam/minPasswordLength.js @@ -46,6 +46,18 @@ module.exports = { default: 14 } }, + asl: { + conditions: [ + { + service: 'iam', + api: 'getAccountPasswordPolicy', + property: 'MinimumPasswordLength', + transform: 'INTEGER', + op: 'GT', + value: 90 + } + ] + }, run: function(cache, settings, callback) { var config = { diff --git a/plugins/aws/iam/passwordExpiration.js b/plugins/aws/iam/passwordExpiration.js index 173fceedef..772e6f221e 100644 --- a/plugins/aws/iam/passwordExpiration.js +++ b/plugins/aws/iam/passwordExpiration.js @@ -26,6 +26,18 @@ module.exports = { 'password expirations enforces this policy.', cis1: '1.11 Ensure IAM password policy expires passwords within 90 days or less' }, + asl: { + conditions: [ + { + service: 'iam', + api: 'getAccountPasswordPolicy', + property: 'MaxPasswordAge', + transform: 'INTEGER', + op: 'GT', + value: 90 + } + ] + }, run: function(cache, settings, callback) { var results = []; diff --git a/plugins/aws/iam/usersMfaEnabled.js b/plugins/aws/iam/usersMfaEnabled.js index 1e9df89f8f..fdddeca8f5 100644 --- a/plugins/aws/iam/usersMfaEnabled.js +++ b/plugins/aws/iam/usersMfaEnabled.js @@ -18,6 +18,18 @@ module.exports = { cis1: '1.2 Ensure multi-factor authentication (MFA) is enabled for all ' + 'IAM users that have a console password' }, + asl: { + conditions: [ + { + service: 'iam', + api: 'generateCredentialReport', + property: 'mfa_active', + transform: 'STRING', + op: 'EQ', + value: 'true' + } + ] + }, run: function(cache, settings, callback) { var results = []; diff --git a/plugins/aws/kms/kmsScheduledDeletion.js b/plugins/aws/kms/kmsScheduledDeletion.js index 6c0f0fa0b5..5973024f22 100644 --- a/plugins/aws/kms/kmsScheduledDeletion.js +++ b/plugins/aws/kms/kmsScheduledDeletion.js @@ -9,6 +9,18 @@ module.exports = { recommended_action: 'Disable the key deletion before the scheduled deletion time.', link: 'http://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html', apis: ['KMS:listKeys', 'KMS:describeKey'], + asl: { + conditions: [ + { + service: 'kms', + api: 'describeKey', + property: 'KeyMetadata.KeyState', + transform: 'STRING', + op: 'EQ', + value: 'PendingDeletion' + } + ] + }, run: function(cache, settings, callback) { var results = []; diff --git a/plugins/aws/s3/bucketLogging.js b/plugins/aws/s3/bucketLogging.js index a6f289b4b1..473c76bdf1 100644 --- a/plugins/aws/s3/bucketLogging.js +++ b/plugins/aws/s3/bucketLogging.js @@ -18,7 +18,18 @@ module.exports = { pci: 'PCI requires logging of all network access to environments containing ' + 'cardholder data. Enable S3 bucket access logs to log these network requests.' }, - + asl: { + conditions: [ + { + service: 's3', + api: 'getBucketLogging', + property: 'LoggingEnabled', + transform: 'STRING', + op: 'EQ', + value: 'true' + } + ] + }, run: function(cache, settings, callback) { var results = []; var source = {}; diff --git a/plugins/aws/s3/bucketVersioning.js b/plugins/aws/s3/bucketVersioning.js index e4eadecd00..a8d02cad9b 100644 --- a/plugins/aws/s3/bucketVersioning.js +++ b/plugins/aws/s3/bucketVersioning.js @@ -23,6 +23,35 @@ module.exports = { rollback: ['s3:PutBucketVersioning'] }, realtime_triggers: ['s3:CreateBucket', 's3:PutBucketVersioning'], + asl: { + conditions: [ + { + service: 's3', + api: 'getBucketVersioning', + property: 'Status', + transform: 'STRING', + op: 'EXISTS', + }, + { + service: 's3', + api: 'getBucketVersioning', + property: 'Status', + transform: 'STRING', + op: 'EQ', + value: 'Enabled', + logical: 'AND' + }, + { + service: 's3', + api: 'getBucketVersioning', + property: 'Status', + transform: 'STRING', + op: 'MATCHES', + value: '^[A-Z]{1}[a-z]+$', + logical: 'AND' + } + ] + }, run: function(cache, settings, callback) { var results = []; var source = {}; diff --git a/plugins/aws/sns/topicEncrypted.js b/plugins/aws/sns/topicEncrypted.js index e6cd7e02fc..44762ee63a 100644 --- a/plugins/aws/sns/topicEncrypted.js +++ b/plugins/aws/sns/topicEncrypted.js @@ -29,6 +29,18 @@ module.exports = { rollback: ['sns:SetTopicAttributes'] }, realtime_triggers: ['sns:CreateTopic', 'sns:SetTopicAttributes'], + asl: { + conditions: [ + { + service: 'sns', + api: 'getTopicAttributes', + property: 'Attributes.KmsMasterKeyId', + transform: 'STRING', + op: 'NE', + value: null + } + ] + }, run: function(cache, settings, callback) { var results = []; diff --git a/plugins/azure/cdnprofiles/detectInsecureCustomOrigin.js b/plugins/azure/cdnprofiles/detectInsecureCustomOrigin.js index b70f68ac24..fb3df8d631 100644 --- a/plugins/azure/cdnprofiles/detectInsecureCustomOrigin.js +++ b/plugins/azure/cdnprofiles/detectInsecureCustomOrigin.js @@ -46,7 +46,7 @@ module.exports = { if (!endpoints || endpoints.err || !endpoints.data) { helpers.addResult(results, 3, - 'Unable to query for CDN Profile endpoints: ' + helpers.addError(endpoints), profile.id); + 'Unable to query for CDN Profile endpoints: ' + helpers.addError(endpoints), location, profile.id); } else { if (!endpoints.data.length) { helpers.addResult(results, 0, diff --git a/plugins/google/storage/bucketLogging.js b/plugins/google/storage/bucketLogging.js index f6e974f9e7..451a46569c 100644 --- a/plugins/google/storage/bucketLogging.js +++ b/plugins/google/storage/bucketLogging.js @@ -34,20 +34,22 @@ module.exports = { helpers.addResult(results, 0, 'No storage buckets found', region); return rcb(); } + var bucketFound = false; buckets.data.forEach(bucket => { if (bucket.id) { - if (bucket.logging && - bucket.logging.logObjectPrefix == 'AccessLog') { + bucketFound = true; + if (bucket.logging && bucket.logging.logObjectPrefix && bucket.logging.logObjectPrefix.length) { helpers.addResult(results, 0, 'Bucket Logging Enabled', region, bucket.id); } else { helpers.addResult(results, 2, 'Bucket Logging not Enabled', region, bucket.id); } - } else { - helpers.addResult(results, 0, 'No storage buckets found', region); - return; } }); + if (!bucketFound) { + helpers.addResult(results, 0, 'No storage buckets found', region); + } + rcb(); }, function(){ // Global checking goes here diff --git a/plugins/oracle/audit/logRetentionPeriod.js b/plugins/oracle/audit/logRetentionPeriod.js index 77c6f58943..a5dd59b485 100644 --- a/plugins/oracle/audit/logRetentionPeriod.js +++ b/plugins/oracle/audit/logRetentionPeriod.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle/'); module.exports = { @@ -30,43 +29,39 @@ module.exports = { }; var results = []; - var source = {}; - var regions = helpers.regions(settings.govcloud); + var source = {} + var region = helpers.objectFirstKey(cache['regionSubscription']['list']); - async.each(regions.configuration, function(region, rcb){ - if (helpers.checkRegionSubscription(cache, source, results, region)) { + if (helpers.checkRegionSubscription(cache, source, results, region)) { - var configurations = helpers.addSource(cache, source, - ['configuration', 'get', region]); + var configurations = helpers.addSource(cache, source, + ['configuration', 'get', region]); - if (!configurations) return rcb(); + if (!configurations) return callback(null, results, source); - if (configurations.err || !configurations.data) { - helpers.addResult(results, 3, - 'Unable to query for audit configurations: ' + helpers.addError(configurations), region); - return rcb(); - } + if (configurations.err || !configurations.data) { + helpers.addResult(results, 3, + 'Unable to query for audit configurations: ' + helpers.addError(configurations), region); + return callback(null, results, source); + } - if (!Object.keys(configurations.data).length) { - helpers.addResult(results, 0, 'No audit configurations found', region); - return rcb(); - } - var configuration = configurations.data; + if (!Object.keys(configurations.data).length) { + helpers.addResult(results, 0, 'No audit configurations found', region); + return callback(null, results, source); + } + var configuration = configurations.data; - if (configuration.retentionPeriodDays && - config.audit_log_retention_days && - configuration.retentionPeriodDays >= config.audit_log_retention_days) { - helpers.addResult(results, 0, - `Audit configuration period is ${configuration.retentionPeriodDays} days`, region); - } else { - helpers.addResult(results, 2, - `Audit configuration period is ${configuration.retentionPeriodDays} days`, region); - } + if (configuration.retentionPeriodDays && + config.audit_log_retention_days && + configuration.retentionPeriodDays >= config.audit_log_retention_days) { + helpers.addResult(results, 0, + `Audit configuration period is ${configuration.retentionPeriodDays} days`, region); + } else { + helpers.addResult(results, 2, + `Audit configuration period is ${configuration.retentionPeriodDays} days`, region); } - rcb(); - }, function(){ - // Global checking goes here - callback(null, results, source); - }); + } + + callback(null, results, source); } }; \ No newline at end of file diff --git a/plugins/oracle/blockstorage/blockPolicyProtection.js b/plugins/oracle/blockstorage/blockPolicyProtection.js index 0049ca0019..0b45c537c1 100644 --- a/plugins/oracle/blockstorage/blockPolicyProtection.js +++ b/plugins/oracle/blockstorage/blockPolicyProtection.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -21,96 +20,92 @@ module.exports = { run: function (cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); var config = { policy_group_admins: settings.policy_group_admins || this.settings.policy_group_admins.default }; - async.each(regions.default, function (region, rcb) { - - var policies = helpers.addSource(cache, source, - ['policy', 'list', region]); - - if (!policies) return rcb(); - - if (policies.err || !policies.data) { - helpers.addResult(results, 3, - 'Unable to query for policies: ' + helpers.addError(policies), region); - return rcb(); - } + var region = helpers.objectFirstKey(cache['regionSubscription']['list']) + + var policies = helpers.addSource(cache, source, + ['policy', 'list', region]); + + if (!policies) return callback(null, results, source); + + if (policies.err || !policies.data) { + helpers.addResult(results, 3, + 'Unable to query for policies: ' + helpers.addError(policies), region); + return callback(null, results, source); + } + + if (!policies.data.length) { + helpers.addResult(results, 0, 'No policies found', region); + return callback(null, results, source); + } + var policyProtection = true; + var entered = false; + + policies.data.forEach(policy => { + if (policy.statements && + policy.statements.length) { + entered = true; + policy.statements.forEach(statement => { + + const statementLower = statement.toLowerCase(); + + if (statementLower.indexOf('allow') > -1 && + (statementLower.indexOf('manage') > -1 || + statementLower.indexOf('use') > -1) && + (statementLower.indexOf('request.permission') === -1 && + statementLower.indexOf('!=') === -1 && + statementLower.indexOf('_delete') === -1 && + (statementLower.indexOf('volume_') === -1 || + statementLower.indexOf('volume_backup_') === -1 || + statementLower.indexOf('volume_attachments_') === -1)) && + (statementLower.indexOf('volume') > -1 || + statementLower.indexOf('volumes-backups') > -1 || + statementLower.indexOf('volume-attachments') > -1 || + statementLower.indexOf('volume-family') > -1 || + statementLower.indexOf('all-resources') > -1)) { + + policyProtection = false; + var statementArr = statementLower.split(' '); + var statementNormalArr = statement.split(' '); + + var severity = 2; + + if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; + var groupType = statementArr[1]; + } else { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; + var groupType = 'The ' + statementArr[1]; + } - if (!policies.data.length) { - helpers.addResult(results, 0, 'No policies found', region); - return rcb(); - } - var policyProtection = true; - var entered = false; - - policies.data.forEach(policy => { - if (policy.statements && - policy.statements.length) { - entered = true; - policy.statements.forEach(statement => { - - const statementLower = statement.toLowerCase(); - - if (statementLower.indexOf('allow') > -1 && - (statementLower.indexOf('manage') > -1 || - statementLower.indexOf('use') > -1) && - (statementLower.indexOf('request.permission') === -1 && - statementLower.indexOf('!=') === -1 && - statementLower.indexOf('_delete') === -1 && - (statementLower.indexOf('volume_') === -1 || - statementLower.indexOf('volume_backup_') === -1 || - statementLower.indexOf('volume_attachments_') === -1)) && - (statementLower.indexOf('volume') > -1 || - statementLower.indexOf('volumes-backups') > -1 || - statementLower.indexOf('volume-attachments') > -1 || - statementLower.indexOf('volume-family') > -1 || - statementLower.indexOf('all-resources') > -1)) { - - policyProtection = false; - var statementArr = statementLower.split(' '); - var statementNormalArr = statement.split(' '); - - var severity = 2; - - if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; - var groupType = statementArr[1]; - } else { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; - var groupType = 'The ' + statementArr[1]; - } - - if (groupName === config.policy_group_admins.toLowerCase()) return; - if (statementArr.indexOf('request.user.name') > -1) { - groupType = 'The user'; - groupName = statementArr[statementArr.length - 1]; - severity = 1; - } - - helpers.addResult(results, severity, - `${groupType} ${groupName} has the ability to delete all block storage volumes in ${compartment} ${compartmentName}`, region, policy.id); + if (groupName === config.policy_group_admins.toLowerCase()) return; + if (statementArr.indexOf('request.user.name') > -1) { + groupType = 'The user'; + groupName = statementArr[statementArr.length - 1]; + severity = 1; } - }); + helpers.addResult(results, severity, + `${groupType} ${groupName} has the ability to delete all block storage volumes in ${compartment} ${compartmentName}`, region, policy.id); + } + }); - } - }); - if (policyProtection && entered) { - helpers.addResult(results, 0, 'All policies have block volume delete protection enabled', region); } - rcb(); - }, function () { - // Global checking goes here - callback(null, results, source); }); + + if (policyProtection && entered) { + helpers.addResult(results, 0, 'All policies have block volume delete protection enabled', region); + } + + callback(null, results, source); } }; \ No newline at end of file diff --git a/plugins/oracle/compute/instancePolicyProtection.js b/plugins/oracle/compute/instancePolicyProtection.js index a82971d6c2..e2ec28dd5b 100644 --- a/plugins/oracle/compute/instancePolicyProtection.js +++ b/plugins/oracle/compute/instancePolicyProtection.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -21,88 +20,86 @@ module.exports = { run: function (cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); + var config = { policy_group_admins: settings.policy_group_admins || this.settings.policy_group_admins.default }; - async.each(regions.default, function (region, rcb) { - - var policies = helpers.addSource(cache, source, - ['policy', 'list', region]); - - if (!policies) return rcb(); - - if (policies.err || !policies.data) { - helpers.addResult(results, 3, - 'Unable to query for policies: ' + helpers.addError(policies), region); - return rcb(); - } + var region = helpers.objectFirstKey(cache['regionSubscription']['list']) + + var policies = helpers.addSource(cache, source, + ['policy', 'list', region]); + + if (!policies) return callback(null, results, source); + + if (policies.err || !policies.data) { + helpers.addResult(results, 3, + 'Unable to query for policies: ' + helpers.addError(policies), region); + return callback(null, results, source); + } + + if (!policies.data.length) { + helpers.addResult(results, 0, 'No policies found', region); + return callback(null, results, source); + } + var policyProtection = true; + var entered = false; + + policies.data.forEach(policy => { + if (policy.statements && + policy.statements.length) { + entered = true; + + policy.statements.forEach(statement => { + + const statementLower = statement.toLowerCase(); + + if (statementLower.indexOf('allow') > -1 && + (statementLower.indexOf('manage') > -1 || + statementLower.indexOf('use') > -1) && + (statementLower.indexOf('request.permission') === -1 && + statementLower.indexOf('!=') === -1 && + statementLower.indexOf('_delete') === -1 && + statementLower.indexOf('instance_') === -1) && + (statementLower.indexOf('instance-family') > -1 || + statementLower.indexOf('all-resources') > -1)) { + + policyProtection = false; + var statementArr = statementLower.split(' '); + var statementNormalArr = statement.split(' '); + var severity = 2; + + if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; + var groupType = statementArr[1]; + } else { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; + var groupType = 'The ' + statementArr[1]; + } - if (!policies.data.length) { - helpers.addResult(results, 0, 'No policies found', region); - return rcb(); - } - var policyProtection = true; - var entered = false; - - policies.data.forEach(policy => { - if (policy.statements && - policy.statements.length) { - entered = true; - - policy.statements.forEach(statement => { - - const statementLower = statement.toLowerCase(); - - if (statementLower.indexOf('allow') > -1 && - (statementLower.indexOf('manage') > -1 || - statementLower.indexOf('use') > -1) && - (statementLower.indexOf('request.permission') === -1 && - statementLower.indexOf('!=') === -1 && - statementLower.indexOf('_delete') === -1 && - statementLower.indexOf('instance_') === -1) && - (statementLower.indexOf('instance-family') > -1 || - statementLower.indexOf('all-resources') > -1)) { - - policyProtection = false; - var statementArr = statementLower.split(' '); - var statementNormalArr = statement.split(' '); - var severity = 2; - - if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; - var groupType = statementArr[1]; - } else { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; - var groupType = 'The ' + statementArr[1]; - } - - if (groupName === config.policy_group_admins.toLowerCase()) return; - if (statementArr.indexOf('request.user.name') > -1) { - groupType = 'The user'; - groupName = statementArr[statementArr.length - 1]; - severity = 1; - } - - helpers.addResult(results, severity, - `${groupType} ${groupName} has the ability to delete all compute instances in ${compartment} ${compartmentName}`, region, policy.id); + if (groupName === config.policy_group_admins.toLowerCase()) return; + if (statementArr.indexOf('request.user.name') > -1) { + groupType = 'The user'; + groupName = statementArr[statementArr.length - 1]; + severity = 1; } - }); - } - }); - if (policyProtection && entered) { - helpers.addResult(results, 0, 'All policies have compute instance delete protection enabled', region); + + helpers.addResult(results, severity, + `${groupType} ${groupName} has the ability to delete all compute instances in ${compartment} ${compartmentName}`, region, policy.id); + } + }); } - rcb(); - }, function () { - // Global checking goes here - callback(null, results, source); }); + + if (policyProtection && entered) { + helpers.addResult(results, 0, 'All policies have compute instance delete protection enabled', region); + } + + callback(null, results, source); } }; \ No newline at end of file diff --git a/plugins/oracle/database/dbPolicyProtection.js b/plugins/oracle/database/dbPolicyProtection.js index ac043c9a9e..0b44ea0e3f 100644 --- a/plugins/oracle/database/dbPolicyProtection.js +++ b/plugins/oracle/database/dbPolicyProtection.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -21,91 +20,88 @@ module.exports = { run: function (cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); + var config = { policy_group_admins: settings.policy_group_admins || this.settings.policy_group_admins.default }; + + var region = helpers.objectFirstKey(cache['regionSubscription']['list']) - async.each(regions.default, function (region, rcb) { + var policies = helpers.addSource(cache, source, + ['policy', 'list', region]); - var policies = helpers.addSource(cache, source, - ['policy', 'list', region]); + if (!policies) return callback(null, results, source); - if (!policies) return rcb(); + if (policies.err || !policies.data) { + helpers.addResult(results, 3, + 'Unable to query for policies: ' + helpers.addError(policies), region); + return callback(null, results, source); + } - if (policies.err || !policies.data) { - helpers.addResult(results, 3, - 'Unable to query for policies: ' + helpers.addError(policies), region); - return rcb(); - } + if (!policies.data.length) { + helpers.addResult(results, 0, 'No policies found', region); + return callback(null, results, source); + } + var policyProtection = true; + var entered = false; - if (!policies.data.length) { - helpers.addResult(results, 0, 'No policies found', region); - return rcb(); - } - var policyProtection = true; - var entered = false; - - policies.data.forEach(policy => { - if (policy.statements && - policy.statements.length) { - entered = true; - - policy.statements.forEach(statement => { - const statementLower = statement.toLowerCase(); - - if (statementLower.indexOf('allow') > -1 && - (statementLower.indexOf('manage') > -1 || - statementLower.indexOf('use') > -1) && - (statementLower.indexOf('request.permission') === -1 && - statementLower.indexOf('!=') === -1 && - statementLower.indexOf('_delete') === -1 && - (statementLower.indexOf('db_system_') === -1 || - statementLower.indexOf('database_') === -1 || - statementLower.indexOf('db_home_') === -1)) && - (statementLower.indexOf('db-systems') > -1 || - statementLower.indexOf('databases') > -1 || - statementLower.indexOf('db-homes') > -1 || - statementLower.indexOf('all-resources') > -1)) { - - policyProtection = false; - var statementArr = statementLower.split(' '); - var statementNormalArr = statement.split(' '); - var severity = 2; - - if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; - var groupType = statementArr[1]; - } else { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; - var groupType = 'The ' + statementArr[1]; - } - - if (groupName === config.policy_group_admins.toLowerCase()) return; - if (statementArr.indexOf('request.user.name') > -1) { - groupType = 'The user'; - groupName = statementArr[statementArr.length - 1]; - severity = 1; - } - - helpers.addResult(results, severity, - `${groupType} ${groupName} has the ability to delete all database services in ${compartment} ${compartmentName}`, region, policy.id); + policies.data.forEach(policy => { + if (policy.statements && + policy.statements.length) { + entered = true; + + policy.statements.forEach(statement => { + const statementLower = statement.toLowerCase(); + + if (statementLower.indexOf('allow') > -1 && + (statementLower.indexOf('manage') > -1 || + statementLower.indexOf('use') > -1) && + (statementLower.indexOf('request.permission') === -1 && + statementLower.indexOf('!=') === -1 && + statementLower.indexOf('_delete') === -1 && + (statementLower.indexOf('db_system_') === -1 || + statementLower.indexOf('database_') === -1 || + statementLower.indexOf('db_home_') === -1)) && + (statementLower.indexOf('db-systems') > -1 || + statementLower.indexOf('databases') > -1 || + statementLower.indexOf('db-homes') > -1 || + statementLower.indexOf('all-resources') > -1)) { + + policyProtection = false; + var statementArr = statementLower.split(' '); + var statementNormalArr = statement.split(' '); + var severity = 2; + + if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; + var groupType = statementArr[1]; + } else { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; + var groupType = 'The ' + statementArr[1]; + } + + if (groupName === config.policy_group_admins.toLowerCase()) return; + if (statementArr.indexOf('request.user.name') > -1) { + groupType = 'The user'; + groupName = statementArr[statementArr.length - 1]; + severity = 1; } - }); - } - }); - if (policyProtection && entered) { - helpers.addResult(results, 0, 'All policies have database delete protection enabled', region); + helpers.addResult(results, severity, + `${groupType} ${groupName} has the ability to delete all database services in ${compartment} ${compartmentName}`, region, policy.id); + } + }); } - rcb(); - }, function () { - // Global checking goes here - callback(null, results, source); }); + + if (policyProtection && entered) { + helpers.addResult(results, 0, 'All policies have database delete protection enabled', region); + } + + callback(null, results, source); } }; \ No newline at end of file diff --git a/plugins/oracle/filestorage/nfsPolicyProtection.js b/plugins/oracle/filestorage/nfsPolicyProtection.js index 78d411fd8b..9e2052de74 100644 --- a/plugins/oracle/filestorage/nfsPolicyProtection.js +++ b/plugins/oracle/filestorage/nfsPolicyProtection.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -21,93 +20,88 @@ module.exports = { run: function (cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); var config = { policy_group_admins: settings.policy_group_admins || this.settings.policy_group_admins.default }; - async.each(regions.default, function (region, rcb) { - - var policies = helpers.addSource(cache, source, - ['policy', 'list', region]); - - if (!policies) return rcb(); - - if (policies.err || !policies.data) { - helpers.addResult(results, 3, - 'Unable to query for policies: ' + helpers.addError(policies), region); - return rcb(); - } + var region = helpers.objectFirstKey(cache['regionSubscription']['list']) + + var policies = helpers.addSource(cache, source, + ['policy', 'list', region]); + + if (!policies) return callback(null, results, source); + + if (policies.err || !policies.data) { + helpers.addResult(results, 3, + 'Unable to query for policies: ' + helpers.addError(policies), region); + return callback(null, results, source); + } + + if (!policies.data.length) { + helpers.addResult(results, 0, 'No policies found', region); + return callback(null, results, source); + } + var policyProtection = true; + var entered = false; + + policies.data.forEach(policy => { + if (policy.statements && + policy.statements.length) { + entered = true; + policy.statements.forEach(statement => { + + const statementLower = statement.toLowerCase(); + + if (statementLower.indexOf('allow') > -1 && + (statementLower.indexOf('manage') > -1 || + statementLower.indexOf('use') > -1) && + (statementLower.indexOf('request.permission') === -1 && + statementLower.indexOf('!=') === -1 && + statementLower.indexOf('_delete') === -1 && + (statementLower.indexOf('file_system_') === -1 || + statementLower.indexOf('mount_target_') === -1 || + statementLower.indexOf('export_set_') === -1)) && + (statementLower.indexOf('file-systems') > -1 || + statementLower.indexOf('mount-targets') > -1 || + statementLower.indexOf('export-sets') > -1 || + statementLower.indexOf('all-resources') > -1)) { + + policyProtection = false; + var statementArr = statementLower.split(' '); + var statementNormalArr = statement.split(' '); + var severity = 2; + + if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; + var groupType = statementArr[1]; + } else { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; + var groupType = 'The ' + statementArr[1]; + } - if (!policies.data.length) { - helpers.addResult(results, 0, 'No policies found', region); - return rcb(); - } - var policyProtection = true; - var entered = false; - - policies.data.forEach(policy => { - if (policy.statements && - policy.statements.length) { - entered = true; - policy.statements.forEach(statement => { - - const statementLower = statement.toLowerCase(); - - if (statementLower.indexOf('allow') > -1 && - (statementLower.indexOf('manage') > -1 || - statementLower.indexOf('use') > -1) && - (statementLower.indexOf('request.permission') === -1 && - statementLower.indexOf('!=') === -1 && - statementLower.indexOf('_delete') === -1 && - (statementLower.indexOf('file_system_') === -1 || - statementLower.indexOf('mount_target_') === -1 || - statementLower.indexOf('export_set_') === -1)) && - (statementLower.indexOf('file-systems') > -1 || - statementLower.indexOf('mount-targets') > -1 || - statementLower.indexOf('export-sets') > -1 || - statementLower.indexOf('all-resources') > -1)) { - - policyProtection = false; - var statementArr = statementLower.split(' '); - var statementNormalArr = statement.split(' '); - var severity = 2; - - if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; - var groupType = statementArr[1]; - } else { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; - var groupType = 'The ' + statementArr[1]; - } - - if (groupName === config.policy_group_admins.toLowerCase()) return; - if (statementArr.indexOf('request.user.name') > -1) { - groupType = 'The user'; - groupName = statementArr[statementArr.length - 1]; - severity = 1; - } - - helpers.addResult(results, severity, - `${groupType} ${groupName} has the ability to delete all file storage services in ${compartment} ${compartmentName}`, region, policy.id); + if (groupName === config.policy_group_admins.toLowerCase()) return; + if (statementArr.indexOf('request.user.name') > -1) { + groupType = 'The user'; + groupName = statementArr[statementArr.length - 1]; + severity = 1; } - }); - } - }); - if (policyProtection && entered) { - helpers.addResult(results, 0, 'All policies have file storage delete protection enabled', region); + helpers.addResult(results, severity, + `${groupType} ${groupName} has the ability to delete all file storage services in ${compartment} ${compartmentName}`, region, policy.id); + } + }); } - - rcb(); - }, function () { - // Global checking goes here - callback(null, results, source); }); + + if (policyProtection && entered) { + helpers.addResult(results, 0, 'All policies have file storage delete protection enabled', region); + } + + callback(null, results, source); } }; \ No newline at end of file diff --git a/plugins/oracle/identity/excessivePolicies.js b/plugins/oracle/identity/excessivePolicies.js index a297bc2d4f..01900074e8 100644 --- a/plugins/oracle/identity/excessivePolicies.js +++ b/plugins/oracle/identity/excessivePolicies.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle/'); module.exports = { @@ -31,7 +30,6 @@ module.exports = { run: function(cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); var config = { excessive_policy_fail: settings.excessive_policy_fail || this.settings.excessive_policy_fail.default, excessive_policy_warn: settings.excessive_policy_warn || this.settings.excessive_policy_warn.default @@ -39,42 +37,39 @@ module.exports = { var custom = helpers.isCustom(settings, this.settings); - async.each(regions.default, function(region, rcb){ + var region = helpers.objectFirstKey(cache['regionSubscription']['list']) - if (helpers.checkRegionSubscription(cache, source, results, region)) { + if (helpers.checkRegionSubscription(cache, source, results, region)) { - var policies = helpers.addSource(cache, source, - ['policy', 'list', region]); + var policies = helpers.addSource(cache, source, + ['policy', 'list', region]); - if (!policies) return rcb(); + if (!policies) return callback(null, results, source); - if (policies.err || !policies.data) { - helpers.addResult(results, 3, - 'Unable to query for policies: ' + helpers.addError(policies), region); - return rcb(); - } + if (policies.err || !policies.data) { + helpers.addResult(results, 3, + 'Unable to query for policies: ' + helpers.addError(policies), region); + return callback(null, results, source); + } - if (!policies.data.length) { - helpers.addResult(results, 0, 'No policies found', region); - return rcb(); - } + if (!policies.data.length) { + helpers.addResult(results, 0, 'No policies found', region); + return callback(null, results, source); + } - var policyAmt = policies.data.length; + var policyAmt = policies.data.length; - var returnMsg = ' number of policies: ' + policyAmt + ' found'; + var returnMsg = ' number of policies: ' + policyAmt + ' found'; - if (policyAmt > config.excessive_policy_fail) { - helpers.addResult(results, 2, 'Excessive' + returnMsg, region, null, custom); - } else if (policyAmt > config.excessive_policy_warn) { - helpers.addResult(results, 1, 'Large' + returnMsg, region, null, custom); - } else { - helpers.addResult(results, 0, 'Acceptable' + returnMsg, region, null, custom); - } + if (policyAmt > config.excessive_policy_fail) { + helpers.addResult(results, 2, 'Excessive' + returnMsg, region, null, custom); + } else if (policyAmt > config.excessive_policy_warn) { + helpers.addResult(results, 1, 'Large' + returnMsg, region, null, custom); + } else { + helpers.addResult(results, 0, 'Acceptable' + returnMsg, region, null, custom); } - rcb(); - }, function(){ - // Global checking goes here - callback(null, results, source); - }); + } + + callback(null, results, source); } }; \ No newline at end of file diff --git a/plugins/oracle/identity/excessivePolicyStatements.js b/plugins/oracle/identity/excessivePolicyStatements.js index ce5e984d02..de527d8427 100644 --- a/plugins/oracle/identity/excessivePolicyStatements.js +++ b/plugins/oracle/identity/excessivePolicyStatements.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle/'); module.exports = { @@ -30,51 +29,48 @@ module.exports = { run: function(cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); var config = { excessive_policy_statement_fail: settings.excessive_policy_statement_fail || this.settings.excessive_policy_statement_fail.default, excessive_policy_statement_warn: settings.excessive_policy_statement_warn || this.settings.excessive_policy_statement_warn.default }; var custom = helpers.isCustom(settings, this.settings); + + var region = helpers.objectFirstKey(cache['regionSubscription']['list']) + - async.each(regions.default, function(region, rcb){ + if (helpers.checkRegionSubscription(cache, source, results, region)) { - if (helpers.checkRegionSubscription(cache, source, results, region)) { + var policies = helpers.addSource(cache, source, + ['policy', 'list', region]); - var policies = helpers.addSource(cache, source, - ['policy', 'list', region]); + if (!policies) return callback(null, results, source); - if (!policies) return rcb(); + if (policies.err || !policies.data) { + helpers.addResult(results, 3, + 'Unable to query for policies: ' + helpers.addError(policies), region); + return callback(null, results, source); + } - if (policies.err || !policies.data) { - helpers.addResult(results, 3, - 'Unable to query for policies: ' + helpers.addError(policies), region); - return rcb(); - } + if (!policies.data.length) { + helpers.addResult(results, 0, 'No policies found', region); + return callback(null, results, source); + } + + policies.data.forEach(policy => { + var policyAmt = policy.statements.length; + var returnMsg = ' number of policy statements: ' + policyAmt + ' found'; - if (!policies.data.length) { - helpers.addResult(results, 0, 'No policies found', region); - return rcb(); + if (policyAmt > config.excessive_policy_statement_fail) { + helpers.addResult(results, 2, 'Excessive' + returnMsg, region, policy.id, custom); + } else if (policyAmt > config.excessive_policy_statement_warn) { + helpers.addResult(results, 1, 'Large' + returnMsg, region, policy.id, custom); + } else { + helpers.addResult(results, 0, 'Acceptable' + returnMsg, region, policy.id, custom); } + }); + } - policies.data.forEach(policy => { - var policyAmt = policy.statements.length; - var returnMsg = ' number of policy statements: ' + policyAmt + ' found'; - - if (policyAmt > config.excessive_policy_statement_fail) { - helpers.addResult(results, 2, 'Excessive' + returnMsg, region, policy.id, custom); - } else if (policyAmt > config.excessive_policy_statement_warn) { - helpers.addResult(results, 1, 'Large' + returnMsg, region, policy.id, custom); - } else { - helpers.addResult(results, 0, 'Acceptable' + returnMsg, region, policy.id, custom); - } - }); - } - rcb(); - }, function(){ - // Global checking goes here - callback(null, results, source); - }); + callback(null, results, source); } }; \ No newline at end of file diff --git a/plugins/oracle/identity/minPasswordLength.js b/plugins/oracle/identity/minPasswordLength.js index 5e91eb49b4..bce5b6598b 100644 --- a/plugins/oracle/identity/minPasswordLength.js +++ b/plugins/oracle/identity/minPasswordLength.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -18,15 +17,8 @@ module.exports = { run: function(cache, settings, callback) { var results = []; var source = {}; - var defaultRegion = ''; - if (cache.authenticationPolicy && - cache.authenticationPolicy.get && - Object.keys(cache.authenticationPolicy.get).length) { - defaultRegion = helpers.objectFirstKey(cache.authenticationPolicy.get); - } else { - return callback(null, results, source); - } + var defaultRegion = helpers.objectFirstKey(cache['regionSubscription']['list']); var authenticationPolicy = helpers.addSource(cache, source, ['authenticationPolicy', 'get', defaultRegion]); @@ -48,7 +40,7 @@ module.exports = { var passwordPolicy = policy.passwordPolicy; if (passwordPolicy && passwordPolicy.minimumPasswordLength) { - if (passwordPolicy.minimumPasswordLength > 14) { + if (passwordPolicy.minimumPasswordLength >= 14) { helpers.addResult(results, 0, 'Minimum password length of: ' + passwordPolicy.minimumPasswordLength + ' is suitable', 'global', authenticationPolicy.data.compartmentId); } else if (passwordPolicy && passwordPolicy.minimumPasswordLength && diff --git a/plugins/oracle/identity/passwordRequiresLowercase.js b/plugins/oracle/identity/passwordRequiresLowercase.js index b96bab47c0..47db3fb743 100644 --- a/plugins/oracle/identity/passwordRequiresLowercase.js +++ b/plugins/oracle/identity/passwordRequiresLowercase.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -16,17 +15,9 @@ module.exports = { run: function(cache, settings, callback) { var results = []; var source = {}; - var defaultRegion = ''; - - if (cache.authenticationPolicy && - cache.authenticationPolicy.get && - Object.keys(cache.authenticationPolicy.get).length && - Object.keys(cache.authenticationPolicy.get).length > 0) { - defaultRegion = helpers.objectFirstKey(cache.authenticationPolicy.get); - } else { - return callback(null, results, source); - } + var defaultRegion = helpers.objectFirstKey(cache['regionSubscription']['list']); + var authenticationPolicy = helpers.addSource(cache, source, ['authenticationPolicy', 'get', defaultRegion]); diff --git a/plugins/oracle/identity/passwordRequiresNumbers.js b/plugins/oracle/identity/passwordRequiresNumbers.js index 8a2cae06a0..088ca701e4 100644 --- a/plugins/oracle/identity/passwordRequiresNumbers.js +++ b/plugins/oracle/identity/passwordRequiresNumbers.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -16,16 +15,8 @@ module.exports = { run: function(cache, settings, callback) { var results = []; var source = {}; - var defaultRegion = ''; - - if (cache.authenticationPolicy && - cache.authenticationPolicy.get && - Object.keys(cache.authenticationPolicy.get).length && - Object.keys(cache.authenticationPolicy.get).length > 0) { - defaultRegion = helpers.objectFirstKey(cache.authenticationPolicy.get); - } else { - return callback(null, results, source); - } + + var defaultRegion = helpers.objectFirstKey(cache['regionSubscription']['list']); var authenticationPolicy = helpers.addSource(cache, source, ['authenticationPolicy', 'get', defaultRegion]); diff --git a/plugins/oracle/identity/passwordRequiresSymbols.js b/plugins/oracle/identity/passwordRequiresSymbols.js index fff741bd45..1b27f207c6 100644 --- a/plugins/oracle/identity/passwordRequiresSymbols.js +++ b/plugins/oracle/identity/passwordRequiresSymbols.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -16,16 +15,8 @@ module.exports = { run: function(cache, settings, callback) { var results = []; var source = {}; - var defaultRegion = ''; - - if (cache.authenticationPolicy && - cache.authenticationPolicy.get && - Object.keys(cache.authenticationPolicy.get).length && - Object.keys(cache.authenticationPolicy.get).length > 0) { - defaultRegion = helpers.objectFirstKey(cache.authenticationPolicy.get); - } else { - return callback(null, results, source); - } + + var defaultRegion = helpers.objectFirstKey(cache['regionSubscription']['list']); var authenticationPolicy = helpers.addSource(cache, source, ['authenticationPolicy', 'get', defaultRegion]); diff --git a/plugins/oracle/identity/passwordRequiresUppercase.js b/plugins/oracle/identity/passwordRequiresUppercase.js index f80f14a504..4dda50834e 100644 --- a/plugins/oracle/identity/passwordRequiresUppercase.js +++ b/plugins/oracle/identity/passwordRequiresUppercase.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -16,16 +15,8 @@ module.exports = { run: function(cache, settings, callback) { var results = []; var source = {}; - var defaultRegion = ''; - - if (cache.authenticationPolicy && - cache.authenticationPolicy.get && - Object.keys(cache.authenticationPolicy.get).length && - Object.keys(cache.authenticationPolicy.get).length > 0) { - defaultRegion = helpers.objectFirstKey(cache.authenticationPolicy.get); - } else { - return callback(null, results, source); - } + + var defaultRegion = helpers.objectFirstKey(cache['regionSubscription']['list']); var authenticationPolicy = helpers.addSource(cache, source, ['authenticationPolicy', 'get', defaultRegion]); diff --git a/plugins/oracle/identity/policyLeastPrivilege.js b/plugins/oracle/identity/policyLeastPrivilege.js index c88b85d044..48197d1b20 100644 --- a/plugins/oracle/identity/policyLeastPrivilege.js +++ b/plugins/oracle/identity/policyLeastPrivilege.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -29,91 +28,86 @@ module.exports = { run: function (cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); var config = { policy_group_admins: settings.policy_group_admins || this.settings.policy_group_admins.default }; + var region = helpers.objectFirstKey(cache['regionSubscription']['list']) + + var policies = helpers.addSource(cache, source, + ['policy', 'list', region]); + + if (!policies) return callback(null, results, source); + + if (policies.err || !policies.data) { + helpers.addResult(results, 3, + 'Unable to query for policies: ' + helpers.addError(policies), region); + return callback(null, results, source); + } + + if (!policies.data.length) { + helpers.addResult(results, 0, 'No policies found', region); + return callback(null, results, source); + } + var policyProtection = true; + var entered = false; + policies.data.forEach(policy => { + if (policy.statements && + policy.statements.length) { + entered = true; + policy.statements.forEach(statement => { + + const statementLower = statement.toLowerCase(); + + if (statementLower.indexOf('allow') > -1 && + (statementLower.indexOf('manage') > -1 || + statementLower.indexOf('use') > -1) && + ((statementLower.indexOf('request.permission') === -1 && + statementLower.indexOf('!=') === -1) || + (statementLower.indexOf('request.operation') === -1 && + statementLower.indexOf('!=') === -1))) { + + policyProtection = false; + var statementArr = statementLower.split(' '); + var statementNormalArr = statement.split(' '); + var severity = 2; + + if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var resourceType = statementArr[4]; + var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; + var groupType = statementArr[1]; + var verb = statementArr[3]; + } else { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var resourceType = statementArr[5]; + var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; + var groupType = statementArr[1]; + var verb = statementArr[4]; + } - async.each(regions.default, function (region, rcb) { - var policies = helpers.addSource(cache, source, - ['policy', 'list', region]); - - if (!policies) return rcb(); - - if (policies.err || !policies.data) { - helpers.addResult(results, 3, - 'Unable to query for policies: ' + helpers.addError(policies), region); - return rcb(); - } - - if (!policies.data.length) { - helpers.addResult(results, 0, 'No policies found', region); - return rcb(); - } - var policyProtection = true; - var entered = false; - policies.data.forEach(policy => { - if (policy.statements && - policy.statements.length) { - entered = true; - policy.statements.forEach(statement => { - - const statementLower = statement.toLowerCase(); - - if (statementLower.indexOf('allow') > -1 && - (statementLower.indexOf('manage') > -1 || - statementLower.indexOf('use') > -1) && - ((statementLower.indexOf('request.permission') === -1 && - statementLower.indexOf('!=') === -1) || - (statementLower.indexOf('request.operation') === -1 && - statementLower.indexOf('!=') === -1))) { - - policyProtection = false; - var statementArr = statementLower.split(' '); - var statementNormalArr = statement.split(' '); - var severity = 2; - - if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var resourceType = statementArr[4]; - var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; - var groupType = statementArr[1]; - var verb = statementArr[3]; - } else { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var resourceType = statementArr[5]; - var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; - var groupType = statementArr[1]; - var verb = statementArr[4]; - } - - var adminsArr = config.policy_group_admins.toLowerCase().replace(' ', '').split(','); - - if (adminsArr.indexOf(groupName) > -1) return; - if (statementArr.indexOf('request.user.name') > -1) { - groupType = 'User'; - groupName = statementArr[statementArr.length - 1]; - severity = 1; - } + var adminsArr = config.policy_group_admins.toLowerCase().replace(' ', '').split(','); - helpers.addResult(results, severity, - `${groupType} ${groupName} has the ability to ${verb} ${resourceType} in ${compartment} ${compartmentName}`, region, policy.id); + if (adminsArr.indexOf(groupName) > -1) return; + if (statementArr.indexOf('request.user.name') > -1) { + groupType = 'User'; + groupName = statementArr[statementArr.length - 1]; + severity = 1; } - }); - } - }); - if (policyProtection && entered) { - helpers.addResult(results, 0, 'All policies follow least access.', region); + helpers.addResult(results, severity, + `${groupType} ${groupName} has the ability to ${verb} ${resourceType} in ${compartment} ${compartmentName}`, region, policy.id); + } + }); } - - rcb(); - }, function () { - // Global checking goes here - callback(null, results, source); }); + + if (policyProtection && entered) { + helpers.addResult(results, 0, 'All policies follow least access.', region); + } + + callback(null, results, source); } -}; +}; \ No newline at end of file diff --git a/plugins/oracle/identity/usersMfaEnabled.js b/plugins/oracle/identity/usersMfaEnabled.js index 9c51f6ccc7..8a7fc2ff9a 100644 --- a/plugins/oracle/identity/usersMfaEnabled.js +++ b/plugins/oracle/identity/usersMfaEnabled.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -21,24 +20,23 @@ module.exports = { run: function (cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); - var noMFAUsers = []; - async.each(regions.user, function (region, rcb) { + var region = helpers.objectFirstKey(cache['regionSubscription']['list']); + var users = helpers.addSource(cache, source, ['user', 'list', region]); - if (!users) return rcb(); + if (!users) return callback(null, results, source); if (users.err || !users.data) { helpers.addResult(results, 3, 'Unable to query for user MFA status: ' + helpers.addError(users)); - return rcb(); + return callback(null, results, source); } if (users.data.length < 2) { helpers.addResult(results, 0, 'No user accounts found'); - return rcb(); + return callback(null, results, source); } users.data.forEach(user => { @@ -48,10 +46,7 @@ module.exports = { helpers.addResult(results, 2, 'The user has MFA disabled', 'global', user.id); } }); - rcb(); - }, function () { - // Global checking goes here + callback(null, results, source); - }); } }; diff --git a/plugins/oracle/objectstore/objectPolicyProtection.js b/plugins/oracle/objectstore/objectPolicyProtection.js index f636eeb8fb..64b357ebd5 100644 --- a/plugins/oracle/objectstore/objectPolicyProtection.js +++ b/plugins/oracle/objectstore/objectPolicyProtection.js @@ -1,4 +1,3 @@ -var async = require('async'); var helpers = require('../../../helpers/oracle'); module.exports = { @@ -25,90 +24,86 @@ module.exports = { run: function (cache, settings, callback) { var results = []; var source = {}; - var regions = helpers.regions(settings.govcloud); + var config = { policy_group_admins: settings.policy_group_admins || this.settings.policy_group_admins.default }; + + var region = helpers.objectFirstKey(cache['regionSubscription']['list']); + + var policies = helpers.addSource(cache, source, + ['policy', 'list', region]); + + if (!policies) return callback(null, results, source); + + if (policies.err || !policies.data) { + helpers.addResult(results, 3, + 'Unable to query for policies: ' + helpers.addError(policies), region); + return callback(null, results, source); + } + + if (!policies.data.length) { + helpers.addResult(results, 0, 'No policies found', region); + return callback(null, results, source); + } + var policyProtection = true; + var entered = false; + policies.data.forEach(policy => { + if (policy.statements && + policy.statements.length) { + entered = true; + policy.statements.forEach(statement => { + + const statementLower = statement.toLowerCase(); + + if (statementLower.indexOf('allow') > -1 && + (statementLower.indexOf('manage') > -1 || + statementLower.indexOf('use') > -1) && + (statementLower.indexOf('request.permission') === -1 && + statementLower.indexOf('!=') === -1 && + statementLower.indexOf('_delete') === -1 && + (statementLower.indexOf('object_') === -1 || + statementLower.indexOf('bucket_') === -1)) && + (statementLower.indexOf('objects') > -1 || + statementLower.indexOf('buckets') > -1 || + statementLower.indexOf('all-resources') > -1)) { + + policyProtection = false; + var statementArr = statementLower.split(' '); + var statementNormalArr = statement.split(' '); + var severity = 2; + + if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; + var groupType = statementArr[1]; + } else { + var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; + var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; + var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; + var groupType = 'The ' + statementArr[1]; + } - async.each(regions.default, function (region, rcb) { - - var policies = helpers.addSource(cache, source, - ['policy', 'list', region]); - - if (!policies) return rcb(); - - if (policies.err || !policies.data) { - helpers.addResult(results, 3, - 'Unable to query for policies: ' + helpers.addError(policies), region); - return rcb(); - } - - if (!policies.data.length) { - helpers.addResult(results, 0, 'No policies found', region); - return rcb(); - } - var policyProtection = true; - var entered = false; - policies.data.forEach(policy => { - if (policy.statements && - policy.statements.length) { - entered = true; - policy.statements.forEach(statement => { - - const statementLower = statement.toLowerCase(); - - if (statementLower.indexOf('allow') > -1 && - (statementLower.indexOf('manage') > -1 || - statementLower.indexOf('use') > -1) && - (statementLower.indexOf('request.permission') === -1 && - statementLower.indexOf('!=') === -1 && - statementLower.indexOf('_delete') === -1 && - (statementLower.indexOf('object_') === -1 || - statementLower.indexOf('bucket_') === -1)) && - (statementLower.indexOf('objects') > -1 || - statementLower.indexOf('buckets') > -1 || - statementLower.indexOf('all-resources') > -1)) { - - policyProtection = false; - var statementArr = statementLower.split(' '); - var statementNormalArr = statement.split(' '); - var severity = 2; - - if (statementArr[1] === 'any-user' || statementArr[1] === 'dynamic-group') { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[6] === 'tenancy' ? 'tenancy' : statementArr[6]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[7]; - var groupType = statementArr[1]; - } else { - var groupName = statementArr[2] === 'to' ? '' : statementNormalArr[2]; - var compartment = statementArr[7] === 'tenancy' ? 'tenancy' : statementArr[7]; - var compartmentName = (!statementArr[7] || statementArr[7] === 'tenancy') ? '' : statementNormalArr[8]; - var groupType = 'The ' + statementArr[1]; - } - - if (groupName === config.policy_group_admins.toLowerCase()) return; - if (statementArr.indexOf('request.user.name') > -1) { - groupType = 'The user'; - groupName = statementArr[statementArr.length - 1]; - severity = 1; - } - - helpers.addResult(results, severity, - `${groupType} ${groupName} has the ability to delete all object store services in ${compartment} ${compartmentName}`, region, policy.id); + if (groupName === config.policy_group_admins.toLowerCase()) return; + if (statementArr.indexOf('request.user.name') > -1) { + groupType = 'The user'; + groupName = statementArr[statementArr.length - 1]; + severity = 1; } - }); - } - }); - if (policyProtection && entered) { - helpers.addResult(results, 0, 'All policies have object store delete protection enabled', region); + helpers.addResult(results, severity, + `${groupType} ${groupName} has the ability to delete all object store services in ${compartment} ${compartmentName}`, region, policy.id); + } + }); } - - rcb(); - }, function () { - // Global checking goes here - callback(null, results, source); }); + + if (policyProtection && entered) { + helpers.addResult(results, 0, 'All policies have object store delete protection enabled', region); + } + + callback(null, results, source); } }; \ No newline at end of file