prowler-cloud__prowler METASEP

providers/aws/services/s3/s3_bucket_server_access_logging_enabled/s3_bucket_server_access_logging_enabled.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "s3_bucket_server_access_logging_enabled",
  "CheckTitle": "Check if S3 buckets have server access logging enabled",
  "CheckType": "Logging and Monitoring",
  "ServiceName": "s3",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsS3Bucket",
  "Description": "Check if S3 buckets have server access logging enabled",
  "Risk": "Server access logs can assist you in security and access audits; help you learn about your customer base; and understand your Amazon S3 bill.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Ensure that S3 buckets have Logging enabled. CloudTrail data events can be used in place of S3 bucket logging. If that is the case, this finding can be considered a false positive.",
      "Url": "https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/s3/s3_bucket_object_versioning/s3_bucket_object_versioning.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "s3_bucket_object_versioning",
  "CheckTitle": "Check if S3 buckets have object versioning enabled",
  "CheckType": "Data Protection",
  "ServiceName": "s3",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsS3Bucket",
  "Description": "Check if S3 buckets have object versioning enabled",
  "Risk": "With versioning, you can easily recover from both unintended user actions and application failures.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Configure versioning using the Amazon console or API for buckets with sensitive information that is changing frecuently; and backup may not be enough to capture all the changes.",
      "Url": "https://docs.aws.amazon.com/AmazonS3/latest/dev-retired/Versioning.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/iam/iam_user_two_active_access_key/iam_user_two_active_access_key.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_user_two_active_access_key",
  "CheckTitle": "Check if IAM users have two active access keys",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsIamUser",
  "Description": "Check if IAM users have two active access keys",
  "Risk": "Access Keys could be lost or stolen. It creates a critical risk.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Avoid using long lived access keys.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAccessKeys.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/iam/iam_user_mfa_enabled_console_access/iam_user_mfa_enabled_console_access.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_user_mfa_enabled_console_access",
  "CheckTitle": "Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password.",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsIamUser",
  "Description": "Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password.",
  "Risk": "Unauthorized access to this critical account if password is not secure or it is disclosed in any way.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Enable MFA for users account. MFA is a simple best practice that adds an extra layer of protection on top of your user name and password. Recommended to use hardware keys over virtual MFA.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/iam/iam_user_hardware_mfa_enabled/iam_user_hardware_mfa_enabled.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_user_hardware_mfa_enabled",
  "CheckTitle": "Check if IAM users have Hardware MFA enabled.",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsIamUser",
  "Description": "Check if IAM users have Hardware MFA enabled.",
  "Risk": "Hardware MFA is preferred over virtual MFA.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Enable hardware MFA device for an IAM user from the AWS Management Console; the command line; or the IAM API.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_physical.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_rotate_access_key_90_days",
  "CheckTitle": "Ensure access keys are rotated every 90 days or less",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsIamUser",
  "Description": "Ensure access keys are rotated every 90 days or less",
  "Risk": "Access keys consist of an access key ID and secret access key which are used to sign programmatic requests that you make to AWS. AWS users need their own access keys to make programmatic calls to AWS from the AWS Command Line Interface (AWS CLI)- Tools for Windows PowerShell- the AWS SDKs- or direct HTTP calls using the APIs for individual AWS services. It is recommended that all access keys be regularly rotated.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use the credential report to  ensure  access_key_X_last_rotated  is less than 90 days ago.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "1.4"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level1"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_root_mfa_enabled",
  "CheckTitle": "Ensure MFA is enabled for the root account",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "critical",
  "ResourceType": "AwsIamUser",
  "Description": "Ensure MFA is enabled for the root account",
  "Risk": "The root account is the most privileged user in an AWS account. MFA adds an extra layer of protection on top of a user name and password. With MFA enabled when a user signs in to an AWS website they will be prompted for their user name and password as well as for an authentication code from their AWS MFA device. When virtual MFA is used for root accounts it is recommended that the device used is NOT a personal device but rather a dedicated mobile device (tablet or phone) that is managed to be kept charged and secured independent of any individual personal devices. (non-personal virtual MFA) This lessens the risks of losing access to the MFA due to device loss / trade-in or if the individual owning the device is no longer employed at the company.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Using IAM console navigate to Dashboard and expand Activate MFA on your root account.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#id_root-user_manage_mfa"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "1.13"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level1"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_root_hardware_mfa_enabled",
  "CheckTitle": "Ensure hardware MFA is enabled for the root account",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "critical",
  "ResourceType": "AwsIamUser",
  "Description": "Ensure hardware MFA is enabled for the root account",
  "Risk": "The root account is the most privileged user in an AWS account. MFA adds an extra layer of protection on top of a user name and password. With MFA enabled when a user signs in to an AWS website they will be prompted for their user name and password as well as for an authentication code from their AWS MFA device. For Level 2 it is recommended that the root account be protected with a hardware MFA.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Using IAM console navigate to Dashboard and expand Activate MFA on your root account.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#id_root-user_manage_mfa"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "1.14"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level1"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/iam/iam_policy_allows_privilege_escalation/iam_policy_allows_privilege_escalation.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_policy_allows_privilege_escalation",
  "CheckTitle": "Ensure no Customer Managed IAM policies allow actions that may lead into Privilege Escalation",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsIamPolicy",
  "Description": "Ensure no Customer Managed IAM policies allow actions that may lead into Privilege Escalation",
  "Risk": "Users with some IAM permissions are allowed to elevate their privileges up to administrator rights.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Grant usage permission on a per-resource basis and applying least privilege principle.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "CAF Security Epic: IAM",
  "Compliance": []
}

providers/aws/services/iam/iam_password_policy_expires_passwords_within_90_days_or_less/iam_password_policy_expires_passwords_within_90_days_or_less.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam-password-policy-expires-passwords-within-90-days-or-less",
  "CheckTitle": "Ensure IAM password policy expires passwords within 90 days or less",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsIamPasswordPolicy",
  "Description": "Ensure IAM password policy expires passwords within 90 days or less",
  "Risk": "Password policies are used to enforce password complexity requirements. IAM password policies can be used to ensure password are comprised of different character sets. It is recommended that the password policy require at least one uppercase letter.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Ensure Password expiration period (in days): is set to 90 or less.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "1.11"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level1"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_no_root_access_key",
  "CheckTitle": "Ensure no root account access key exists",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "critical",
  "ResourceType": "AwsIamUser",
  "Description": "Ensure no root account access key exists",
  "Risk": "The root account is the most privileged user in an AWS account. AWS Access Keys provide programmatic access to a given AWS account. It is recommended that all access keys associated with the root account be removed. Removing access keys associated with the root account limits vectors by which the account can be compromised. Removing the root access keys encourages the creation and use of role based accounts that are least privileged.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use the credential report to  that the user and ensure the access_key_1_active and access_key_2_active fields are set to FALSE.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "1.12"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level1"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/iam/iam_disable_90_days_credentials/iam_disable_90_days_credentials.metadata.json METASEP
{
  "Categories": [],
  "CheckID": "iam_disable_90_days_credentials",
  "CheckTitle": "Ensure credentials unused for 90 days or greater are disabled",
  "CheckType": "Software and Configuration Checks",
  "Compliance": [
    {
      "Control": [
        "1.3"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level1"
      ],
      "Version": "1.4"
    }
  ],
  "DependsOn": [],
  "Description": "Ensure credentials unused for 90 days or greater are disabled",
  "Notes": "",
  "Provider": "aws",
  "RelatedTo": [],
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Find the credentials that they were using and ensure that they are no longer operational. Ideally; you delete credentials if they are no longer needed. You can always recreate them at a later date if the need arises. At the very least; you should change the password or deactivate the access keys so that the former users no longer have access.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_finding-unused.html"
    }
  },
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "ResourceType": "AwsIamUser",
  "Risk": "AWS IAM users can access AWS resources using different types of credentials (passwords or access keys). It is recommended that all credentials that have been unused in 90 or greater days be removed or deactivated.",
  "ServiceName": "iam",
  "Severity": "medium",
  "SubServiceName": "",
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  }
}

providers/aws/services/iam/iam_disable_30_days_credentials/iam_disable_30_days_credentials.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_disable_30_days_credentials",
  "CheckTitle": "Ensure credentials unused for 30 days or greater are disabled",
  "CheckType": "Software and Configuration Checks",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsIamUser",
  "Description": "Ensure credentials unused for 30 days or greater are disabled",
  "Risk": "To increase the security of your AWS account; remove IAM user credentials (that is; passwords and access keys) that are not needed. For example; when users leave your organization or no longer need AWS access.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Find the credentials that they were using and ensure that they are no longer operational. Ideally; you delete credentials if they are no longer needed. You can always recreate them at a later date if the need arises. At the very least; you should change the password or deactivate the access keys so that the former users no longer have access.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_finding-unused.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_avoid_root_usage",
  "CheckTitle": "Avoid the use of the root accounts",
  "CheckType": "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsIamUser",
  "Description": "Avoid the use of the root account",
  "Risk": "The root account has unrestricted access to all resources in the AWS account. It is highly recommended that the use of this account be avoided.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Follow the remediation instructions of the Ensure IAM policies are attached only to groups or roles recommendation.",
      "Url": "http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "1.1"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level1"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/iam/iam_administrator_access_with_mfa/iam_administrator_access_with_mfa.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "iam_administrator_access_with_mfa",
  "CheckTitle": "Ensure users of groups with AdministratorAccess policy have MFA tokens enabled",
  "CheckType": "Infrastructure Security",
  "ServiceName": "iam",
  "SubServiceName": "",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsIamUser",
  "Description": "Ensure users of groups with AdministratorAccess policy have MFA tokens enabled",
  "Risk": "Policy may allow Anonymous users to perform actions.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Ensure this repository and its contents should be publicly accessible.",
      "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.metadata.json METASEP
{
  "Provider": "aws",

  "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483",
  "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Oracle ports 1521 or 2483.",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "securitygroup",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsEc2SecurityGroup",
  "Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Oracle ports 1521 or 2483.",
  "Risk": "If Security groups are not properly configured the attack surface is increased.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306",
  "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MySQL port 3306.",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "securitygroups",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsEc2SecurityGroup",
  "Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MySQL port 3306.",
  "Risk": "If Security groups are not properly configured the attack surface is increased.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389",
  "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 3389.",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "securitygroup",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsEc2SecurityGroup",
  "Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 3389.",
  "Risk": "If Security groups are not properly configured the attack surface is increased.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "4.2"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level2"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
  "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to SSH port 22.",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "securitygroup",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsEc2SecurityGroup",
  "Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to SSH port 22.",
  "Risk": "If Security groups are not properly configured the attack surface is increased.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "4.1"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level2"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21",
  "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to FTP ports 20 or 21.",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "securitygroup",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsEc2SecurityGroup",
  "Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to FTP ports 20 or 21.",
  "Risk": "If Security groups are not properly configured the attack surface is increased.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "4.1"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level2"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_any_port",
  "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to any port.",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "securitygroup",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsEc2SecurityGroup",
  "Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to any port.",
  "Risk": "If Security groups are not properly configured the attack surface is increased.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_3389/ec2_networkacl_allow_ingress_tcp_port_3389.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_networkacl_allow_ingress_tcp_port_3389",
  "CheckTitle": "Ensure no Network ACLs allow ingress from 0.0.0.0/0 to Microsoft RDP port 3389",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "networkacl",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsEc2NetworkAcl",
  "Description": "Ensure no Network ACLs allow ingress from 0.0.0.0/0 to Microsoft RDP port 3389",
  "Risk": "Even having a perimeter firewall, having network acls open allows any user or malware with vpc access to scan for well known and sensitive ports and gain access to instance.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Apply Zero Trust approach. Implement a process to scan and remediate unrestricted or overly permissive network acls. Recommended best practices is to narrow the definition for the minimum ports required.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "4.6"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level2"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_22/ec2_networkacl_allow_ingress_tcp_port_22.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_networkacl_allow_ingress_tcp_port_22",
  "CheckTitle": "Ensure no Network ACLs allow ingress from 0.0.0.0/0 to SSH port 22",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "networkacl",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "high",
  "ResourceType": "AwsEc2NetworkAcl",
  "Description": "Ensure no Network ACLs allow ingress from 0.0.0.0/0 to SSH port 22",
  "Risk": "Even having a perimeter firewall, having network acls open allows any user or malware with vpc access to scan for well known and sensitive ports and gain access to instance.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Apply Zero Trust approach. Implement a process to scan and remediate unrestricted or overly permissive network acls. Recommended best practices is to narrow the definition for the minimum ports required.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": [
    {
      "Control": [
        "4.5"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level2"
      ],
      "Version": "1.4"
    }
  ]
}

providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_instance_public_ip",
  "CheckTitle": "Check for EC2 Instances with Public IP.",
  "CheckType": "Infrastructure Security",
  "ServiceName": "ec2",
  "SubServiceName": "instance",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsEc2SecurityGroup",
  "Description": "Check for EC2 Instances with Public IP.",
  "Risk": "Exposing an EC2 directly to internet increases the attack surface and therefore the risk of compromise.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Use an ALB and apply WAF ACL.",
      "Url": "https://aws.amazon.com/blogs/aws/aws-web-application-firewall-waf-for-application-load-balancers/"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_ebs_snapshots_encrypted",
  "CheckTitle": "Check if EBS snapshots are encrypted.",
  "CheckType": "Data Protection",
  "ServiceName": "ec2",
  "SubServiceName": "snapshot",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "medium",
  "ResourceType": "AwsEc2Snapshot",
  "Description": "Check if EBS snapshots are encrypted.",
  "Risk": "Data encryption at rest prevents data visibility in the event of its unauthorized access or theft.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Encrypt all EBS Snapshot and Enable Encryption by default. You can configure your AWS account to enforce the encryption of the new EBS volumes and snapshot copies that you create. For example; Amazon EBS encrypts the EBS volumes created when you launch an instance and the snapshots that you copy from an unencrypted snapshot.",
      "Url": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html#encryption-by-default"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_ebs_public_snapshot",
  "CheckTitle": "Ensure there are no EBS Snapshots set as Public.",
  "CheckType": "Data Protection",
  "ServiceName": "ec2",
  "SubServiceName": "snapshot",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "critical",
  "ResourceType": "AwsEc2Snapshot",
  "Description": "Ensure there are no EBS Snapshots set as Public.",
  "Risk": "When you share a snapshot, you are giving others access to all of the data on the snapshot. Share snapshots only with people with whom you want to share all of your snapshot data.",
  "RelatedUrl": "",
  "Remediation": {
    "Code": {
      "CLI": "",
      "NativeIaC": "",
      "Other": "",
      "Terraform": ""
    },
    "Recommendation": {
      "Text": "Ensure the snapshot should be shared.",
      "Url": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-modifying-snapshot-permissions.html"
    }
  },
  "Categories": [],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": "",
  "Compliance": []
}

providers/aws/services/custom/sample-check/sample-check.sh METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation:
#
#   here URL to the relevand/official documentation
#
#   here commands or steps to fix it if avalable, like:
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/MyCloudTrailLG \
#     --filter-name AWSCloudTrailChanges \
#     --filter-pattern '{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }' \
#     --metric-transformations metricName=CloudTrailEventCount,metricNamespace=CloudTrailMetrics,metricValue=1

# CHECK_ID_checkN="N.N"
# CHECK_TITLE_checkN="[checkN] Description "
# CHECK_SCORED_checkN="NOT_SCORED"
# CHECK_CIS_LEVEL_checkN="EXTRA"
# CHECK_SEVERITY_checkNN="Medium"
# CHECK_ASFF_RESOURCE_TYPE_checkN="AwsAccount" # Choose appropriate value from https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format.html#asff-resources
# CHECK_ALTERNATE_checkN="extraN"
# CHECK_SERVICENAME_checkN="service" # get service short name from `curl -s https://api.regional-table.region-services.aws.a2z.com/index.json | jq -r '.prices[] | .id' | awk -F: '{ print $1 }' | sort -u`
# CHECK_RISK_checkN=""
# CHECK_REMEDIATION_checkN=""
# CHECK_DOC_checkN=""
# CHECK_CAF_EPIC_checkN=""

# General comments
# ----------------
# Do not add double quotes (") arround variable ${PROFILE_OPT} because this variable holds "--profile <profile-name>" and we need to read it as it is
# Always check for AccessDenied|UnauthorizedOperation|AuthorizationError after AWS CLI command, using "2>&1" at the end
# Avoid execute the same AWS CLI command again to check different attribute:
#  - Return all attributes on "--query"
#  - Use "read -r" to get all individual attributes
#  - Use "here-string" (<<<) when is necessary to interate through AWS CLI output with multiple attributes on the same line
#    - Here-string variable must be enclosed with double quotes, like "${LIST_OF_PUBLIC_INSTANCES}"
#  - See "Example of regional resource" below about how to do it
# When an attribute doesn't exist, AWS CLI "--query" always return "none" if output is json or "None" if output is text
# Use bash features to handle variable:
#  - ${var:N}      : Return string from position 'N'
#  - ${var:N:len}  : Return 'len' characters from position 'N'
#  - ${var^^}      : Convert to upper-case all characters
#  - ${var,,}      : Convert to lower-case all characters
#  - ATTENTION: macOS original bash version "GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin19)" doesn't support some variable expansion above.
#               Please make sure to test it.
#  - For more examples and how to use it please refer to https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
# Check code with ShellCheck for best practices:
#  - https://www.shellcheck.net/
#  - https://github.com/koalaman/shellcheck#user-content-in-your-editor

# Example of regional resource
# extraN(){
#   # "Description "
#   textInfo "Looking for instances in all regions...  "
#   for regx in ${REGIONS}; do
#     LIST_OF_PUBLIC_INSTANCES=$("${AWSCLI}" ec2 describe-instances ${PROFILE_OPT} --region "${regx}" --query 'Reservations[*].Instances[?PublicIpAddress].[InstanceId,PublicIpAddress]' --output text 2>&1)
#     if [[ $(echo "${LIST_OF_PUBLIC_INSTANCES}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
#       textInfo "${regx}: Access Denied trying to list EC2 Instances" "${regx}"
#       continue
#     fi
#     if [[ "${LIST_OF_PUBLIC_INSTANCES}" != "" && "${LIST_OF_PUBLIC_INSTANCES,,}" != "none" ]]; then
#       while read -r INSTANCE_ID PUBLIC_IP; do
#         textFail "${regx}: Instance: ${INSTANCE_ID} at IP: ${PUBLIC_IP} is internet-facing!" "${regx}" "${INSTANCE_ID}"
#       done <<< "${LIST_OF_PUBLIC_INSTANCES}"
#     else
#       textPass "${regx}: no Internet Facing EC2 Instances found" "${regx}"
#     fi
#   done
# }

# Example of global resource
# extraN(){
#   # "Description "
#   LIST_DISTRIBUTIONS=$("${AWSCLI}" cloudfront list-distributions ${PROFILE_OPT} --query 'DistributionList.Items[*].Id' --output text 2>&1)
#   if [[ $(echo "${LIST_DISTRIBUTIONS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
#     textInfo "${REGION}: Access Denied trying to list distributions" "${REGION}"
#     return
#   fi
#   if [[ "${LIST_DISTRIBUTIONS}" != "" && "${LIST_DISTRIBUTIONS,,}" != "none" ]]; then
#     for dist in ${LIST_DISTRIBUTIONS}; do
#       GEO_ENABLED=$("${AWSCLI}" cloudfront get-distribution-config $PROFILE_OPT --id "${dist}" --query 'DistributionConfig.Restrictions.GeoRestriction.RestrictionType' --output text 2>&1)
#       if [[ $(echo "${GEO_ENABLED}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
#         textInfo "${REGION}: Access Denied trying to get distribution config for ${dist}" "${REGION}"
#         continue
#       fi
#       if [[ "${GEO_ENABLED,,}" == "none" ]]; then
#         textFail "${REGION}: CloudFront distribution ${dist} has not Geo restrictions" "${REGION}" "${dist}"
#       else
#         textPass "${REGION}: CloudFront distribution ${dist} has Geo restrictions enabled" "${REGION}" "${dist}"
#       fi
#     done
#   else
#     textInfo "${REGION}: No CloudFront distributions found"
#   fi
# }

providers/aws/services/custom/custom-defined-check/custom-defined-check.sh METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra9999="9.9999"
CHECK_TITLE_extra9999="[check9999] Custom Defined Check"
CHECK_SCORED_extra79999="NOT_SCORED"
CHECK_CIS_LEVEL_extra9999="EXTRA"
CHECK_SEVERITY_extra9999="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra9999="Custom"
CHECK_ALTERNATE_extra9999="extra9999"
CHECK_SERVICENAME_extra9999="custom"
CHECK_RISK_cextra9999="Custom Defined Risk"
CHECK_REMEDIATION_extra9999="Custom Remediation"
CHECK_CAF_EPIC_extra9999="Custom EPIC"

extra9999(){

    for regx in $REGIONS; do
        MY_CUSTOM_CMD=$($AWSCLI $CUSTOM_CMD $PROFILE_OPT --region $regx --output text 2>&1)
        if [[ $(echo "$MY_CUSTOM_CMD" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
            textInfo "$regx: Access Denied or error trying to execute the custom command" "$regx"
            continue
        fi
        if [[ $MY_CUSTOM_CMD ]]; then
            for element in $MY_CUSTOM_CMD; do
                textFail "$regx: Custom output is: $element" "$regx" "$CHECK_SGDEFAULT_ID"
            done
        else
            textPass "$regx: Custom output is empty" "$regx" "$CHECK_SGDEFAULT_ID"
        fi
    done
}

contrib/org-multi-account/serverless_codebuild/docs/images/s3_screenshot.png METASEP

contrib/org-multi-account/serverless_codebuild/docs/images/prowler_org_architecture.png METASEP

providers/aws/services/s3/s3_bucket_server_access_logging_enabled/s3_bucket_server_access_logging_enabled.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.s3.s3_service import s3_client


class s3_bucket_server_access_logging_enabled(Check):
    def execute(self):
        findings = []
        for bucket in s3_client.buckets:
            report = Check_Report(self.metadata)
            report.region = bucket.region
            report.resource_id = bucket.name
            if bucket.logging:
                report.status = "PASS"
                report.status_extended = (
                    f"S3 Bucket {bucket.name} has server access logging enabled."
                )
            else:
                report.status = "FAIL"
                report.status_extended = (
                    f"S3 Bucket {bucket.name} has server access logging disabled."
                )
            findings.append(report)

        return findings

providers/aws/services/s3/s3_bucket_server_access_logging_enabled/__init__.py METASEP

providers/aws/services/s3/s3_bucket_object_versioning/s3_bucket_object_versioning.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.s3.s3_service import s3_client


class s3_bucket_object_versioning(Check):
    def execute(self):
        findings = []
        for bucket in s3_client.buckets:
            report = Check_Report(self.metadata)
            report.region = bucket.region
            report.resource_id = bucket.name
            if bucket.versioning:
                report.status = "PASS"
                report.status_extended = (
                    f"S3 Bucket {bucket.name} has versioning enabled."
                )
            else:
                report.status = "FAIL"
                report.status_extended = (
                    f"S3 Bucket {bucket.name} has versioning disabled."
                )
            findings.append(report)

        return findings

providers/aws/services/s3/s3_bucket_object_versioning/__init__.py METASEP

providers/aws/services/iam/iam_user_two_active_access_key/iam_user_two_active_access_key.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client


class iam_user_two_active_access_key(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.credential_report
        for user in response:
            report = Check_Report(self.metadata)
            report.resource_id = user["user"]
            report.resource_arn = user["arn"]
            report.region = iam_client.region
            if (
                user["access_key_1_active"] == "true"
                and user["access_key_2_active"] == "true"
            ):
                report.status = "FAIL"
                report.status_extended = (
                    f"User {user['user']} has 2 active access keys."
                )
            else:
                report.status = "PASS"
                report.status_extended = (
                    f"User {user['user']} has not 2 active access keys."
                )
            findings.append(report)

        return findings

providers/aws/services/iam/iam_user_two_active_access_key/__init__.py METASEP

providers/aws/services/iam/iam_user_mfa_enabled_console_access/iam_user_mfa_enabled_console_access.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client


class iam_user_mfa_enabled_console_access(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.credential_report
        for user in response:
            report = Check_Report(self.metadata)
            report.resource_id = user["user"]
            report.resource_arn = user["arn"]
            report.region = iam_client.region
            if user["password_enabled"] != "not_supported":
                if user["mfa_active"] == "false":
                    report.status = "FAIL"
                    report.status_extended = f"User {user['user']} has Console Password enabled but MFA disabled."
                else:
                    report.status = "PASS"
                    report.status_extended = f"User {user['user']} has Console Password enabled and MFA enabled."
            else:
                report.status = "PASS"
                report.status_extended = (
                    f"User {user['user']} has not Console Password enabled."
                )
            findings.append(report)

        return findings

providers/aws/services/iam/iam_user_mfa_enabled_console_access/__init__.py METASEP

providers/aws/services/iam/iam_user_hardware_mfa_enabled/iam_user_hardware_mfa_enabled.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client


class iam_user_hardware_mfa_enabled(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.users

        for user in response:
            report = Check_Report(self.metadata)
            report.resource_id = user.name
            report.resource_arn = user.arn
            report.region = iam_client.region
            if user.mfa_devices:
                for mfa_device in user.mfa_devices:
                    if mfa_device.type == "mfa" or mfa_device.type == "sms-mfa":
                        report.status = "FAIL"
                        report.status_extended = f"User {user.name} has a virtual MFA instead of a hardware MFA enabled."
                        findings.append(report)
                    else:
                        report.status = "PASS"
                        report.status_extended = (
                            f"User {user.name} has hardware MFA enabled."
                        )
                        findings.append(report)
            else:
                report.status = "FAIL"
                report.status_extended = (
                    f"User {user.name} has not any type of MFA enabled."
                )
                findings.append(report)

        return findings

providers/aws/services/iam/iam_user_hardware_mfa_enabled/__init__.py METASEP

providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days.py METASEP
import datetime

from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client

maximum_expiration_days = 90


class iam_rotate_access_key_90_days(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.credential_report

        for user in response:
            report = Check_Report(self.metadata)
            report.region = iam_client.region
            report.resource_id = user["user"]
            report.resource_arn = user["arn"]
            if (
                user["access_key_1_last_rotated"] == "N/A"
                and user["access_key_2_last_rotated"] == "N/A"
            ):
                report.status = "PASS"
                report.status_extended = f"User {user['user']} has not access keys."
            else:
                old_access_keys = False
                if user["access_key_1_last_rotated"] != "N/A":
                    access_key_1_last_rotated = (
                        datetime.datetime.now()
                        - datetime.datetime.strptime(
                            user["access_key_1_last_rotated"],
                            "%Y-%m-%dT%H:%M:%S+00:00",
                        )
                    )
                    if access_key_1_last_rotated.days > maximum_expiration_days:
                        old_access_keys = True
                        report.status = "FAIL"
                        report.status_extended = f"User {user['user']} has not rotated access key 1 in over 90 days ({access_key_1_last_rotated.days} days)."
                if user["access_key_2_last_rotated"] != "N/A":
                    access_key_2_last_rotated = (
                        datetime.datetime.now()
                        - datetime.datetime.strptime(
                            user["access_key_2_last_rotated"],
                            "%Y-%m-%dT%H:%M:%S+00:00",
                        )
                    )
                    if access_key_2_last_rotated.days > maximum_expiration_days:
                        old_access_keys = True
                        report.status = "FAIL"
                        report.status_extended = f"User {user['user']} has not rotated access key 2 in over 90 days ({access_key_2_last_rotated.days} days)."
                if not old_access_keys:
                    report.status = "PASS"
                    report.status_extended = (
                        f"User {user['user']} has access keys not older than 90 days."
                    )
            findings.append(report)

        return findings

providers/aws/services/iam/iam_rotate_access_key_90_days/__init__.py METASEP

providers/aws/services/iam/iam_root_mfa_enabled/iam_root_mfa_enabled.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client


class iam_root_mfa_enabled(Check):
    def execute(self) -> Check_Report:
        findings = []

        for user in iam_client.credential_report:
            if user["user"] == "<root_account>":
                report = Check_Report(self.metadata)
                report.region = iam_client.region
                report.resource_id = user["user"]
                report.resource_arn = user["arn"]
                if user["mfa_active"] == "false":
                    report.status = "FAIL"
                    report.status_extended = "MFA is not enabled for root account."
                else:
                    report.status = "PASS"
                    report.status_extended = "MFA is enabled for root account."
                findings.append(report)

        return findings

providers/aws/services/iam/iam_root_mfa_enabled/__init__.py METASEP

providers/aws/services/iam/iam_root_hardware_mfa_enabled/iam_root_hardware_mfa_enabled.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client


class iam_root_hardware_mfa_enabled(Check):
    def execute(self) -> Check_Report:
        findings = []
        virtual_mfa = False
        report = Check_Report(self.metadata)
        report.region = iam_client.region
        report.resource_id = "root"
        report.resource_arn = f"arn:aws:iam::{iam_client.account}:root"

        if iam_client.account_summary["SummaryMap"]["AccountMFAEnabled"] > 0:
            virtual_mfas = iam_client.virtual_mfa_devices
            for mfa in virtual_mfas:
                if "root" in mfa["SerialNumber"]:
                    virtual_mfa = True
                    report.status = "FAIL"
                    report.status_extended = "Root account has a virtual MFA instead of a hardware MFA enabled."
            if not virtual_mfa:
                report.status = "PASS"
                report.status_extended = "Root account has hardware MFA enabled."
        else:
            report.status = "FAIL"
            report.status_extended = "MFA is not enabled for root account."

        findings.append(report)

        return findings

providers/aws/services/iam/iam_root_hardware_mfa_enabled/__init__.py METASEP

providers/aws/services/iam/iam_policy_allows_privilege_escalation/iam_policy_allows_privilege_escalation.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client

# Does the tool analyze both users and roles, or just one or the other? --> Everything using AttachementCount.
# Does the tool take a principal-centric or policy-centric approach? --> Policy-centric approach.
# Does the tool handle resource constraints? --> We don't check if the policy affects all resources or not, we check everything.
# Does the tool consider the permissions of service roles? --> Just checks policies.
# Does the tool handle transitive privesc paths (i.e., attack chains)? --> Not yet.
# Does the tool handle the DENY effect as expected? --> Yes, it checks DENY's statements with Action and NotAction.
# Does the tool handle NotAction as expected? --> Yes
# Does the tool handle Condition constraints? --> Not yet.
# Does the tool handle service control policy (SCP) restrictions? --> No, SCP are within Organizations AWS API.


class iam_policy_allows_privilege_escalation(Check):
    def execute(self) -> Check_Report:
        privilege_escalation_iam_actions = {
            "iam:AttachGroupPolicy",
            "iam:SetDefaultPolicyVersion2",
            "iam:AddUserToGroup",
            "iam:AttachRolePolicy",
            "iam:AttachUserPolicy",
            "iam:CreateAccessKey",
            "iam:CreatePolicyVersion",
            "iam:CreateLoginProfile",
            "iam:PassRole",
            "iam:PutGroupPolicy",
            "iam:PutRolePolicy",
            "iam:PutUserPolicy",
            "iam:SetDefaultPolicyVersion",
            "iam:UpdateAssumeRolePolicy",
            "iam:UpdateLoginProfile",
            "sts:AssumeRole",
            "ec2:RunInstances",
            "lambda:CreateEventSourceMapping",
            "lambda:CreateFunction",
            "lambda:InvokeFunction",
            "lambda:UpdateFunctionCode",
            "dynamodb:CreateTable",
            "dynamodb:PutItem",
            "glue:CreateDevEndpoint",
            "glue:GetDevEndpoint",
            "glue:GetDevEndpoints",
            "glue:UpdateDevEndpoint",
            "cloudformation:CreateStack",
            "cloudformation:DescribeStacks",
            "datapipeline:CreatePipeline",
            "datapipeline:PutPipelineDefinition",
            "datapipeline:ActivatePipeline",
        }
        findings = []
        for policy in iam_client.customer_managed_policies:
            report = Check_Report(self.metadata)
            report.resource_id = policy["PolicyName"]
            report.resource_arn = policy["Arn"]
            report.region = iam_client.region

            # List of policy actions
            allowed_actions = set()
            denied_actions = set()
            denied_not_actions = set()

            # Recover all policy actions
            for statements in policy["PolicyDocument"]["Statement"]:
                # Recover allowed actions
                if statements["Effect"] == "Allow":
                    if type(statements["Action"]) is str:
                        allowed_actions = {statements["Action"]}
                    if type(statements["Action"]) is list:
                        allowed_actions = set(statements["Action"])

                # Recover denied actions
                if statements["Effect"] == "Deny":
                    if "Action" in statements:
                        if type(statements["Action"]) is str:
                            denied_actions = {statements["Action"]}
                        if type(statements["Action"]) is list:
                            denied_actions = set(statements["Action"])

                    if "NotAction" in statements:
                        if type(statements["NotAction"]) is str:
                            denied_not_actions = {statements["NotAction"]}
                        if type(statements["NotAction"]) is list:
                            denied_not_actions = set(statements["NotAction"])

            # First, we need to perform a left join with ALLOWED_ACTIONS and DENIED_ACTIONS
            left_actions = allowed_actions.difference(denied_actions)
            # Then, we need to find the DENIED_NOT_ACTIONS in LEFT_ACTIONS
            privileged_actions = left_actions.intersection(denied_not_actions)
            # Finally, check if there is a privilege escalation action within this policy
            policy_privilege_escalation_actions = privileged_actions.intersection(
                privilege_escalation_iam_actions
            )

            if len(policy_privilege_escalation_actions) == 0:
                report.status = "PASS"
                report.status_extended = f"Customer Managed IAM Policy {report.resource_arn} not allows for privilege escalation"
            else:
                report.status = "FAIL"
                report.status_extended = f"Customer Managed IAM Policy {report.resource_arn} allows for privilege escalation using the following actions: {policy_privilege_escalation_actions}"
            findings.append(report)
        return findings

providers/aws/services/iam/iam_policy_allows_privilege_escalation/__init__.py METASEP

providers/aws/services/iam/iam_password_policy_expires_passwords_within_90_days_or_less/iam_password_policy_expires_passwords_within_90_days_or_less.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client


class iam_password_policy_expires_passwords_within_90_days_or_less(Check):
    def execute(self) -> Check_Report:
        findings = []
        report = Check_Report(self.metadata)
        report.region = iam_client.region
        report.resource_id = "password_policy"
        # Check if password policy exists
        if iam_client.password_policy:
            # Check if password policy expiration exists
            if iam_client.password_policy.max_age:
                if iam_client.password_policy.max_age < 90:
                    report.status = "PASS"
                    report.status_extended = f"Password expiration is set lower than 90 days ({iam_client.password_policy.max_age} days)."
                else:
                    report.status = "FAIL"
                    report.status_extended = f"Password expiration is set greater than 90 days ({iam_client.password_policy.max_age} days)."
            else:
                report.status = "FAIL"
                report.status_extended = "Password expiration is not set."
        else:
            report.status = "FAIL"
            report.status_extended = "Password policy cannot be found."
        findings.append(report)

        return findings

providers/aws/services/iam/iam_password_policy_expires_passwords_within_90_days_or_less/__init__.py METASEP

providers/aws/services/iam/iam_no_root_access_key/iam_no_root_access_key.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client


class iam_no_root_access_key(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.credential_report

        for user in response:
            if user["user"] == "<root_account>":
                report = Check_Report(self.metadata)
                report.region = iam_client.region
                report.resource_id = user["user"]
                report.resource_arn = user["arn"]
                if (
                    user["access_key_1_active"] == "false"
                    and user["access_key_2_active"] == "false"
                ):
                    report.status = "PASS"
                    report.status_extended = f"User {user['user']} has not access keys."
                elif (
                    user["access_key_1_active"] == "true"
                    and user["access_key_2_active"] == "true"
                ):
                    report.status = "FAIL"
                    report.status_extended = (
                        f"User {user['user']} has two active access keys."
                    )
                else:
                    report.status = "FAIL"
                    report.status_extended = (
                        f"User {user['user']} has one active access key."
                    )
                findings.append(report)

        return findings

providers/aws/services/iam/iam_no_root_access_key/__init__.py METASEP

providers/aws/services/iam/iam_disable_90_days_credentials/iam_disable_90_days_credentials.py METASEP
import datetime

from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client

maximum_expiration_days = 90


class iam_disable_90_days_credentials(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.users

        for user in response:
            report = Check_Report(self.metadata)
            report.region = iam_client.region
            report.resource_id = user.name
            report.resource_arn = user.arn
            if user.password_last_used and user.password_last_used != "":
                time_since_insertion = (
                    datetime.datetime.now()
                    - datetime.datetime.strptime(
                        str(user.password_last_used), "%Y-%m-%d %H:%M:%S+00:00"
                    )
                )
                if time_since_insertion.days > maximum_expiration_days:
                    report.status = "FAIL"
                    report.status_extended = f"User {user.name} has not logged into the console in the past 90 days."
                else:
                    report.status = "PASS"
                    report.status_extended = f"User {user.name} has logged into the console in the past 90 days."
            else:
                report.status = "PASS"

                report.status_extended = (
                    f"User {user.name} has not a console password or is unused."
                )
            # Append report
            findings.append(report)

        return findings

providers/aws/services/iam/iam_disable_90_days_credentials/__init__.py METASEP

providers/aws/services/iam/iam_disable_30_days_credentials/iam_disable_30_days_credentials.py METASEP
import datetime

from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client

maximum_expiration_days = 30


class iam_disable_30_days_credentials(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.users

        for user in response:
            report = Check_Report(self.metadata)
            report.resource_id = user.name
            report.resource_arn = user.arn
            report.region = iam_client.region
            if user.password_last_used and user.password_last_used != "":
                time_since_insertion = (
                    datetime.datetime.now()
                    - datetime.datetime.strptime(
                        str(user.password_last_used), "%Y-%m-%d %H:%M:%S+00:00"
                    )
                )
                if time_since_insertion.days > maximum_expiration_days:
                    report.status = "FAIL"
                    report.status_extended = f"User {user.name} has not logged into the console in the past 30 days."
                else:
                    report.status = "PASS"
                    report.status_extended = f"User {user.name} has logged into the console in the past 30 days."
            else:
                report.status = "PASS"
                report.status_extended = (
                    f"User {user.name} has not a console password or is unused."
                )

            # Append report
            findings.append(report)

        return findings

providers/aws/services/iam/iam_disable_30_days_credentials/__init__.py METASEP

providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage.py METASEP
import datetime

from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client

maximum_access_days = 1


class iam_avoid_root_usage(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.credential_report

        for user in response:
            if user["user"] == "<root_account>":
                report = Check_Report(self.metadata)
                report.region = iam_client.region
                report.resource_id = user["user"]
                report.resource_arn = user["arn"]
                if (
                    user["password_last_used"] != "no_information"
                    or user["access_key_1_last_used_date"] != "N/A"
                    or user["access_key_2_last_used_date"] != "N/A"
                ):
                    if user["password_last_used"] != "no_information":
                        days_since_accessed = (
                            datetime.datetime.now()
                            - datetime.datetime.strptime(
                                user["password_last_used"],
                                "%Y-%m-%dT%H:%M:%S+00:00",
                            )
                        ).days
                    elif user["access_key_1_last_used_date"] != "N/A":
                        days_since_accessed = (
                            datetime.datetime.now()
                            - datetime.datetime.strptime(
                                user["access_key_1_last_used_date"],
                                "%Y-%m-%dT%H:%M:%S+00:00",
                            )
                        ).days
                    elif user["access_key_2_last_used_date"] != "N/A":
                        days_since_accessed = (
                            datetime.datetime.now()
                            - datetime.datetime.strptime(
                                user["access_key_2_last_used_date"],
                                "%Y-%m-%dT%H:%M:%S+00:00",
                            )
                        ).days
                    if days_since_accessed > maximum_access_days:
                        report.status = "FAIL"
                        report.status_extended = f"Root user in the account was last accessed {days_since_accessed} days ago."
                    else:
                        report.status = "PASS"
                        report.status_extended = f"Root user in the account wasn't accessed in the last {maximum_access_days} days."
                else:
                    report.status = "PASS"
                    report.status_extended = f"Root user in the account wasn't accessed in the last {maximum_access_days} days."
                findings.append(report)

        return findings

providers/aws/services/iam/iam_avoid_root_usage/__init__.py METASEP

providers/aws/services/iam/iam_administrator_access_with_mfa/iam_administrator_access_with_mfa.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.iam.iam_service import iam_client


class iam_administrator_access_with_mfa(Check):
    def execute(self) -> Check_Report:
        findings = []
        response = iam_client.groups

        for group in response:
            report = Check_Report(self.metadata)
            report.resource_id = group.name
            report.resource_arn = group.arn
            report.region = iam_client.region
            if group.attached_policies:
                admin_policy = False
                for group_policy in group.attached_policies:
                    if (
                        group_policy["PolicyArn"]
                        == "arn:aws:iam::aws:policy/AdministratorAccess"
                    ):
                        admin_policy = True
                        # users in group are Administrators
                        if group.users:
                            for group_user in group.users:
                                for user in iam_client.credential_report:
                                    if (
                                        user["user"] == group_user.name
                                        and user["mfa_active"] == "false"
                                    ):
                                        report.status = "FAIL"
                                        report.status_extended = f"Group {group.name} provides administrator access to User {group_user.name} with MFA disabled."
                                        findings.append(report)
                                    elif (
                                        user["user"] == group_user.name
                                        and user["mfa_active"] == "true"
                                    ):
                                        report.status = "PASS"
                                        report.status_extended = f"Group {group.name} provides administrator access to User {group_user.name} with MFA enabled."
                                        findings.append(report)
                        else:
                            report.status = "PASS"
                            report.status_extended = f"Group {group.name} provides administrative access but does not have users."
                            findings.append(report)
                if not admin_policy:
                    report.status = "PASS"
                    report.status_extended = (
                        f"Group {group.name} provides non-administrative access."
                    )
                    findings.append(report)
            else:
                report.status = "PASS"
                report.status_extended = f"Group {group.name} has no policies."
                findings.append(report)

        return findings

providers/aws/services/iam/iam_administrator_access_with_mfa/__init__.py METASEP

providers/aws/services/ec2/lib/security_groups.py METASEP
from typing import Any


################## Security Groups
# Check if the security group ingress rule has public access to the check_ports using the protocol
def check_security_group(ingress_rule: Any, protocol: str, ports: list = []) -> bool:
    public_IPv4 = "0.0.0.0/0"
    public_IPv6 = "::/0"

    # Check for all traffic ingress rules regardless of the protocol
    if ingress_rule["IpProtocol"] == "-1" and (
        (
            "0.0.0.0/0" in str(ingress_rule["IpRanges"])
            or "::/0" in str(ingress_rule["Ipv6Ranges"])
        )
    ):
        return True

    # Check for specific ports in ingress rules
    if "FromPort" in ingress_rule:
        # All ports
        if ingress_rule["FromPort"] == 0 and ingress_rule["ToPort"] == 65535:
            return True

        # If there is a port range
        if ingress_rule["FromPort"] != ingress_rule["ToPort"]:
            # Calculate port range, adding 1
            diff = (ingress_rule["ToPort"] - ingress_rule["FromPort"]) + 1
            ingress_port_range = []
            for x in range(diff):
                ingress_port_range.append(int(ingress_rule["FromPort"]) + x)
        # If FromPort and ToPort are the same
        else:
            ingress_port_range = []
            ingress_port_range.append(int(ingress_rule["FromPort"]))

        # Test Security Group
        for port in ports:
            if (
                (
                    public_IPv4 in str(ingress_rule["IpRanges"])
                    or public_IPv6 in str(ingress_rule["Ipv6Ranges"])
                )
                and port in ingress_port_range
                and ingress_rule["IpProtocol"] == protocol
            ):
                return True
    return False

providers/aws/services/ec2/lib/network_acls.py METASEP
from typing import Any


################## Network ACLs
# Check if the network acls ingress rule has public access to the check_ports using the protocol
def check_network_acl(entry: Any, protocol: str, port: str, ip_version: str) -> bool:
    # For IPv4
    if ip_version == "IPv4":
        entry_value = "CidrBlock"
        public_ip = "0.0.0.0/0"
    # For IPv6
    elif ip_version == "IPv6":
        entry_value = "Ipv6CidrBlock"
        public_ip = "::/0"

    if (
        entry[entry_value] == public_ip
        and entry["RuleAction"] == "allow"
        and not entry["Egress"]
    ):
        if entry["Protocol"] == "-1" or (
            entry["PortRange"]["From"] == port
            and entry["PortRange"]["To"] == port
            and entry["Protocol"] == protocol
        ):
            return True

    return False

providers/aws/services/ec2/lib/__init__.py METASEP

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client
from providers.aws.services.ec2.lib.security_groups import check_security_group


class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483(Check):
    def execute(self):
        findings = []
        check_ports = [1521, 2483]
        for security_group in ec2_client.security_groups:
            public = False
            report = Check_Report(self.metadata)
            report.region = security_group.region
            # Loop through every security group's ingress rule and check it
            for ingress_rule in security_group.ingress_rules:
                public = check_security_group(ingress_rule, "tcp", check_ports)
                # Check
                if public:
                    report.status = "FAIL"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Oracle ports 1521 and 2483 open to the Internet."
                    report.resource_id = security_group.id
                else:
                    report.status = "PASS"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Oracle ports 1521 and 2483 open to the Internet."
                    report.resource_id = security_group.id
                findings.append(report)

        return findings

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483/__init__.py METASEP

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client
from providers.aws.services.ec2.lib.security_groups import check_security_group


class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306(Check):
    def execute(self):
        findings = []
        check_ports = [3306]
        for security_group in ec2_client.security_groups:
            public = False
            report = Check_Report(self.metadata)
            report.region = security_group.region
            # Loop through every security group's ingress rule and check it
            for ingress_rule in security_group.ingress_rules:
                public = check_security_group(ingress_rule, "tcp", check_ports)
                # Check
                if public:
                    report.status = "FAIL"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has the MySQL port 3306 open to the Internet."
                    report.resource_id = security_group.id
                else:
                    report.status = "PASS"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not MySQL port 3306 open to the Internet."
                    report.resource_id = security_group.id
                findings.append(report)

        return findings

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306/__init__.py METASEP

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client
from providers.aws.services.ec2.lib.security_groups import check_security_group


class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389(Check):
    def execute(self):
        findings = []
        check_ports = [3389]
        for security_group in ec2_client.security_groups:
            public = False
            report = Check_Report(self.metadata)
            report.region = security_group.region
            # Loop through every security group's ingress rule and check it
            for ingress_rule in security_group.ingress_rules:
                public = check_security_group(ingress_rule, "tcp", check_ports)
                # Check
                if public:
                    report.status = "FAIL"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Microsoft RDP port 3389 open to the Internet."
                    report.resource_id = security_group.id
                else:
                    report.status = "PASS"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Microsoft RDP port 3389 open to the Internet."
                    report.resource_id = security_group.id
                findings.append(report)

        return findings

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/__init__.py METASEP

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client
from providers.aws.services.ec2.lib.security_groups import check_security_group


class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22(Check):
    def execute(self):
        findings = []
        check_ports = [22]
        for security_group in ec2_client.security_groups:
            public = False
            report = Check_Report(self.metadata)
            report.region = security_group.region
            # Loop through every security group's ingress rule and check it
            for ingress_rule in security_group.ingress_rules:
                public = check_security_group(ingress_rule, "tcp", check_ports)
                # Check
                if public:
                    report.status = "FAIL"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has the SSH port 22 open to the Internet."
                    report.resource_id = security_group.id
                else:
                    report.status = "PASS"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not SSH port 22 open to the Internet."
                    report.resource_id = security_group.id
                findings.append(report)

        return findings

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/__init__.py METASEP

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import check_security_group, ec2_client


class ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21(Check):
    def execute(self):
        findings = []
        check_ports = [20, 21]
        for security_group in ec2_client.security_groups:
            public = False
            report = Check_Report(self.metadata)
            report.region = security_group.region
            # Loop through every security group's ingress rule and check it
            for ingress_rule in security_group.ingress_rules:
                public = check_security_group(ingress_rule, "tcp", check_ports)
                # Check
                if public:
                    report.status = "FAIL"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has FTP ports 20 and 21 open to the Internet."
                    report.resource_id = security_group.id
                else:
                    report.status = "PASS"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not FPT ports 20 and 21 open to the Internet."
                    report.resource_id = security_group.id
                findings.append(report)

        return findings

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21/__init__.py METASEP

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client
from providers.aws.services.ec2.lib.security_groups import check_security_group


class ec2_securitygroup_allow_ingress_from_internet_to_any_port(Check):
    def execute(self):
        findings = []
        for security_group in ec2_client.security_groups:
            public = False
            report = Check_Report(self.metadata)
            report.region = security_group.region
            # Loop through every security group's ingress rule and check it
            for ingress_rule in security_group.ingress_rules:
                public = check_security_group(ingress_rule, "-1")
                # Check
                if public:
                    report.status = "FAIL"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has all ports open to the Internet."
                    report.resource_id = security_group.id
                else:
                    report.status = "PASS"
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not all ports open to the Internet."
                    report.resource_id = security_group.id
                findings.append(report)

        return findings

providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/__init__.py METASEP

providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_3389/ec2_networkacl_allow_ingress_tcp_port_3389.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client
from providers.aws.services.ec2.lib.network_acls import check_network_acl


class ec2_networkacl_allow_ingress_tcp_port_3389(Check):
    def execute(self):
        findings = []
        tcp_protocol = "6"
        check_port = 3389
        for network_acl in ec2_client.network_acls:
            public = False
            report = Check_Report(self.metadata)
            report.region = network_acl.region
            for entry in network_acl.entries:
                # For IPv4
                if "CidrBlock" in entry:
                    public = check_network_acl(entry, tcp_protocol, check_port, "IPv4")
                # For IPv6
                if "Ipv6CidrBlock" in entry:
                    public = check_network_acl(entry, tcp_protocol, check_port, "IPv6")
            if not public:
                report.status = "PASS"
                report.status_extended = f"Network ACL {network_acl.id} has not Microsoft RDP port 3389 open to the Internet."
                report.resource_id = network_acl.id
            else:
                report.status = "FAIL"
                report.status_extended = f"Network ACL {network_acl.id} has Microsoft RDP port 3389 open to the Internet."
                report.resource_id = network_acl.id
            findings.append(report)

        return findings

providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_3389/__init__.py METASEP

providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_22/ec2_networkacl_allow_ingress_tcp_port_22.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client
from providers.aws.services.ec2.lib.network_acls import check_network_acl


class ec2_networkacl_allow_ingress_tcp_port_22(Check):
    def execute(self):
        findings = []
        tcp_protocol = "6"
        check_port = 22
        for network_acl in ec2_client.network_acls:
            public = False
            report = Check_Report(self.metadata)
            report.region = network_acl.region
            for entry in network_acl.entries:
                # For IPv4
                if "CidrBlock" in entry:
                    public = check_network_acl(entry, tcp_protocol, check_port, "IPv4")
                # For IPv6
                if "Ipv6CidrBlock" in entry:
                    public = check_network_acl(entry, tcp_protocol, check_port, "IPv6")
                # If some entry allows it, that ACL is not securely configured
                if public:
                    break
            if not public:
                report.status = "PASS"
                report.status_extended = f"Network ACL {network_acl.id} has not SSH port 22 open to the Internet."
                report.resource_id = network_acl.id
            else:
                report.status = "FAIL"
                report.status_extended = f"Network ACL {network_acl.id} has SSH port 22 open to the Internet."
                report.resource_id = network_acl.id
            findings.append(report)

        return findings

providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_22/__init__.py METASEP

providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client


class ec2_instance_public_ip(Check):
    def execute(self):
        findings = []
        for instance in ec2_client.instances:
            report = Check_Report(self.metadata)
            report.region = instance.region
            if instance.public_ip:
                report.status = "FAIL"
                report.status_extended = f"EC2 instance {instance.id} has a Public IP: {instance.public_ip} ({instance.public_dns})."
                report.resource_id = instance.id
            else:
                report.status = "PASS"
                report.status_extended = (
                    f"EC2 instance {instance.id} has not a Public IP."
                )
                report.resource_id = instance.id
            findings.append(report)

        return findings

providers/aws/services/ec2/ec2_instance_public_ip/__init__.py METASEP

providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client


class ec2_ebs_snapshots_encrypted(Check):
    def execute(self):
        findings = []
        for snapshot in ec2_client.snapshots:
            report = Check_Report(self.metadata)
            report.region = snapshot.region
            if snapshot.encrypted:
                report.status = "PASS"
                report.status_extended = f"EBS Snapshot {snapshot.id} is encrypted"
                report.resource_id = snapshot.id
            else:
                report.status = "FAIL"
                report.status_extended = f"EBS Snapshot {snapshot.id} is unencrypted"
                report.resource_id = snapshot.id
            findings.append(report)

        return findings

providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/__init__.py METASEP

providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.py METASEP
from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import ec2_client


class ec2_ebs_public_snapshot(Check):
    def execute(self):
        findings = []
        for snapshot in ec2_client.snapshots:
            report = Check_Report(self.metadata)
            report.region = snapshot.region
            if not snapshot.public:
                report.status = "PASS"
                report.status_extended = f"EBS Snapshot {snapshot.id} is not Public"
                report.resource_id = snapshot.id
            else:
                report.status = "FAIL"
                report.status_extended = (
                    f"EBS Snapshot {snapshot.id} is currently Public"
                )
                report.resource_id = snapshot.id
            findings.append(report)

        return findings

providers/aws/services/ec2/ec2_ebs_public_snapshot/__init__.py METASEP

providers/aws/services/vpc/check_extra790 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra790="7.90"
CHECK_TITLE_extra790="[extra790] Find trust boundaries in VPC endpoint services allowlisted principles"
CHECK_SCORED_extra790="NOT_SCORED"
CHECK_CIS_LEVEL_extra790="EXTRA"
CHECK_SEVERITY_extra790="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra790="AwsEc2Vpc"
CHECK_ALTERNATE_extra790="extra790"
CHECK_SERVICENAME_extra790="vpc"
CHECK_RISK_extra790='Account VPC could be linked to other accounts.'
CHECK_REMEDIATION_extra790='In multi Account environments identify untrusted links. Check trust chaining and dependencies between accounts.'
CHECK_DOC_extra790='https://github.com/toniblyx/prowler/#trust-boundaries-checks'
CHECK_CAF_EPIC_extra790='Infrastructure Security'

extra790(){
    TRUSTED_ACCOUNT_IDS=$( echo "${ACCOUNT_NUM} ${GROUP_TRUSTBOUNDARIES_TRUSTED_ACCOUNT_IDS}" | xargs )

    for regx in ${REGIONS}; do
        ENDPOINT_SERVICES_IDS=$(${AWSCLI} ec2 describe-vpc-endpoint-services \
                                ${PROFILE_OPT} \
                                --query "ServiceDetails[?Owner=='${ACCOUNT_NUM}'].ServiceId" \
                                --region ${regx} \
                                --output text | xargs 2>&1)
    if [[ $(echo "$ENDPOINT_SERVICES_IDS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe VPC endpoint services" "$regx"
        continue
    fi

        for ENDPOINT_SERVICE_ID in ${ENDPOINT_SERVICES_IDS}; do
            ENDPOINT_PERMISSIONS_LIST=$(${AWSCLI} ec2 describe-vpc-endpoint-service-permissions \
                                        ${PROFILE_OPT} \
                                        --service-id ${ENDPOINT_SERVICE_ID} \
                                        --query "AllowedPrincipals[*].Principal" \
                                        --region ${regx} \
                                        --output text | xargs
                                        )

            for ENDPOINT_PERMISSION in ${ENDPOINT_PERMISSIONS_LIST}; do
                # Take only account id from ENDPOINT_PERMISSION: arn:aws:iam::965406151242:root
                ENDPOINT_PERMISSION_ACCOUNT_ID=$(echo ${ENDPOINT_PERMISSION} | cut -d':' -f5 | xargs)

                for ACCOUNT_ID in ${TRUSTED_ACCOUNT_IDS}; do
                    if [[ "${ACCOUNT_ID}" == "${ENDPOINT_PERMISSION_ACCOUNT_ID}" ]]; then
                        textPass "${regx}: Found trusted account in VPC endpoint service permission ${ENDPOINT_PERMISSION}" "${regx}"
                        # Algorithm:
                        # Remove all trusted ACCOUNT_IDs from ENDPOINT_PERMISSIONS_LIST.
                        # As a result, the ENDPOINT_PERMISSIONS_LIST finally contains only unknown/untrusted account ids.
                        ENDPOINT_PERMISSIONS_LIST=("${ENDPOINT_PERMISSIONS_LIST[@]/$ENDPOINT_PERMISSION}")
                    fi
                done
            done

            for UNTRUSTED_PERMISSION in ${ENDPOINT_PERMISSIONS_LIST}; do
                textFail "${regx}: Found untrusted account in VPC endpoint service permission ${UNTRUSTED_PERMISSION}" "${regx}" "${UNTRUSTED_PERMISSION}"
            done
        done
    done
}

providers/aws/services/vpc/check_extra789 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra789="7.89"
CHECK_TITLE_extra789="[extra789] Find trust boundaries in VPC endpoint services connections"
CHECK_SCORED_extra789="NOT_SCORED"
CHECK_CIS_LEVEL_extra789="EXTRA"
CHECK_SEVERITY_extra789="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra789="AwsEc2Vpc"
CHECK_ALTERNATE_extra789="extra789"
CHECK_SERVICENAME_extra789="vpc"
CHECK_RISK_extra789='Account VPC could be linked to other accounts.'
CHECK_REMEDIATION_extra789='In multi Account environments identify untrusted links. Check trust chaining and dependencies between accounts.'
CHECK_DOC_extra789='https://github.com/toniblyx/prowler/#trust-boundaries-checks'
CHECK_CAF_EPIC_extra789='Infrastructure Security'

extra789(){
    TRUSTED_ACCOUNT_IDS=$( echo "${ACCOUNT_NUM} ${GROUP_TRUSTBOUNDARIES_TRUSTED_ACCOUNT_IDS}" | xargs )

    for regx in ${REGIONS}; do
        ENDPOINT_SERVICES_IDS=$(${AWSCLI} ec2 describe-vpc-endpoint-services \
                                ${PROFILE_OPT} \
                                --query "ServiceDetails[?Owner=='${ACCOUNT_NUM}'].ServiceId" \
                                --region ${regx} \
                                --output text | xargs 2>&1)
    if [[ $(echo "$ENDPOINT_SERVICES_IDS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe VPC endpoint services" "$regx"
        continue
    fi

        for ENDPOINT_SERVICE_ID in ${ENDPOINT_SERVICES_IDS}; do

            ENDPOINT_CONNECTION_LIST=$(${AWSCLI} ec2 describe-vpc-endpoint-connections \
                                        ${PROFILE_OPT} \
                                        --query "VpcEndpointConnections[?VpcEndpointState=='available'].VpcEndpointOwner" \
                                        --region ${regx} \
                                        --output text | xargs
                                        )

            for ENDPOINT_CONNECTION in ${ENDPOINT_CONNECTION_LIST}; do
                for ACCOUNT_ID in ${TRUSTED_ACCOUNT_IDS}; do
                    if [[ "${ACCOUNT_ID}" == "${ENDPOINT_CONNECTION}" ]]; then
                        textPass "${regx}: Found trusted account in VPC endpoint service connection ${ENDPOINT_CONNECTION}" "${regx}" "${ENDPOINT_CONNECTION}"
                        # Algorithm:
                        # Remove all trusted ACCOUNT_IDs from ENDPOINT_CONNECTION_LIST.
                        # As a result, the ENDPOINT_CONNECTION_LIST finally contains only unknown/untrusted account ids.
                        ENDPOINT_CONNECTION_LIST=("${ENDPOINT_CONNECTION_LIST[@]/$ENDPOINT_CONNECTION}") # remove hit from allowlist
                    fi
                done
            done

            for UNTRUSTED_CONNECTION in ${ENDPOINT_CONNECTION_LIST}; do
                textFail "${regx}: Found untrusted account in VPC endpoint service connection ${UNTRUSTED_CONNECTION}" "${regx}" "${ENDPOINT_CONNECTION}"
            done
        done
    done
}

providers/aws/services/vpc/check44 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check44="4.4"
CHECK_TITLE_check44="[check44] Ensure routing tables for VPC peering are \"least access\""
CHECK_SCORED_check44="NOT_SCORED"
CHECK_CIS_LEVEL_check44="LEVEL2"
CHECK_SEVERITY_check44="Medium"
CHECK_ASFF_TYPE_check44="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check44="AwsEc2Vpc"
CHECK_ALTERNATE_check404="check44"
CHECK_SERVICENAME_check44="vpc"
CHECK_RISK_check44='Being highly selective in peering routing tables is a very effective way of minimizing the impact of breach as resources outside of these routes are inaccessible to the peered VPC.'
CHECK_REMEDIATION_check44='Review routing tables of peered VPCs for whether they route all subnets of each VPC and whether that is necessary to accomplish the intended purposes for peering the VPCs.'
CHECK_DOC_check44='https://docs.aws.amazon.com/vpc/latest/peering/peering-configurations-partial-access.html'
CHECK_CAF_EPIC_check44='Infrastructure Security'

check44(){
  # "Ensure routing tables for VPC peering are \"least access\" (Not Scored)"
  for regx in $REGIONS; do
    LIST_OF_VPCS_PEERING_CONNECTIONS=$($AWSCLI ec2 describe-vpc-peering-connections --output text $PROFILE_OPT --region $regx --query 'VpcPeeringConnections[*].VpcPeeringConnectionId' 2>&1| sort | paste -s -d" " - )
    if [[ $(echo "$LIST_OF_VPCS_PEERING_CONNECTIONS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe vpc peering connections" "$regx"
        continue
    fi
    if [[ $LIST_OF_VPCS_PEERING_CONNECTIONS ]];then
      textInfo "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS - review routing tables" "$regx" "$LIST_OF_VPCS_PEERING_CONNECTIONS"
      #LIST_OF_VPCS=$($AWSCLI ec2 describe-vpcs $PROFILE_OPT --region $regx --query 'Vpcs[*].VpcId' --output text)
      #aws ec2 describe-route-tables --filter "Name=vpc-id,Values=vpc-0213e864" --query "RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes, AssociatedSubnets:Associations[*].SubnetId}" $PROFILE_OPT --region $regx
      # for vpc in $LIST_OF_VPCS; do
      #   VPCS_WITH_PEERING=$($AWSCLI ec2 describe-route-tables --filter "Name=vpc-id,Values=$vpc" $PROFILE_OPT --region $regx --query "RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes, AssociatedSubnets:Associations[*].SubnetId}" |grep GatewayId|grep pcx-)
      # done
      #echo $VPCS_WITH_PEERING
    else
      textPass "$regx: No VPC peering found" "$regx" "$LIST_OF_VPCS_PEERING_CONNECTIONS"
    fi
  done
}

providers/aws/services/vpc/check314 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name VPCNetworkConfigChanges \
#     --filter-pattern '{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }' \
#     --metric-transformations metricName=VpcEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name VPCNetworkConfigChangesAlarm \
#     --alarm-description "Triggered by AWS VPC(s) environment config changes." \
#     --metric-name VpcEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check314="3.14"
CHECK_TITLE_check314="[check314] Ensure a log metric filter and alarm exist for VPC changes"
CHECK_SCORED_check314="SCORED"
CHECK_CIS_LEVEL_check314="LEVEL1"
CHECK_SEVERITY_check314="Medium"
CHECK_ASFF_TYPE_check314="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check314="AwsCloudTrailTrail"
CHECK_ALTERNATE_check314="check314"
CHECK_SERVICENAME_check314="vpc"
CHECK_RISK_check314='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check314='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check314='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check314='Logging and Monitoring'

check314(){
  check3x '\$\.eventName\s*=\s*CreateVpc.+\$\.eventName\s*=\s*DeleteVpc.+\$\.eventName\s*=\s*ModifyVpcAttribute.+\$\.eventName\s*=\s*AcceptVpcPeeringConnection.+\$\.eventName\s*=\s*CreateVpcPeeringConnection.+\$\.eventName\s*=\s*DeleteVpcPeeringConnection.+\$\.eventName\s*=\s*RejectVpcPeeringConnection.+\$\.eventName\s*=\s*AttachClassicLinkVpc.+\$\.eventName\s*=\s*DetachClassicLinkVpc.+\$\.eventName\s*=\s*DisableVpcClassicLink.+\$\.eventName\s*=\s*EnableVpcClassicLink'
}

providers/aws/services/vpc/check313 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name RouteTableConfigChanges \
#     --filter-pattern '{ ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DisassociateRouteTable) }' \
#     --metric-transformations metricName=RouteTableEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name RouteTableConfigChangesAlarm \
#     --alarm-description "Triggered by AWS Route Table config changes." \
#     --metric-name RouteTableEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check313="3.13"
CHECK_TITLE_check313="[check313] Ensure a log metric filter and alarm exist for route table changes"
CHECK_SCORED_check313="SCORED"
CHECK_CIS_LEVEL_check313="LEVEL1"
CHECK_SEVERITY_check313="Medium"
CHECK_ASFF_TYPE_check313="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check313="AwsCloudTrailTrail"
CHECK_ALTERNATE_check313="check313"
CHECK_SERVICENAME_check313="vpc"
CHECK_RISK_check313='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check313='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check313='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check313='Logging and Monitoring'

check313(){
  check3x '\$\.eventName\s*=\s*CreateRoute.+\$\.eventName\s*=\s*CreateRouteTable.+\$\.eventName\s*=\s*ReplaceRoute.+\$\.eventName\s*=\s*ReplaceRouteTableAssociation.+\$\.eventName\s*=\s*DeleteRouteTable.+\$\.eventName\s*=\s*DeleteRoute.+\$\.eventName\s*=\s*DisassociateRouteTable'
}

providers/aws/services/vpc/check312 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name VPCGatewayConfigChanges \
#     --filter-pattern '{ ($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }' \
#     --metric-transformations metricName=GatewayEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name VPCGatewayConfigChangesAlarm \
#     --alarm-description "Triggered by VPC Customer/Internet Gateway changes." \
#     --metric-name GatewayEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check312="3.12"
CHECK_TITLE_check312="[check312] Ensure a log metric filter and alarm exist for changes to network gateways"
CHECK_SCORED_check312="SCORED"
CHECK_CIS_LEVEL_check312="LEVEL1"
CHECK_SEVERITY_check312="Medium"
CHECK_ASFF_TYPE_check312="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check312="AwsCloudTrailTrail"
CHECK_ALTERNATE_check312="check312"
CHECK_SERVICENAME_check312="vpc"
CHECK_RISK_check312='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check312='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check312='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check312='Logging and Monitoring'

check312(){
  check3x '\$\.eventName\s*=\s*CreateCustomerGateway.+\$\.eventName\s*=\s*DeleteCustomerGateway.+\$\.eventName\s*=\s*AttachInternetGateway.+\$\.eventName\s*=\s*CreateInternetGateway.+\$\.eventName\s*=\s*DeleteInternetGateway.+\$\.eventName\s*=\s*DetachInternetGateway'
}

providers/aws/services/vpc/check311 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name NetworkACLConfigChanges \
#     --filter-pattern '{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }' \
#     --metric-transformations metricName=NetworkAclEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name NetworkACLConfigChangesAlarm \
#     --alarm-description "Triggered by AWS Network ACL(s) config changes." \
#     --metric-name NetworkAclEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check311="3.11"
CHECK_TITLE_check311="[check311] Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL)"
CHECK_SCORED_check311="SCORED"
CHECK_CIS_LEVEL_check311="LEVEL2"
CHECK_SEVERITY_check311="Medium"
CHECK_ASFF_TYPE_check311="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check311="AwsCloudTrailTrail"
CHECK_ALTERNATE_check311="check311"
CHECK_SERVICENAME_check311="vpc"
CHECK_RISK_check311='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check311='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check311='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check311='Logging and Monitoring'

check311(){
  check3x '\$\.eventName\s*=\s*CreateNetworkAcl.+\$\.eventName\s*=\s*CreateNetworkAclEntry.+\$\.eventName\s*=\s*DeleteNetworkAcl.+\$\.eventName\s*=\s*DeleteNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclAssociation'
}

providers/aws/services/vpc/check29 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check29="2.9"
CHECK_TITLE_check29="[check29] Ensure VPC Flow Logging is Enabled in all VPCs"
CHECK_SCORED_check29="SCORED"
CHECK_CIS_LEVEL_check29="LEVEL2"
CHECK_SEVERITY_check29="Medium"
CHECK_ASFF_TYPE_check29="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check29="AwsEc2Vpc"
CHECK_ALTERNATE_check209="check29"
CHECK_ASFF_COMPLIANCE_TYPE_check29="ens-op.mon.1.aws.flow.1"
CHECK_SERVICENAME_check29="vpc"
CHECK_RISK_check29='VPC Flow Logs provide visibility into network traffic that traverses the VPC and can be used to detect anomalous traffic or insight during security workflows.'
CHECK_REMEDIATION_check29='It is recommended that VPC Flow Logs be enabled for packet "Rejects" for VPCs. '
CHECK_DOC_check29='http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/flow-logs.html '
CHECK_CAF_EPIC_check29='Logging and Monitoring'

check29(){
  # "Ensure VPC Flow Logging is Enabled in all VPCs (Scored)"
  for regx in $REGIONS; do
    AVAILABLE_VPC=$($AWSCLI ec2 describe-vpcs $PROFILE_OPT --region $regx --query 'Vpcs[?State==`available`].VpcId' --output text 2>&1)
    if [[ $(echo "$AVAILABLE_VPC" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
      textInfo "$regx: Access Denied trying to describe VPCs" "$regx" "$vpcx"
      continue
    fi
    for vpcx in $AVAILABLE_VPC; do
      CHECK_FL=$($AWSCLI ec2 describe-flow-logs $PROFILE_OPT --region $regx --filter Name="resource-id",Values="${vpcx}" --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].FlowLogId' --output text 2>&1)
      if [[ $(echo "$CHECK_FL" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe flow logs in VPC $vpcx" "$regx" "$vpcx"
        continue
      fi
      if [[ $CHECK_FL ]]; then
        for FL in $CHECK_FL; do
          textPass "$regx: VPC $vpcx VPCFlowLog is enabled for LogGroupName: $FL" "$regx" "$vpcx"
        done
      else
        textFail "$regx: VPC $vpcx VPCFlowLog is disabled" "$regx" "$vpcx"
      fi
    done
  done
}

providers/aws/services/trustedadvisor/check_extra726 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra726="7.26"
CHECK_TITLE_extra726="[extra726] Check Trusted Advisor for errors and warnings"
CHECK_SCORED_extra726="NOT_SCORED"
CHECK_CIS_LEVEL_extra726="EXTRA"
CHECK_SEVERITY_extra726="Medium"
CHECK_ALTERNATE_check726="extra726"
CHECK_SERVICENAME_extra726="trustedadvisor"
CHECK_RISK_extra726='Improve the security of your application by closing gaps; enabling various AWS security features; and examining your permissions.'
CHECK_REMEDIATION_extra726='Review and act upon its recommendations.'
CHECK_DOC_extra726='https://aws.amazon.com/premiumsupport/technology/trusted-advisor/best-practice-checklist/'
CHECK_CAF_EPIC_extra726='IAM'

extra726(){
  trap "exit" INT
  # forcing REGION if not set will be us-east-1 region only since support only works in that region
  TA_CHECKS_ID=$($AWSCLI support describe-trusted-advisor-checks --language en $PROFILE_OPT --region $REGION --query checks[*].id  --output text 2>&1)
  if [[ $(echo "$TA_CHECKS_ID" | grep SubscriptionRequiredException) ]]; then
    textInfo "$REGION: Trusted Advisor requires AWS Premium Support Subscription" "$REGION"
    return
  fi
  if [[ $(echo "$TA_CHECKS_ID" | grep "Could not connect") ]]; then
    textInfo "$REGION: Trusted Advisor not available in this region" "$REGION"
    return
  fi

  for checkid in $TA_CHECKS_ID; do
    TA_CHECKS_NAME=$($AWSCLI support describe-trusted-advisor-checks --language en $PROFILE_OPT --region $REGION --query "checks[?id==\`$checkid\`].{name:name}[*]" --output text)
    QUERY_TA_CHECK_RESULT=$($AWSCLI support describe-trusted-advisor-check-result --check-id $checkid --language en $PROFILE_OPT --region $REGION --query 'result.status' --output text)
    # Possible results - https://docs.aws.amazon.com/cli/latest/reference/support/describe-trusted-advisor-check-result.html
    case "$QUERY_TA_CHECK_RESULT" in
      "ok")
        textPass "$REGION: Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT" "$REGION" "$TA_CHECKS_NAME"
        ;;
      "error")
        textFail "$REGION: Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT" "$REGION" "$TA_CHECKS_NAME"
        ;;
      "warning")
        textInfo "$REGION: Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT" "$REGION" "$TA_CHECKS_NAME"
        ;;
      "not_available")
        textInfo "$REGION: Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT" "$REGION" "$TA_CHECKS_NAME"
        ;;
      "*")
        textFail "$REGION: Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT" "$REGION" "$TA_CHECKS_NAME"
        ;;
    esac
  done
}

providers/aws/services/support/check118 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check118="1.18"
CHECK_TITLE_check118="[check118] Ensure security contact information is registered"
CHECK_SCORED_check118="NOT_SCORED"
CHECK_CIS_LEVEL_check118="LEVEL1"
CHECK_SEVERITY_check118="Medium"
CHECK_ASFF_TYPE_check118="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check118="check118"
CHECK_SERVICENAME_check118="support"
CHECK_RISK_check118='AWS provides customers with the option of specifying the contact information for accounts security team. It is recommended that this information be provided. Specifying security-specific contact information will help ensure that security advisories sent by AWS reach the team in your organization that is best equipped to respond to them.'
CHECK_REMEDIATION_check118='Go to the My Account section and complete alternate contacts.'
CHECK_DOC_check118='https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/manage-account-payment.html'
CHECK_CAF_EPIC_check118='IAM'

check118(){
  if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
    textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
  else
    # "Ensure security contact information is registered (Scored)"
    # No command available
    textInfo "No command available for check 1.18. See section 1.18 on the CIS Benchmark guide for details."
  fi
}

providers/aws/services/support/check117 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check117="1.17"
CHECK_TITLE_check117="[check117] Maintain current contact details"
CHECK_SCORED_check117="NOT_SCORED"
CHECK_CIS_LEVEL_check117="LEVEL1"
CHECK_SEVERITY_check117="Medium"
CHECK_ASFF_TYPE_check117="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check117="check117"
CHECK_SERVICENAME_check117="support"
CHECK_RISK_check117='Ensure contact email and telephone details for AWS accounts are current and map to more than one individual in your organization. An AWS account supports a number of contact details; and AWS will use these to contact the account owner if activity judged to be in breach of Acceptable Use Policy. If an AWS account is observed to be behaving in a prohibited or suspicious manner; AWS will attempt to contact the account owner by email and phone using the contact details listed. If this is unsuccessful and the account behavior needs urgent mitigation; proactive measures may be taken; including throttling of traffic between the account exhibiting suspicious behavior and the AWS API endpoints and the Internet. This will result in impaired service to and from the account in question.'
CHECK_REMEDIATION_check117='Using the Billing and Cost Management console complete contact details.'
CHECK_DOC_check117='https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/manage-account-payment.html#contact-info'
CHECK_CAF_EPIC_check117='IAM'

check117(){
  if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
    textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
  else
    # "Maintain current contact details (Scored)"
    # No command available
    textInfo "No command available for check 1.17. See section 1.17 on the CIS Benchmark guide for details."
  fi
}

providers/aws/services/support/check115 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check115="1.15"
CHECK_TITLE_check115="[check115] Ensure security questions are registered in the AWS account"
CHECK_SCORED_check115="NOT_SCORED"
CHECK_CIS_LEVEL_check115="LEVEL1"
CHECK_SEVERITY_check115="Medium"
CHECK_ASFF_TYPE_check115="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check115="check115"
CHECK_SERVICENAME_check115="support"
CHECK_RISK_check115='The AWS support portal allows account owners to establish security questions that can be used to authenticate individuals calling AWS customer service for support. It is recommended that security questions be established. When creating a new AWS account a default super user is automatically created. This account is referred to as the "root" account. It is recommended that the use of this account be limited and highly controlled. During events in which the root password is no longer accessible or the MFA token associated with root is lost/destroyed it is possible through authentication using secret questions and associated answers to recover root login access.'
CHECK_REMEDIATION_check115='Login as root account and from My Account configure Security questions.'
CHECK_DOC_check115='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys_retrieve.html'
CHECK_CAF_EPIC_check115='IAM'

check115(){
  if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
    textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
  else
    # "Ensure security questions are registered in the AWS account (Not Scored)"
    textInfo "${REGION}: No command available for check 1.15. Login to the AWS Console as root & click on the Account. Name -> My Account -> Configure Security Challenge Questions."
  fi
}

providers/aws/services/ssm/check_extra7141 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7141="7.141"
CHECK_TITLE_extra7141="[extra7141] Find secrets in SSM Documents"
CHECK_SCORED_extra7141="NOT_SCORED"
CHECK_CIS_LEVEL_extra7141="EXTRA"
CHECK_SEVERITY_extra7141="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra7141="AwsSsmDocument"
CHECK_ALTERNATE_check7141="extra7141"
CHECK_SERVICENAME_extra7141="ssm"
CHECK_RISK_extra7141='Secrets hardcoded into SSM Documents by malware and bad actors to gain lateral access to other services.'
CHECK_REMEDIATION_extra7141='Implement automated detective control (e.g. using tools like Prowler) to scan accounts for passwords and secrets. Use Secrets Manager service to store and retrieve passwords and secrets.'
CHECK_DOC_extra7141='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-secret-generatesecretstring.html'
CHECK_CAF_EPIC_extra7141='IAM'

extra7141(){
  SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM-$PROWLER_START_TIME"
  if [[ ! -d "${SECRETS_TEMP_FOLDER}" ]]; then
    # this folder is deleted once this check is finished
    mkdir "${SECRETS_TEMP_FOLDER}"
  fi

  for regx in ${REGIONS}; do
    CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
    if [[ $? -eq 241 ]]; then
      textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
    else
      SSM_DOCS=$("${AWSCLI}" ${PROFILE_OPT} --region "${regx}" ssm list-documents --filters 'Key=Owner,Values=Self' --query 'DocumentIdentifiers[].Name' --output text 2>&1)
      if [[ $(echo "${SSM_DOCS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "${regx}: Access Denied trying to list documents" "${regx}"
          continue
      fi
      if [[ ${SSM_DOCS} ]];then
        for ssmdoc in ${SSM_DOCS}; do
          SSM_DOC_FILE="${SECRETS_TEMP_FOLDER}/extra7141-${ssmdoc}-${regx}-content.txt"
          "${AWSCLI}" ${PROFILE_OPT} --region "${regx}" ssm get-document --name "${ssmdoc}" --output text --document-format JSON > "${SSM_DOC_FILE}" 2>&1
          if [[ $(grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' "${SSM_DOC_FILE}") ]]; then
              textInfo "${regx}: Access Denied trying to get document" "${regx}"
              continue
          fi
          FINDINGS=$(secretsDetector file "${SSM_DOC_FILE}")
          if [[ "${FINDINGS}" -eq 0 ]]; then
            textPass "${regx}: No secrets found in SSM Document ${ssmdoc}" "${regx}" "${ssmdoc}"
            # delete file if nothing interesting is there
            rm -f "${SSM_DOC_FILE}"
          else
            textFail "${regx}: Potential secret found SSM Document ${ssmdoc}" "${regx}" "${ssmdoc}"
            # delete file to not leave trace, user must look at the CFN Stack
            rm -f "${SSM_DOC_FILE}"
          fi
        done
      else
        textInfo "${regx}: No SSM Document found." "${regx}"
      fi
    fi
  done
  rm -rf "${SECRETS_TEMP_FOLDER}"
}

providers/aws/services/ssm/check_extra7140 METASEP
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7140="7.140"
CHECK_TITLE_extra7140="[extra7140] Check if there are SSM Documents set as public"
CHECK_SCORED_extra7140="NOT_SCORED"
CHECK_CIS_LEVEL_extra7140="EXTRA"
CHECK_SEVERITY_extra7140="High"
CHECK_ASFF_RESOURCE_TYPE_extra7140="AwsSsmDocument"
CHECK_ALTERNATE_check7140="extra7140"
CHECK_SERVICENAME_extra7140="ssm"
CHECK_RISK_extra7140='SSM Documents may contain private information or even secrets and tokens.'
CHECK_REMEDIATION_extra7140='Carefully review the contents of the document before is shared. Enable SSM Block public sharing for documents.'
CHECK_DOC_extra7140='https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-before-you-share.html'
CHECK_CAF_EPIC_extra7140='Data Protection'
extra7140(){

  for regx in $REGIONS; do
    SSM_DOCS=$($AWSCLI $PROFILE_OPT --region $regx ssm list-documents --filters Key=Owner,Values=Self --query DocumentIdentifiers[].Name --output text 2>&1)
    if [[ $(echo "$SSM_DOCS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list documents" "$regx"
        continue
    fi
    if [[ $SSM_DOCS ]];then
      for ssmdoc in $SSM_DOCS; do
        SSM_DOC_SHARED_ALL=$($AWSCLI $PROFILE_OPT --region $regx ssm describe-document-permission --name "$ssmdoc" --permission-type "Share" --query AccountIds[] --output text | grep all)
        if [[ $SSM_DOC_SHARED_ALL ]];then
          textFail "$regx: SSM Document $ssmdoc is public." "$regx" "$ssmdoc"
        else
          textPass "$regx: SSM Document $ssmdoc is not public." "$regx" "$ssmdoc"
        fi
      done
    else
      textInfo "$regx: No SSM Document found." "$regx"
    fi
  done
}

providers/aws/services/ssm/check_extra7127 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7127="7.127"
CHECK_TITLE_extra7127="[extra7127] Check if EC2 instances managed by Systems Manager are compliant with patching requirements"
CHECK_SCORED_extra7127="NOT_SCORED"
CHECK_CIS_LEVEL_extra7127="EXTRA"
CHECK_SEVERITY_extra7127="High"
CHECK_ASFF_RESOURCE_TYPE_extra7127="AwsEc2Instance"
CHECK_ASFF_TYPE_extra7127="Software and Configuration Checks/ENS op.exp.4.aws.sys.1"
CHECK_ALTERNATE_check7127="extra7127"
CHECK_ASFF_COMPLIANCE_TYPE_extra7127="ens-op.exp.1.aws.sys.1 ens-op.exp.4.aws.sys.1"
CHECK_SERVICENAME_extra7127="ssm"
CHECK_RISK_extra7127='Without the most recent security patches your system is potentially vulnerable to cyberattacks. Even the best-designed software can not anticipate every future threat to cybersecurity. Poor patch management can leave an organizations data exposed subjecting them to malware and ransomware attacks.'
CHECK_REMEDIATION_extra7127='Consider using SSM in all accounts and services to at least monitor for missing patches on servers. Use a robust process to apply security fixes as soon as they are made available. Patch compliance data from Patch Manager can be sent to AWS Security Hub to centralize security issues.'
CHECK_DOC_extra7127='https://docs.aws.amazon.com/systems-manager/latest/userguide/patch-compliance-identify.html'
CHECK_CAF_EPIC_extra7127='Infrastructure Security'

extra7127(){
  for regx in $REGIONS; do
    NON_COMPLIANT_SSM_MANAGED_INSTANCES=$($AWSCLI ssm list-resource-compliance-summaries $PROFILE_OPT --region $regx --filters Key=Status,Values=NON_COMPLIANT --query ResourceComplianceSummaryItems[].ResourceId --output text 2>&1)
    if [[ $(echo "$NON_COMPLIANT_SSM_MANAGED_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list resource compliance summaries" "$regx"
        continue
    fi
    COMPLIANT_SSM_MANAGED_INSTANCES=$($AWSCLI ssm list-resource-compliance-summaries $PROFILE_OPT --region $regx --filters Key=Status,Values=COMPLIANT --query ResourceComplianceSummaryItems[].ResourceId --output text)
    if [[ $NON_COMPLIANT_SSM_MANAGED_INSTANCES || $COMPLIANT_SSM_MANAGED_INSTANCES ]]; then
      if [[ $NON_COMPLIANT_SSM_MANAGED_INSTANCES ]]; then
        for instance in $NON_COMPLIANT_SSM_MANAGED_INSTANCES; do
          textFail "$regx: EC2 managed instance $instance is non-compliant" "$regx" "$instance"
        done
      fi
      if [[ $COMPLIANT_SSM_MANAGED_INSTANCES ]]; then
        for instance in $COMPLIANT_SSM_MANAGED_INSTANCES; do
          textPass "$regx: EC2 managed instance $instance is compliant" "$regx" "$instance"
        done
      fi
    else
      textInfo "$regx: No EC2 managed instances found" "$regx"
    fi
  done
}

providers/aws/services/ssm/check_extra7124 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7124="7.124"
CHECK_TITLE_extra7124="[extra7124] Check if EC2 instances are managed by Systems Manager."
CHECK_SCORED_extra7124="NOT_SCORED"
CHECK_CIS_LEVEL_extra7124="EXTRA"
CHECK_SEVERITY_extra7124="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7124="AwsEc2Instance"
CHECK_ALTERNATE_check7124="extra7124"
CHECK_ASFF_COMPLIANCE_TYPE_extra7124="ens-op.exp.1.aws.sys.1 ens-op.acc.4.aws.sys.1"
CHECK_SERVICENAME_extra7124="ssm"
CHECK_RISK_extra7124='AWS Config provides AWS Managed Rules; which are predefined; customizable rules that AWS Config uses to evaluate whether your AWS resource configurations comply with common best practices.'
CHECK_REMEDIATION_extra7124='Verify and apply Systems Manager Prerequisites.'
CHECK_DOC_extra7124='https://docs.aws.amazon.com/systems-manager/latest/userguide/managed_instances.html'
CHECK_CAF_EPIC_extra7124='Infrastructure Security'

extra7124(){
  for regx in $REGIONS; do
    # Filters running instances only
    LIST_EC2_INSTANCES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --query 'Reservations[*].Instances[*].[InstanceId]' --filters Name=instance-state-name,Values=running --region $regx --output text 2>&1)
    if [[ $(echo "$LIST_EC2_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe instances" "$regx"
        continue
    fi
    if [[ $LIST_EC2_INSTANCES ]]; then
      LIST_SSM_MANAGED_INSTANCES=$($AWSCLI ssm describe-instance-information $PROFILE_OPT --query "InstanceInformationList[].InstanceId"  --region $regx | jq -r '.[]')
      LIST_EC2_UNMANAGED=$(echo ${LIST_SSM_MANAGED_INSTANCES[@]} ${LIST_EC2_INSTANCES[@]} | tr ' ' '\n' | sort | uniq -u)
      if [[ $LIST_EC2_UNMANAGED ]]; then
        for instance in $LIST_EC2_UNMANAGED; do
          textFail "$regx: EC2 instance $instance is not managed by Systems Manager" "$regx" "$instance"
        done
      fi
      if [[ $LIST_SSM_MANAGED_INSTANCES ]]; then
        for instance in $LIST_SSM_MANAGED_INSTANCES; do
          textPass "$regx: EC2 instance $instance is managed by Systems Manager" "$regx" "$instance"
        done
      fi
    else
      textInfo "$regx: No EC2 instances running found" "$regx"
    fi
  done
}

providers/aws/services/sqs/check_extra728 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra728="7.28"
CHECK_TITLE_extra728="[extra728] Check if SQS queues have Server Side Encryption enabled"
CHECK_SCORED_extra728="NOT_SCORED"
CHECK_CIS_LEVEL_extra728="EXTRA"
CHECK_SEVERITY_extra728="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra728="AwsSqsQueue"
CHECK_ALTERNATE_check728="extra728"
CHECK_ASFF_COMPLIANCE_TYPE_extra728="ens-mp.info.3.sns.1"
CHECK_SERVICENAME_extra728="sqs"
CHECK_RISK_extra728='If not enabled sensitive information in transit is not protected.'
CHECK_REMEDIATION_extra728='Enable Encryption. Use a CMK where possible. It will provide additional management and privacy benefits.'
CHECK_DOC_extra728='https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-sse-existing-queue.html'
CHECK_CAF_EPIC_extra728='Data Protection'

extra728(){
  for regx in $REGIONS; do
    LIST_SQS=$($AWSCLI sqs list-queues $PROFILE_OPT --region $regx --query QueueUrls --output text 2>&1|grep -v ^None )
    if [[ $(echo "$LIST_SQS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to list queues" "$regx"
        continue
    fi
    if [[ $LIST_SQS ]]; then
      for queue in $LIST_SQS; do
        # check if the policy has KmsMasterKeyId therefore SSE enabled
        SSE_ENABLED_QUEUE=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes.KmsMasterKeyId --output text|grep -v ^None)
        if [[ $SSE_ENABLED_QUEUE ]]; then
          textPass "$regx: SQS queue $queue is using Server Side Encryption" "$regx" "$queue"
        else
          textFail "$regx: SQS queue $queue is not using Server Side Encryption" "$regx" "$queue"
        fi
      done
    else
      textInfo "$regx: No SQS queues found" "$regx"
    fi
  done
}

providers/aws/services/sqs/check_extra727 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra727="7.27"
CHECK_TITLE_extra727="[extra727] Check if SQS queues have policy set as Public"
CHECK_SCORED_extra727="NOT_SCORED"
CHECK_CIS_LEVEL_extra727="EXTRA"
CHECK_SEVERITY_extra727="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra727="AwsSqsQueue"
CHECK_ALTERNATE_check727="extra727"
CHECK_SERVICENAME_extra727="sqs"
CHECK_RISK_extra727='Sensitive information could be disclosed.'
CHECK_REMEDIATION_extra727='Review service with overly permissive policies. Adhere to Principle of Least Privilege.'
CHECK_DOC_extra727='https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-basic-examples-of-sqs-policies.html'
CHECK_CAF_EPIC_extra727='Infrastructure Security'

extra727(){
  for regx in $REGIONS; do
    LIST_SQS=$($AWSCLI sqs list-queues $PROFILE_OPT --region $regx --query QueueUrls --output text 2>&1|grep -v ^None )
    if [[ $(echo "$LIST_SQS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to list queues" "$regx"
        continue
    fi
    if [[ $LIST_SQS ]]; then
      for queue in $LIST_SQS; do
        SQS_POLICY=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes.Policy)
        if [[ "$SQS_POLICY" != "null" ]]; then
          SQS_POLICY_ALLOW_ALL=$(echo $SQS_POLICY \
            | jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
          if [[ $SQS_POLICY_ALLOW_ALL ]]; then
            SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION=$(echo $SQS_POLICY \
              | jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)')
            if [[ $SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION ]]; then
              SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$(echo $SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION \
                | jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' )
              textFail "$regx: SQS $queue queue policy with public access" "$regx" "$queue"
            else
              textInfo "$regx: SQS $queue queue policy with public access but has a Condition" "$regx" "$queue"
            fi
          else
            textPass "$regx: SQS $queue queue without public access" "$regx" "$queue"
          fi
        else
          textPass "$regx: SQS $queue queue without policy" "$regx" "$queue"
        fi
      done
    else
      textInfo "$regx: No SQS queues found" "$regx"
    fi
  done
}

providers/aws/services/sns/check_extra731 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra731="7.31"
CHECK_TITLE_extra731="[extra731] Check if SNS topics have policy set as Public"
CHECK_SCORED_extra731="NOT_SCORED"
CHECK_CIS_LEVEL_extra731="EXTRA"
CHECK_SEVERITY_extra731="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra731="AwsSnsTopic"
CHECK_ALTERNATE_check731="extra731"
CHECK_SERVICENAME_extra731="sns"
CHECK_RISK_extra731='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra731='Ensure there is a business requirement for service to be public.'
CHECK_DOC_extra731='https://docs.aws.amazon.com/config/latest/developerguide/sns-topic-policy.html'
CHECK_CAF_EPIC_extra731='Infrastructure Security'

extra731(){
  for regx in $REGIONS; do
    LIST_SNS=$($AWSCLI sns list-topics $PROFILE_OPT --region $regx --query Topics --output text 2>&1|grep -v ^None )
    if [[ $(echo "$LIST_SNS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list topics" "$regx"
        continue
    fi
    if [[ $LIST_SNS ]]; then
      for topic in $LIST_SNS; do
        SHORT_TOPIC=$(echo $topic| cut -d: -f6)
        SNS_POLICY=$($AWSCLI sns get-topic-attributes --topic-arn $topic $PROFILE_OPT --region $regx --query Attributes.Policy 2>/dev/null)
        SNS_POLICY_ALLOW_ALL=$(echo $SNS_POLICY \
          | jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
        if [[ $SNS_POLICY_ALLOW_ALL ]]; then
          SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION=$(echo $SNS_POLICY \
            | jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)')
          if [[ $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION ]]; then
            SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$(echo $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION \
              | jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' )
            textFail "$regx: SNS topic $SHORT_TOPIC's policy with public access" "$regx" "$SHORT_TOPIC"
          else
            textPass "$regx: SNS topic $SHORT_TOPIC's policy with public access but has a Condition" "$regx" "$SHORT_TOPIC"
          fi
        else
          textPass "$regx: SNS topic without public access" "$regx"
        fi
      done
    else
      textInfo "$regx: No SNS topic found" "$regx"
    fi
  done
}

providers/aws/services/sns/check_extra7130 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7130="7.130"
CHECK_TITLE_extra7130="[extra7130] Ensure there are no SNS Topics unencrypted"
CHECK_SCORED_extra7130="NOT_SCORED"
CHECK_CIS_LEVEL_extra7130="EXTRA"
CHECK_SEVERITY_extra7130="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7130="AwsSnsTopic"
CHECK_ALTERNATE_check7130="extra7130"
CHECK_SERVICENAME_extra7130="sns"
CHECK_RISK_extra7130='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra7130='Use Amazon SNS with AWS KMS.'
CHECK_DOC_extra7130='https://docs.aws.amazon.com/sns/latest/dg/sns-server-side-encryption.html'
CHECK_CAF_EPIC_extra7130='Data Protection'

extra7130(){
  for regx in $REGIONS; do
    LIST_SNS=$($AWSCLI sns list-topics $PROFILE_OPT --region $regx --query 'Topics[*].TopicArn' --output text 2>&1)
    if [[ $(echo "$LIST_SNS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list topics" "$regx"
        continue
    fi
    if [[ $LIST_SNS ]];then
      for topic in $LIST_SNS; do
        SHORT_TOPIC=$(echo $topic | awk -F ":" '{print $NF}')
        SNS_ENCRYPTION=$($AWSCLI sns get-topic-attributes $PROFILE_OPT --region $regx --topic-arn $topic --query 'Attributes.KmsMasterKeyId' --output text)
        if [[ "None" == $SNS_ENCRYPTION ]]; then
          textFail "$regx: $SHORT_TOPIC is not encrypted!" "$regx" "$SHORT_TOPIC"
        else
          textPass "$regx: $SHORT_TOPIC is encrypted" "$regx" "$SHORT_TOPIC"
        fi
      done
    else
      textInfo "$regx: No SNS topic found" "$regx"
    fi
  done
}

providers/aws/services/shield/check_extra7171 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7171="7.171"
CHECK_TITLE_extra7171="[extra7171] Check if classic load balancers are protected by AWS Shield Advanced"
CHECK_SCORED_extra7171="NOT_SCORED"
CHECK_CIS_LEVEL_extra7171="EXTRA"
CHECK_SEVERITY_extra7171="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7171="AwsElasticLoadBalancingLoadBalancer"
CHECK_ALTERNATE_check7171="extra7171"
CHECK_SERVICENAME_extra7171="shield"
CHECK_RISK_extra7171='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
CHECK_REMEDIATION_extra7171='Add as a protected resource in AWS Shield Advanced.'
CHECK_DOC_extra7171='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
CHECK_CAF_EPIC_extra7171='Infrastructure security'

extra7171() {
  if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
    CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
    PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
    ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5)
    for regx in $REGIONS; do
      LIST_OF_CLASSIC_LOAD_BALANCERS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].[LoadBalancerName]' --output text |grep -v '^None$')
      if [[ $LIST_OF_CLASSIC_LOAD_BALANCERS ]]; then
        for elb in $LIST_OF_CLASSIC_LOAD_BALANCERS; do
          ELB_ARN="arn:${PARTITION}:elasticloadbalancing:${regx}:${ACCOUNT_ID}:loadbalancer/${elb}"
          if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ELB_ARN >/dev/null 2>&1; then
            textPass "$regx: ELB $elb is protected by AWS Shield Advanced" "$regx" "$elb"
          else
            textFail "$regx: ELB $elb is not protected by AWS Shield Advanced" "$regx" "$elb"
          fi
        done
      else
        textInfo "$regx: no classic load balancers found" "$regx"
      fi
    done
  else
    textInfo "No AWS Shield Advanced subscription found. Skipping check."
  fi
}

providers/aws/services/shield/check_extra7170 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7170="7.170"
CHECK_TITLE_extra7170="[extra7170] Check if internet-facing application load balancers are protected by AWS Shield Advanced"
CHECK_SCORED_extra7170="NOT_SCORED"
CHECK_CIS_LEVEL_extra7170="EXTRA"
CHECK_SEVERITY_extra7170="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7170="AwsElasticLoadBalancingV2LoadBalancer"
CHECK_ALTERNATE_check7170="extra7170"
CHECK_SERVICENAME_extra7170="shield"
CHECK_RISK_extra7170='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
CHECK_REMEDIATION_extra7170='Add as a protected resource in AWS Shield Advanced.'
CHECK_DOC_extra7170='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
CHECK_CAF_EPIC_extra7170='Infrastructure security'

extra7170() {
  if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
    for regx in $REGIONS; do
      LIST_OF_APPLICATION_LOAD_BALANCERS=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Type == `application` && Scheme == `internet-facing`].[LoadBalancerName,LoadBalancerArn]' --output text)
      if [[ $LIST_OF_APPLICATION_LOAD_BALANCERS ]]; then
        while read -r alb; do
          ALB_NAME=$(echo $alb | awk '{ print $1; }')
          ALB_ARN=$(echo $alb | awk '{ print $2; }')
          if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ALB_ARN >/dev/null 2>&1; then
            textPass "$regx: ALB $ALB_NAME is protected by AWS Shield Advanced" "$regx" "$ALB_NAME"
          else
            textFail "$regx: ALB $ALB_NAME is not protected by AWS Shield Advanced" "$regx" "$ALB_NAME"
          fi
        done <<<"$LIST_OF_APPLICATION_LOAD_BALANCERS"
      else
        textInfo "$regx: no application load balancers found" "$regx"
      fi
    done
  else
    textInfo "No AWS Shield Advanced subscription found. Skipping check."
  fi
}

providers/aws/services/shield/check_extra7169 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7169="7.169"
CHECK_TITLE_extra7169="[extra7169] Check if global accelerators are protected by AWS Shield Advanced"
CHECK_SCORED_extra7169="NOT_SCORED"
CHECK_CIS_LEVEL_extra7169="EXTRA"
CHECK_SEVERITY_extra7169="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7169="AwsGlobalAccelerator"
CHECK_ALTERNATE_check7169="extra7169"
CHECK_SERVICENAME_extra7169="shield"
CHECK_RISK_extra7169='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
CHECK_REMEDIATION_extra7169='Add as a protected resource in AWS Shield Advanced.'
CHECK_DOC_extra7169='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
CHECK_CAF_EPIC_extra7169='Infrastructure security'

extra7169() {
  if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
    LIST_OF_GLOBAL_ACCELERATORS=$($AWSCLI globalaccelerator list-accelerators --region us-west-2 $PROFILE_OPT --query 'Accelerators[?Enabled].[Name,AcceleratorArn]' --output text)
    if [[ $LIST_OF_GLOBAL_ACCELERATORS ]]; then
      while read -r accelerator; do
        ACCELERATOR_NAME=$(echo $accelerator | awk '{ print $1; }')
        ACCELERATOR_ARN=$(echo $accelerator | awk '{ print $2; }')
        if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ACCELERATOR_ARN >/dev/null 2>&1; then
          textPass "$REGION: Global Accelerator $ACCELERATOR_NAME is protected by AWS Shield Advanced" "$REGION" "$ACCELERATOR_NAME"
        else
          textFail "$REGION: Global Accelerator $ACCELERATOR_NAME is not protected by AWS Shield Advanced" "$REGION" "$ACCELERATOR_NAME"
        fi
      done <<<"$LIST_OF_GLOBAL_ACCELERATORS"
    else
      textInfo "$REGION: no global accelerators found" "$REGION"
    fi
  else
    textInfo "No AWS Shield Advanced subscription found. Skipping check."
  fi
}

providers/aws/services/shield/check_extra7168 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7168="7.168"
CHECK_TITLE_extra7168="[extra7168] Check if Route53 hosted zones are protected by AWS Shield Advanced"
CHECK_SCORED_extra7168="NOT_SCORED"
CHECK_CIS_LEVEL_extra7168="EXTRA"
CHECK_SEVERITY_extra7168="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7168="AwsRoute53Domain"
CHECK_ALTERNATE_check7168="extra7168"
CHECK_SERVICENAME_extra7168="shield"
CHECK_RISK_extra7168='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
CHECK_REMEDIATION_extra7168='Add as a protected resource in AWS Shield Advanced.'
CHECK_DOC_extra7168='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
CHECK_CAF_EPIC_extra7168='Infrastructure security'

extra7168() {
  if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
    CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
    PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
    LIST_OF_ROUTE53_HOSTED_ZONES=$($AWSCLI route53 list-hosted-zones $PROFILE_OPT --query 'HostedZones[*].[Id,Name]' --output text)
    if [[ $LIST_OF_ROUTE53_HOSTED_ZONES ]]; then
      while read -r hosted_zone; do
        HOSTED_ZONE_ID=$(echo $hosted_zone | awk '{ print $1; }')
        HOSTED_ZONE_NAME=$(echo $hosted_zone | awk '{ print $2; }')
        HOSTED_ZONE_ARN="arn:${PARTITION}:route53:::${HOSTED_ZONE_ID:1}"
        if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $HOSTED_ZONE_ARN >/dev/null 2>&1; then
          textPass "$REGION: Route53 Hosted Zone $HOSTED_ZONE_NAME is protected by AWS Shield Advanced" "$REGION" "$HOSTED_ZONE_NAME"
        else
          textFail "$REGION: Route53 Hosted Zone $HOSTED_ZONE_NAME is not protected by AWS Shield Advanced" "$REGION" "$HOSTED_ZONE_NAME"
        fi
      done <<<"$LIST_OF_ROUTE53_HOSTED_ZONES"
    else
      textInfo "$REGION: no Route53 hosted zones found" "$REGION"
    fi
  else
    textInfo "No AWS Shield Advanced subscription found. Skipping check."
  fi
}

providers/aws/services/shield/check_extra7167 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7167="7.167"
CHECK_TITLE_extra7167="[extra7167] Check if Cloudfront distributions are protected by AWS Shield Advanced"
CHECK_SCORED_extra7167="NOT_SCORED"
CHECK_CIS_LEVEL_extra7167="EXTRA"
CHECK_SEVERITY_extra7167="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7167="AwsCloudFrontDistribution"
CHECK_ALTERNATE_check7167="extra7167"
CHECK_SERVICENAME_extra7167="shield"
CHECK_RISK_extra7167='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
CHECK_REMEDIATION_extra7167='Add as a protected resource in AWS Shield Advanced.'
CHECK_DOC_extra7167='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
CHECK_CAF_EPIC_extra7167='Infrastructure security'

extra7167() {
  if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
    LIST_OF_CLOUDFRONT_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[*].[Id,ARN]' --output text  | grep -v None)
    if [[ $LIST_OF_CLOUDFRONT_DISTRIBUTIONS ]]; then
      while read -r distribution; do
        DISTRIBUTION_ID=$(echo $distribution | awk '{ print $1; }')
        DISTRIBUTION_ARN=$(echo $distribution | awk '{ print $2; }')
        if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $DISTRIBUTION_ARN >/dev/null 2>&1; then
          textPass "$REGION: Cloudfront distribution $DISTRIBUTION_ID is protected by AWS Shield Advanced" "$REGION" "$DISTRIBUTION_ID"
        else
          textFail "$REGION: Cloudfront distribution $DISTRIBUTION_ID is not protected by AWS Shield Advanced" "$REGION" "$DISTRIBUTION_ID"
        fi
      done <<<"$LIST_OF_CLOUDFRONT_DISTRIBUTIONS"
    else
      textInfo "$REGION: no Cloudfront distributions found" "$REGION"
    fi
  else
    textInfo "No AWS Shield Advanced subscription found. Skipping check."
  fi
}

providers/aws/services/shield/check_extra7166 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7166="7.166"
CHECK_TITLE_extra7166="[extra7166] Check if Elastic IP addresses with associations are protected by AWS Shield Advanced"
CHECK_SCORED_extra7166="NOT_SCORED"
CHECK_CIS_LEVEL_extra7166="EXTRA"
CHECK_SEVERITY_extra7166="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7166="AwsEc2Eip"
CHECK_ALTERNATE_check7166="extra7166"
CHECK_SERVICENAME_extra7166="shield"
CHECK_RISK_extra7166='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
CHECK_REMEDIATION_extra7166='Add as a protected resource in AWS Shield Advanced.'
CHECK_DOC_extra7166='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
CHECK_CAF_EPIC_extra7166='Infrastructure security'

extra7166() {
  if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
    CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
    PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
    ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5)
    for regx in $REGIONS; do
      LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS=$($AWSCLI ec2 describe-addresses $PROFILE_OPT --region $regx --query 'Addresses[?AssociationId].AllocationId' --output text)
      if [[ $LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS ]]; then
        for elastic_ip in $LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS; do
          EIP_ARN="arn:${PARTITION}:ec2:${regx}:${ACCOUNT_ID}:eip-allocation/${elastic_ip}"
          if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $EIP_ARN >/dev/null 2>&1; then
            textPass "$regx: EIP $elastic_ip is protected by AWS Shield Advanced" "$regx" "$elastic_ip"
          else
            textFail "$regx: EIP $elastic_ip is not protected by AWS Shield Advanced" "$regx" "$elastic_ip"
          fi
        done
      else
        textInfo "$regx: no elastic IP addresses with assocations found" "$regx"
      fi
    done
  else
    textInfo "No AWS Shield Advanced subscription found. Skipping check."
  fi
}

providers/aws/services/securityhub/check_extra799 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra799="7.99"
CHECK_TITLE_extra799="[extra799] Check if Security Hub is enabled and its standard subscriptions"
CHECK_SCORED_extra799="NOT_SCORED"
CHECK_CIS_LEVEL_extra799="EXTRA"
CHECK_SEVERITY_extra799="High"
CHECK_ASFF_RESOURCE_TYPE_extra799="AwsSecurityHubHub"
CHECK_ALTERNATE_check799="extra799"
CHECK_SERVICENAME_extra799="securityhub"
CHECK_RISK_extra799='AWS Security Hub gives you a comprehensive view of your security alerts and security posture across your AWS accounts.'
CHECK_REMEDIATION_extra799='Security Hub is Regional. When you enable or disable a security standard; it is enabled or disabled only in the current Region or in the Region that you specify.'
CHECK_DOC_extra799='https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-enable-disable.html'
CHECK_CAF_EPIC_extra799='Logging and Monitoring'

extra799(){
  for regx in $REGIONS; do
    # If command below fails get nothing then it there are no subscriptions and Security Hub is not enabled.
    LIST_OF_SECHUB_SUBSCRIPTIONS=$($AWSCLI $PROFILE_OPT --region $regx securityhub get-enabled-standards --query 'StandardsSubscriptions[?StandardsStatus == `READY`].StandardsSubscriptionArn' --output json 2>/dev/null | awk -F "/" '{ print $2 }' | tr '\n' ' ' )
    if [[ $LIST_OF_SECHUB_SUBSCRIPTIONS ]]; then
      textPass "$regx: Security Hub is enabled with standards $LIST_OF_SECHUB_SUBSCRIPTIONS" "$regx"
    else
      textInfo "$regx: Security Hub is not enabled" "$regx"
      #textFail "$regx: Security Hub is not enabled" "$regx"
    fi
  done
}

providers/aws/services/secretsmanager/check_extra7163 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation:
#
#   https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/rotate-secret.html
#
#   rotate-secret
#       --secret-id <value>
#       [--client-request-token <value>]
#       [--rotation-lambda-arn <value>]
#       [--rotation-rules <value>]
#       [--cli-input-json <value>]
#       [--generate-cli-skeleton <value>]


CHECK_ID_extra7163="7.163"
CHECK_TITLE_extra7163="[extra7163] Check if Secrets Manager key rotation is enabled"
CHECK_SCORED_extra7163="NOT_SCORED"
CHECK_CIS_LEVEL_extra7163="EXTRA"
CHECK_SEVERITY_extra7163="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7163="AwsSecretsManagerSecret"
CHECK_ALTERNATE_extra7163="extra7163"
CHECK_SERVICENAME_extra7163="secretsmanager"
CHECK_RISK_extra7163="Rotating secrets minimizes exposure to attacks using stolen keys."
CHECK_REMEDIATION_extra7163="Enable key rotation on Secrets Manager key."
CHECK_DOC_extra7163="https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets_strategies.html"
CHECK_CAF_EPIC_extra7163="Data Protection"

extra7163(){
    # "Check if Secrets Manager key rotation is enabled"
    for regx in $REGIONS; do
      LIST_OF_SECRETS=$($AWSCLI secretsmanager list-secrets $PROFILE_OPT --region $regx --query 'SecretList[*].Name' --output text 2>&1)
      if [[ $(echo "$LIST_OF_SECRETS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to list secrets" "$regx"
          continue
      fi
      if [[ $LIST_OF_SECRETS ]]; then
        for secret in $LIST_OF_SECRETS; do
          KEY_ROTATION_ENABLED=$($AWSCLI secretsmanager describe-secret $PROFILE_OPT --region $regx --secret-id $secret --output json | jq '.RotationEnabled')
          if [[ $KEY_ROTATION_ENABLED == true ]]; then
            textPass "$regx: $secret has key rotation enabled." "$regx" "$secret"
          else
            textFail "$regx: $secret does not have key rotation enabled." "$regx" "$secret"
          fi
        done
      else
        textPass "$regx: No Secrets Manager secrets found." "$regx"
      fi
    done
}

providers/aws/services/sagemaker/check_extra7112 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7112="7.112"
CHECK_TITLE_extra7112="[extra7112] Check if Amazon SageMaker Notebook instances have data encryption enabled"
CHECK_SCORED_extra7112="NOT_SCORED"
CHECK_CIS_LEVEL_extra7112="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7112="AwsSageMakerNotebookInstance"
CHECK_ALTERNATE_check7112="extra7112"
CHECK_SEVERITY_extra7112="Medium"
CHECK_SERVICENAME_extra7112="sagemaker"
CHECK_RISK_extra7112='Data exfiltration could happen if information is not protected. KMS keys provide additional security level to IAM policies.'
CHECK_REMEDIATION_extra7112='Specify AWS KMS keys to use for input and output from S3 and EBS.'
CHECK_DOC_extra7112='https://docs.aws.amazon.com/sagemaker/latest/dg/key-management.html'
CHECK_CAF_EPIC_extra7112='Data Protection'

extra7112(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_INSTANCES=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-notebook-instances --query 'NotebookInstances[*].NotebookInstanceName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list notebook instances" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_INSTANCES ]];then
            for nb_instance in $LIST_SM_NB_INSTANCES; do
                SM_NB_KMSKEY=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-notebook-instance --notebook-instance-name $nb_instance --query 'KmsKeyId' --output text)
                if [[ "${SM_NB_KMSKEY}" == "None" ]]; then
                    textFail "${regx}: Sagemaker Notebook instance $nb_instance has data encryption disabled" "${regx}" "$nb_instance"
                else
                    textPass "${regx}: Sagemaker Notebook instance $nb_instance has data encryption enabled" "${regx}" "$nb_instance"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Notebook instances found" "${regx}"
        fi
    done
}

providers/aws/services/sagemaker/check_extra7111 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7111="7.111"
CHECK_TITLE_extra7111="[extra7111] Check if Amazon SageMaker Notebook instances have direct internet access"
CHECK_SCORED_extra7111="NOT_SCORED"
CHECK_CIS_LEVEL_extra7111="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7111="AwsSageMakerNotebookInstance"
CHECK_ALTERNATE_check7111="extra7111"
CHECK_SEVERITY_extra7111="Medium"
CHECK_SERVICENAME_extra7111="sagemaker"
CHECK_RISK_extra7111='This could provide an avenue for unauthorized access to your data.'
CHECK_REMEDIATION_extra7111='Restrict which traffic can access by launching Studio in a Virtual Private Cloud (VPC) of your choosing.'
CHECK_DOC_extra7111='https://docs.aws.amazon.com/sagemaker/latest/dg/interface-vpc-endpoint.html'
CHECK_CAF_EPIC_extra7111='Infrastructure Security'

extra7111(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_INSTANCES=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-notebook-instances --query 'NotebookInstances[*].NotebookInstanceName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list notebook instances" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_INSTANCES ]];then
            for nb_instance in $LIST_SM_NB_INSTANCES; do
                SM_NB_DIRECTINET=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-notebook-instance --notebook-instance-name $nb_instance --query 'DirectInternetAccess' --output text)
                if [[ "${SM_NB_DIRECTINET}" == "Enabled" ]]; then
                    textFail "${regx}: Sagemaker Notebook instance $nb_instance has direct internet access enabled" "${regx}" "$nb_instance"
                else
                    textPass "${regx}: Sagemaker Notebook instance $nb_instance has direct internet access disabled" "${regx}" "$nb_instance"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Notebook instances found" "${regx}"
        fi
    done
}

providers/aws/services/sagemaker/check_extra7110 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7110="7.110"
CHECK_TITLE_extra7110="[extra7110] Check if Amazon SageMaker Training job have VPC settings configured."
CHECK_SCORED_extra7110="NOT_SCORED"
CHECK_CIS_LEVEL_extra7110="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7110="AwsSageMakerNotebookInstance"
CHECK_ALTERNATE_check7110="extra7110"
CHECK_SEVERITY_extra7110="Medium"
CHECK_SERVICENAME_extra7110="sagemaker"
CHECK_RISK_extra7110='This could provide an avenue for unauthorized access to your data.'
CHECK_REMEDIATION_extra7110='Restrict which traffic can access by launching Studio in a Virtual Private Cloud (VPC) of your choosing.'
CHECK_DOC_extra7110='https://docs.aws.amazon.com/sagemaker/latest/dg/interface-vpc-endpoint.html'
CHECK_CAF_EPIC_extra7110='Infrastructure Security'

extra7110(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_JOBS=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-training-jobs --query 'TrainingJobSummaries[*].TrainingJobName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_JOBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list training jobs" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_JOBS ]];then
            for nb_job_name in $LIST_SM_NB_JOBS; do
                SM_NB_SUBNETS=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-training-job --training-job-name $nb_job_name --query 'VpcConfig.Subnets' --output text)
                if [[ $SM_NB_SUBNETS == "None" ]]; then
                    textFail "${regx}: Sagemaker Training job $nb_job_name has VPC settings for the training job volume and output disabled" "${regx}" "$nb_job_name"
                else
                    textPass "${regx}: Sagemaker Training job $nb_job_name has VPC settings for the training job volume and output enabled" "${regx}" "$nb_job_name"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Trainings jobs found" "${regx}"
        fi
    done
}

providers/aws/services/sagemaker/check_extra7109 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7109="7.109"
CHECK_TITLE_extra7109="[extra7109] Check if Amazon SageMaker Training jobs have network isolation enabled"
CHECK_SCORED_extra7109="NOT_SCORED"
CHECK_CIS_LEVEL_extra7109="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7109="AwsSageMakerNotebookInstance"
CHECK_ALTERNATE_check7109="extra7109"
CHECK_SEVERITY_extra7109="Medium"
CHECK_SERVICENAME_extra7109="sagemaker"
CHECK_RISK_extra7109='This could provide an avenue for unauthorized access to your data.'
CHECK_REMEDIATION_extra7109='Restrict which traffic can access by launching Studio in a Virtual Private Cloud (VPC) of your choosing.'
CHECK_DOC_extra7109='https://docs.aws.amazon.com/sagemaker/latest/dg/interface-vpc-endpoint.html'
CHECK_CAF_EPIC_extra7109='Infrastructure Security'

extra7109(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_JOBS=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-training-jobs --query 'TrainingJobSummaries[*].TrainingJobName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_JOBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list training jobs" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_JOBS ]];then
            for nb_job_name in $LIST_SM_NB_JOBS; do
                SM_NB_NETWORKISOLATION=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-training-job --training-job-name $nb_job_name --query 'EnableNetworkIsolation' --output text)
                if [[ $SM_NB_NETWORKISOLATION == False ]]; then
                    textFail "${regx}: Sagemaker Training job $nb_job_name has network isolation disabled" "${regx}" "$nb_job_name"
                else
                    textPass "${regx}: Sagemaker Training job $nb_job_name has network isolation enabled" "${regx}" "$nb_job_name"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Trainings jobs found" "${regx}"
        fi
    done
}

providers/aws/services/sagemaker/check_extra7108 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7108="7.108"
CHECK_TITLE_extra7108="[extra7108] Check if Amazon SageMaker Training jobs have volume and output with KMS encryption enabled"
CHECK_SCORED_extra7108="NOT_SCORED"
CHECK_CIS_LEVEL_extra7108="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7108="AwsSageMakerNotebookInstance"
CHECK_ALTERNATE_check7108="extra7108"
CHECK_SEVERITY_extra7108="Medium"
CHECK_SERVICENAME_extra7108="sagemaker"
CHECK_RISK_extra7108='Data exfiltration could happen if information is not protected. KMS keys provide additional security level to IAM policies.'
CHECK_REMEDIATION_extra7108='Specify AWS KMS keys to use for input and output from S3 and EBS.'
CHECK_DOC_extra7108='https://docs.aws.amazon.com/sagemaker/latest/dg/key-management.html'
CHECK_CAF_EPIC_extra7108='Data Protection'

extra7108(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_JOBS=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-training-jobs --query 'TrainingJobSummaries[*].TrainingJobName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_JOBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list training jobs" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_JOBS ]];then
            for nb_job_name in $LIST_SM_NB_JOBS; do
                SM_JOB_KMSENCRYPTION=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-training-job --training-job-name $nb_job_name --query 'ResourceConfig.VolumeKmsKeyId' --output text)
                if [[ "${SM_JOB_KMSENCRYPTION}" == "None" ]];then
                    textFail "${regx}: Sagemaker Trainings job $nb_job_name has KMS encryption disabled" "${regx}" "$nb_job_name"
                else
                    textPass "${regx}: Sagemaker Trainings job $nb_job_name has KSM encryption enabled" "${regx}" "$nb_job_name"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Trainings jobs found" "${regx}"
        fi
    done
}

providers/aws/services/sagemaker/check_extra7107 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7107="7.107"
CHECK_TITLE_extra7107="[extra7107] Check if Amazon SageMaker Training jobs have intercontainer encryption enabled"
CHECK_SCORED_extra7107="NOT_SCORED"
CHECK_CIS_LEVEL_extra7107="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7107="AwsSageMakerNotebookInstance"
CHECK_ALTERNATE_check7107="extra7107"
CHECK_SEVERITY_extra7107="Medium"
CHECK_SERVICENAME_extra7107="sagemaker"
CHECK_RISK_extra7107='If not restricted unintended access could happen.'
CHECK_REMEDIATION_extra7107='Internetwork communications support TLS 1.2 encryption between all components and clients.'
CHECK_DOC_extra7107='https://docs.aws.amazon.com/sagemaker/latest/dg/interface-vpc-endpoint.html'
CHECK_CAF_EPIC_extra7107='Data Protection'

extra7107(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_JOBS=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-training-jobs --query 'TrainingJobSummaries[*].TrainingJobName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_JOBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list training jobs" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_JOBS ]];then
            for nb_job_name in $LIST_SM_NB_JOBS; do
                SM_NB_INTERCONTAINERENCRYPTION=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-training-job --training-job-name $nb_job_name --query 'EnableInterContainerTrafficEncryption' --output text)
                if [[ $SM_NB_INTERCONTAINERENCRYPTION == "False" ]]; then
                    textFail "${regx}: SageMaker Training job $nb_job_name has intercontainer encryption disabled" "${regx}" "$nb_job_name"
                else
                    textPass "${regx}: SageMaker Training jobs $nb_job_name has intercontainer encryption enabled" "${regx}" "$nb_job_name"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Training found" "${regx}"
        fi
    done
}

providers/aws/services/sagemaker/check_extra7106 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7106="7.106"
CHECK_TITLE_extra7106="[extra7106] Check if Amazon SageMaker Models have VPC settings configured"
CHECK_SCORED_extra7106="NOT_SCORED"
CHECK_CIS_LEVEL_extra7106="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7106="AwsSageMakerModel"
CHECK_ALTERNATE_check7106="extra7106"
CHECK_SEVERITY_extra7106="Medium"
CHECK_SERVICENAME_extra7106="sagemaker"
CHECK_RISK_extra7106='This could provide an avenue for unauthorized access to your data.'
CHECK_REMEDIATION_extra7106='Restrict which traffic can access by launching Studio in a Virtual Private Cloud (VPC) of your choosing.'
CHECK_DOC_extra7106='https://docs.aws.amazon.com/sagemaker/latest/dg/studio-notebooks-and-internet-access.html'
CHECK_CAF_EPIC_extra7106='Infrastructure Security'

extra7106(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_MODELS=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-models --query 'Models[*].ModelName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_MODELS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list models" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_MODELS ]];then
            for nb_model_name in $LIST_SM_NB_MODELS; do
                SM_NB_VPCCONFIG=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-model --model-name $nb_model_name --query 'VpcConfig.Subnets' --output text)
                if [[ $SM_NB_VPCCONFIG == "None" ]]; then
                    textFail "${regx}: Amazon SageMaker Model $nb_model_name has VPC settings disabled" "${regx}" "$nb_model_name"
                else
                    textPass "${regx}: Amazon SageMaker Model $nb_model_name has VPC settings enabled" "${regx}" "$nb_model_name"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Models found" "${regx}"
        fi
    done
}

providers/aws/services/sagemaker/check_extra7105 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7105="7.105"
CHECK_TITLE_extra7105="[extra7105] Check if Amazon SageMaker Models have network isolation enabled"
CHECK_SCORED_extra7105="NOT_SCORED"
CHECK_CIS_LEVEL_extra7105="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7105="AwsSageMakerModel"
CHECK_ALTERNATE_check7105="extra7105"
CHECK_SEVERITY_extra7105="Medium"
CHECK_SERVICENAME_extra7105="sagemaker"
CHECK_RISK_extra7105='This could provide an avenue for unauthorized access to your data.'
CHECK_REMEDIATION_extra7105='Restrict which traffic can access by launching Studio in a Virtual Private Cloud (VPC) of your choosing.'
CHECK_DOC_extra7105='https://docs.aws.amazon.com/sagemaker/latest/dg/studio-notebooks-and-internet-access.html'
CHECK_CAF_EPIC_extra7105='Infrastructure Security'

extra7105(){
	for regx in ${REGIONS}; do
		LIST_SM_NB_MODELS=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-models --query 'Models[*].ModelName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_MODELS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list models" "$regx"
            continue
        fi
		if [[ $LIST_SM_NB_MODELS ]];then
			for nb_model_name in $LIST_SM_NB_MODELS; do
				SM_NB_NETWORKISOLATION=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-model --model-name $nb_model_name --query 'EnableNetworkIsolation' --output text)
				if [[ $SM_NB_NETWORKISOLATION == False ]]; then
					textFail "${regx}: SageMaker Model $nb_model_name has network isolation disabled" "${regx}" "$nb_model_name"
				else
					textPass "${regx}: SageMaker Model $nb_model_name has network isolation enabled" "${regx}" "$nb_model_name"
				fi
			done
		else
			textInfo "${regx}: No Sagemaker Models found" "${regx}"
		fi
	done
}

providers/aws/services/sagemaker/check_extra7104 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7104="7.104"
CHECK_TITLE_extra7104="[extra7104] Check if Amazon SageMaker Notebook instances have VPC settings configured"
CHECK_SCORED_extra7104="NOT_SCORED"
CHECK_CIS_LEVEL_extra7104="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7104="AwsSageMakerNotebookInstance"
CHECK_ALTERNATE_check7104="extra7104"
CHECK_SEVERITY_extra7104="Medium"
CHECK_SERVICENAME_extra7104="sagemaker"
CHECK_RISK_extra7104='This could provide an avenue for unauthorized access to your data.'
CHECK_REMEDIATION_extra7104='Restrict which traffic can access by launching Studio in a Virtual Private Cloud (VPC) of your choosing.'
CHECK_DOC_extra7104='https://docs.aws.amazon.com/sagemaker/latest/dg/studio-notebooks-and-internet-access.html'
CHECK_CAF_EPIC_extra7104='Infrastructure Security'

extra7104(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_INSTANCES=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-notebook-instances --query 'NotebookInstances[*].NotebookInstanceName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list notebook instances" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_INSTANCES ]];then
            for nb_instance in $LIST_SM_NB_INSTANCES; do
                SM_NB_SUBNETID=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-notebook-instance --notebook-instance-name $nb_instance --query 'SubnetId' --output text)
                if [[ "${SM_NB_SUBNETID}" == "None" ]]; then
                    textFail "${regx}: Sagemaker Notebook instance $nb_instance has VPC settings disabled" "${regx}" "$nb_instance"
                else
                    textPass "${regx}: Sagemaker Notebook instance $nb_instance is in a VPC" "${regx}" "$nb_instance"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Notebook instances found" "${regx}"
        fi
    done
}

providers/aws/services/sagemaker/check_extra7103 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7103="7.103"
CHECK_TITLE_extra7103="[extra7103] Check if Amazon SageMaker Notebook instances have root access disabled"
CHECK_SCORED_extra7103="NOT_SCORED"
CHECK_CIS_LEVEL_extra7103="EXTRA"
CHECK_ASFF_RESOURCE_TYPE_extra7103="AwsSageMakerNotebookInstance"
CHECK_ALTERNATE_check7103="extra7103"
CHECK_SEVERITY_extra7103="Medium"
CHECK_SERVICENAME_extra7103="sagemaker"
CHECK_RISK_extra7103='Users with root access have administrator privileges; users can access and edit all files on a notebook instance with root access enabled.'
CHECK_REMEDIATION_extra7103='set the RootAccess field to Disabled. You can also disable root access for users when you create or update a notebook instance in the Amazon SageMaker console.'
CHECK_DOC_extra7103='https://docs.aws.amazon.com/sagemaker/latest/dg/nbi-root-access.html'
CHECK_CAF_EPIC_extra7103='IAM'

extra7103(){
    for regx in ${REGIONS}; do
        LIST_SM_NB_INSTANCES=$($AWSCLI $PROFILE_OPT --region $regx sagemaker list-notebook-instances --query 'NotebookInstances[*].NotebookInstanceName' --output text 2>&1)
        if [[ $(echo "$LIST_SM_NB_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to list notebook instances" "$regx"
            continue
        fi
        if [[ $LIST_SM_NB_INSTANCES ]];then
            for nb_instance in $LIST_SM_NB_INSTANCES; do
                SM_NB_ROOTACCESS=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-notebook-instance --notebook-instance-name $nb_instance --query 'RootAccess' --output text)
                if [[ "${SM_NB_ROOTACCESS}" == "Enabled" ]]; then
                    textFail "${regx}: Sagemaker Notebook instance $nb_instance has root access enabled" "${regx}" "$nb_instance"
                else
                    textPass "${regx}: Sagemaker Notebook instance $nb_instance has root access disabled" "${regx}" "$nb_instance"
                fi
            done
        else
            textInfo "${regx}: No Sagemaker Notebook instances found" "${regx}"
        fi
    done
}

providers/aws/services/s3/check_extra771 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra771="7.71"
CHECK_TITLE_extra771="[extra771] Check if S3 buckets have policies which allow WRITE access "
CHECK_SCORED_extra771="NOT_SCORED"
CHECK_CIS_LEVEL_extra771="EXTRA"
CHECK_SEVERITY_extra771="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra771="AwsS3Bucket"
CHECK_ALTERNATE_check771="extra771"
CHECK_SERVICENAME_extra771="s3"
CHECK_RISK_extra771='Non intended users can put objects in a given bucket.'
CHECK_REMEDIATION_extra771='Ensure proper bucket policy is in place with the least privilege principle applied.'
CHECK_DOC_extra771='https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_s3_rw-bucket.html'
CHECK_CAF_EPIC_extra771='IAM'

extra771(){
  LIST_OF_BUCKETS=$("${AWSCLI}" s3api list-buckets ${PROFILE_OPT} --region "${REGION}" --query "sort_by(Buckets, &Name)[].Name" --output text 2>&1)
  if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_BUCKETS}"; then
      textInfo "${REGION}: Access Denied trying to list buckets" "${REGION}"
      return
  fi
  if [[ "${LIST_OF_BUCKETS}" ]]; then
    for bucket in ${LIST_OF_BUCKETS};do
      # Recover Bucket region
      BUCKET_REGION=$("${AWSCLI}" ${PROFILE_OPT} s3api get-bucket-location --bucket "${bucket}" --query LocationConstraint --output text)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_POLICY_STATEMENTS}"; then
          textInfo "${REGION}: Access Denied trying to get bucket policy for ${bucket}" "${REGION}"
      fi
      # If None use default region
      if [[ "${BUCKET_REGION}" == "None" ]]; then
        BUCKET_REGION="${REGION}"
      fi
      # Recover Bucket policy statements
      BUCKET_POLICY_STATEMENTS=$("${AWSCLI}" s3api ${PROFILE_OPT} get-bucket-policy --region "${BUCKET_REGION}" --bucket "${bucket}" --output json --query Policy 2>&1)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_POLICY_STATEMENTS}"; then
          textInfo "${REGION}: Access Denied trying to get bucket policy for ${bucket}" "${REGION}"
          continue
      fi
      if grep -q -E 'NoSuchBucketPolicy'<<< "${BUCKET_POLICY_STATEMENTS}"; then
        textInfo "${REGION}: Bucket policy does not exist for bucket ${bucket}" "${REGION}"
      else
        BUCKET_POLICY_BAD_STATEMENTS=$(jq --compact-output --arg arn "arn:${AWS_PARTITION}:s3:::$bucket" 'fromjson | .Statement[]|select(
                                        .Effect=="Allow" and
                                        (
                                            ( (.Principal|type == "object") and (.Principal.AWS == "*") ) or
                                            ( (.Principal|type == "string") and (.Principal == "*") )
                                        ) and
                                        (
                                            ( (.Action|type == "string") and (.Action|startswith("s3:Put")) ) or
                                            ( (.Action|type == "string") and (.Action|startswith("s3:*")) ) or
                                            ( (.Action|type == "array") and (.Action[]|startswith("s3:Put")) ) or
                                            ( (.Action|type == "array") and (.Action[]|startswith("s3:*")) )
                                        ) and
                                        .Condition == null
                                      )' <<< "${BUCKET_POLICY_STATEMENTS}" | tr '\n' ' ')
        # Make sure JSON comma characted will not break CSV output. Replace "," by word "[comma]"
        BUCKET_POLICY_BAD_STATEMENTS="${BUCKET_POLICY_BAD_STATEMENTS//,/[comma]}"
        if [[ "${BUCKET_POLICY_BAD_STATEMENTS}" != "" ]]; then
          textFail "${REGION}: Bucket ${bucket} allows public write: ${BUCKET_POLICY_BAD_STATEMENTS}" "${REGION}" "${bucket}"
        else
          textPass "${REGION}: Bucket ${bucket} has S3 bucket policy which does not allow public write access" "${REGION}" "${bucket}"
        fi
      fi
    done
  else
    textInfo "${REGION}: No S3 Buckets found" "${REGION}"
  fi
}

providers/aws/services/s3/check_extra764 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra764="7.64"
CHECK_TITLE_extra764="[extra764] Check if S3 buckets have secure transport policy "
CHECK_SCORED_extra764="NOT_SCORED"
CHECK_CIS_LEVEL_extra764="EXTRA"
CHECK_SEVERITY_extra764="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra764="AwsS3Bucket"
CHECK_ALTERNATE_check764="extra764"
CHECK_ASFF_COMPLIANCE_TYPE_extra764="ens-mp.com.2.aws.s3.1"
CHECK_SERVICENAME_extra764="s3"
CHECK_RISK_extra764='If HTTPS is not enforced on the bucket policy; communication between clients and S3 buckets can use unencrypted HTTP. As a result; sensitive information could be transmitted in clear text over the network or internet.'
CHECK_REMEDIATION_extra764='Ensure that S3 buckets has encryption in transit enabled.'
CHECK_DOC_extra764='https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html'
CHECK_CAF_EPIC_extra764='Data Protection'


extra764(){
  LIST_OF_BUCKETS=$(${AWSCLI} s3api list-buckets ${PROFILE_OPT} --query Buckets[*].Name --output text --region ${REGION}|xargs -n1)
  if [[ $LIST_OF_BUCKETS ]]; then
    for bucket in $LIST_OF_BUCKETS;do
      BUCKET_LOCATION=$(${AWSCLI} s3api get-bucket-location ${PROFILE_OPT} --region ${REGION} --bucket ${bucket} --output text 2>&1)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_LOCATION}"; then
        textInfo "${REGION}: Access Denied Trying to Get Bucket Location for ${bucket}" "${REGION}" "${bucket}"
        continue
      elif grep -E 'NoSuchBucket' <<< "${BUCKET_LOCATION}"; then
        textInfo "${REGION}: NoSuchBucket error Bucket ${bucket} does not exist" "${REGION}" "${bucket}"
        continue
      fi
      if [[ "${BUCKET_LOCATION}" == "None" ]]; then
        BUCKET_LOCATION="us-east-1"
      fi
      if [[ "${BUCKET_LOCATION}" == "EU" ]]; then
        BUCKET_LOCATION="eu-west-1"
      fi
      # get bucket policy
      TEMP_STP_POLICY_FILE=$(${AWSCLI} s3api get-bucket-policy ${PROFILE_OPT} --bucket "${bucket}" --output text --query Policy --region "${BUCKET_LOCATION}" 2>&1)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${TEMP_STP_POLICY_FILE}"; then
        textInfo "${BUCKET_LOCATION}: Access Denied Trying to Get Bucket Policy for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
        continue
      fi
      if grep -q "NoSuchBucketPolicy" <<< "${TEMP_STP_POLICY_FILE}" ; then
        textFail "${BUCKET_LOCATION}: No bucket policy for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
        continue
      fi

      # https://aws.amazon.com/premiumsupport/knowledge-center/s3-bucket-policy-for-config-rule/
      # checking if $TEMP_STP_POLICY_FILE is a valid json before converting it to json with jq
      if jq -e . >/dev/null 2>&1 <<< "${TEMP_STP_POLICY_FILE}"; then
        CHECK_BUCKET_STP_POLICY_PRESENT=$(jq --arg arn "arn:${AWS_PARTITION}:s3:::${bucket}" \
          '.Statement[]|select((((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and .Effect=="Deny" and (.Action=="s3:*" or .Action=="*") and (.Resource|type == "array") and (.Resource|map({(.):0})[]|has($arn)) and (.Resource|map({(.):0})[]|has($arn+"/*")) and .Condition.Bool."aws:SecureTransport" == "false")' <<< "${TEMP_STP_POLICY_FILE}")
        if [[ "${CHECK_BUCKET_STP_POLICY_PRESENT}" ]]; then
          textPass "${BUCKET_LOCATION}: Bucket ${bucket} has S3 bucket policy to deny requests over insecure transport" "${BUCKET_LOCATION}" "${bucket}"
        else
          textFail "${BUCKET_LOCATION}: Bucket ${bucket} allows requests over insecure transport" "${BUCKET_LOCATION}" "${bucket}"
        fi
      else
          textInfo "${BUCKET_LOCATION}: Bucket ${bucket} returned an unknown error" "${BUCKET_LOCATION}" "${bucket}"
      fi
    done

  else
    textInfo "${REGION}: No S3 Buckets found" "${REGION}"
  fi
}

providers/aws/services/s3/check_extra763 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra763="7.63"
CHECK_TITLE_extra763="[extra763] Check if S3 buckets have object versioning enabled "
CHECK_SCORED_extra763="NOT_SCORED"
CHECK_CIS_LEVEL_extra763="EXTRA"
CHECK_SEVERITY_extra763="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra763="AwsS3Bucket"
CHECK_ALTERNATE_check763="extra763"
CHECK_SERVICENAME_extra763="s3"
CHECK_RISK_extra763=' With versioning; you can easily recover from both unintended user actions and application failures.'
CHECK_REMEDIATION_extra763='Configure versioning using the Amazon console or API for buckets with sensitive information that is changing frecuently; and backup may not be enough to capture all the changes.'
CHECK_DOC_extra763='https://docs.aws.amazon.com/AmazonS3/latest/dev-retired/Versioning.html'
CHECK_CAF_EPIC_extra763='Data Protection'

extra763(){
  # "Check if S3 buckets have object versioning enabled "
  LIST_OF_BUCKETS=$($AWSCLI s3api list-buckets ${PROFILE_OPT} --region "${REGION}" --query Buckets[*].Name --output text|xargs -n1)
  if [[ $LIST_OF_BUCKETS ]]; then
    for bucket in $LIST_OF_BUCKETS; do
      # Recover Bucket region
      BUCKET_REGION=$("${AWSCLI}" ${PROFILE_OPT} s3api get-bucket-location --bucket "${bucket}" --region "${REGION}" --query LocationConstraint --output text)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_REGION}"; then
          textInfo "${REGION}: Access Denied trying to get bucket location for ${bucket}" "${REGION}"
          continue
      fi
      # If None use default region
      if [[ "${BUCKET_REGION}" == "None" ]]; then
        BUCKET_REGION="${REGION}"
      fi

      BUCKET_VERSIONING_ENABLED=$("${AWSCLI}" s3api get-bucket-versioning --bucket "${bucket}" ${PROFILE_OPT} --region "${BUCKET_REGION}" --query Status --output text 2>&1)
      if grep -q 'AccessDenied' <<< "${BUCKET_VERSIONING_ENABLED}"; then
        textInfo "${BUCKET_REGION}: Access Denied Trying to Get Bucket Versioning for $bucket"
        continue
      fi
      if  grep -q "^Enabled$" <<< "${BUCKET_VERSIONING_ENABLED}"; then
        textPass "${BUCKET_REGION}: Bucket ${bucket} has versioning enabled" "${BUCKET_REGION}" "${bucket}"
      else
        textFail "${BUCKET_REGION}: Bucket ${bucket} has versioning disabled!" "${BUCKET_REGION}" "${bucket}"
      fi
    done
  else
    textInfo "${REGION}: No S3 Buckets found" "${REGION}"
  fi
}

providers/aws/services/s3/check_extra734 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra734="7.34"
CHECK_TITLE_extra734="[extra734] Check if S3 buckets have default encryption (SSE) enabled or use a bucket policy to enforce it"
CHECK_SCORED_extra734="NOT_SCORED"
CHECK_CIS_LEVEL_extra734="EXTRA"
CHECK_SEVERITY_extra734="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra734="AwsS3Bucket"
CHECK_ALTERNATE_check734="extra734"
CHECK_ASFF_COMPLIANCE_TYPE_extra734="ens-mp.info.3.s3.1"
CHECK_SERVICENAME_extra734="s3"
CHECK_RISK_extra734='Amazon S3 default encryption provides a way to set the default encryption behavior for an S3 bucket. This will ensure data-at-rest is encrypted.'
CHECK_REMEDIATION_extra734='Ensure that S3 buckets has encryption at rest enabled.'
CHECK_DOC_extra734='https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html'
CHECK_CAF_EPIC_extra734='Data Protection'

extra734(){
  LIST_OF_BUCKETS=$("${AWSCLI}" s3api list-buckets ${PROFILE_OPT} --region "${REGION}" --query Buckets[*].Name --output text|xargs -n1)
  if [[ $LIST_OF_BUCKETS ]]; then
    for bucket in $LIST_OF_BUCKETS;do
      BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location ${PROFILE_OPT} --region "${REGION}" --bucket "${bucket}" --output text 2>&1)
      if grep -q 'AccessDenied' <<< "${BUCKET_LOCATION}"; then
        textInfo "${REGION}: Access Denied Trying to Get Bucket Location for ${bucket}" "${REGION}" "${bucket}"
        continue
      fi
      if [[ $BUCKET_LOCATION == "None" ]]; then
        BUCKET_LOCATION="us-east-1"
      fi
      if [[ $BUCKET_LOCATION == "EU" ]]; then
        BUCKET_LOCATION="eu-west-1"
      fi
      # For this test to pass one of the following must be present:
      # - Configure ServerSideEncryptionConfiguration rule for AES256 or aws:kms
      #  OR
      # - Have bucket policy denying s3:PutObject when s3:x-amz-server-side-encryption is absent
      # query to get if has encryption enabled or not
      RESULT=$("${AWSCLI}" s3api get-bucket-encryption ${PROFILE_OPT} --region ${BUCKET_LOCATION} --bucket "${bucket}" --query ServerSideEncryptionConfiguration.Rules[].ApplyServerSideEncryptionByDefault[].SSEAlgorithm --output text 2>&1)
      if grep -q 'AccessDenied' <<< "${RESULT}"; then
        textInfo "${BUCKET_LOCATION}: Access Denied Trying to Get Encryption for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
        continue
      elif grep -q 'ServerSideEncryptionConfigurationNotFoundError' <<< "${RESULT}"
      then
        textFail "${BUCKET_LOCATION}: Server Side Encryption configuration is not configured for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
        continue
      fi

      if [[ "${RESULT}" == "AES256" || "${RESULT}" == "aws:kms" ]];
      then
        textPass "${BUCKET_LOCATION}: Bucket $bucket is enabled for default encryption with ${RESULT}" "${BUCKET_LOCATION}" "${bucket}"
        continue
      fi

      TEMP_SSE_POLICY_FILE=$(mktemp -t prowler-"${ACCOUNT_NUM}"-"${bucket}".policy.XXXXXXXXXX)

      # get bucket policy
      "${AWSCLI}" s3api get-bucket-policy ${PROFILE_OPT} --bucket "${bucket}" --region "${BUCKET_LOCATION}" --output text --query Policy > "${TEMP_SSE_POLICY_FILE}" 2>&1
      if grep -q 'AccessDenied' <<< "${TEMP_SSE_POLICY_FILE}"; then
        textInfo "${BUCKET_LOCATION}: Access Denied Trying to Get Bucket Policy for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
        rm -f "${TEMP_SSE_POLICY_FILE}"
        continue
      fi
      if grep -q 'NoSuchBucketPolicy' <<< "${TEMP_SSE_POLICY_FILE}"; then
        textFail "${BUCKET_LOCATION}: No bucket policy for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
        rm -f "${TEMP_SSE_POLICY_FILE}"
        continue
      fi

      # check if the S3 policy forces SSE s3:x-amz-server-side-encryption:true
      CHECK_BUCKET_SSE_POLICY_PRESENT=$(jq --arg arn "arn:${AWS_PARTITION}:s3:::${bucket}/*" '.Statement[]|select(.Effect=="Deny" and ((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*") and .Action=="s3:PutObject" and .Resource==$arn and .Condition.StringNotEquals."s3:x-amz-server-side-encryption" != null)' < "${TEMP_SSE_POLICY_FILE}")
      if [[ "${CHECK_BUCKET_SSE_POLICY_PRESENT}" == "" ]]; then
        textFail "${BUCKET_LOCATION}: Bucket ${bucket} does not enforce encryption!" "${BUCKET_LOCATION}" "${bucket}"
        rm -f "${TEMP_SSE_POLICY_FILE}"
        continue
      fi
      CHECK_BUCKET_SSE_POLICY_VALUE=$(jq -r '.Condition.StringNotEquals."s3:x-amz-server-side-encryption"' <<< "${CHECK_BUCKET_SSE_POLICY_PRESENT}")

      textPass "${BUCKET_LOCATION}: Bucket ${bucket} has S3 bucket policy to enforce encryption with ${CHECK_BUCKET_SSE_POLICY_VALUE}" "${BUCKET_LOCATION}" "${bucket}"

      rm -f "${TEMP_SSE_POLICY_FILE}"
    done

  else
    textInfo "${REGION}: No S3 Buckets found" "${REGION}"
  fi
}

providers/aws/services/s3/check_extra73 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra73="7.3"
CHECK_TITLE_extra73="[extra73] Ensure there are no S3 buckets open to Everyone or Any AWS user"
CHECK_SCORED_extra73="NOT_SCORED"
CHECK_CIS_LEVEL_extra73="EXTRA"
CHECK_SEVERITY_extra73="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra73="AwsS3Bucket"
CHECK_ALTERNATE_extra703="extra73"
CHECK_ALTERNATE_check73="extra73"
CHECK_ALTERNATE_check703="extra73"
CHECK_SERVICENAME_extra73="s3"
CHECK_RISK_extra73='Even if you enable all possible bucket ACL options available in the Amazon S3 console the ACL alone does not allow everyone to download objects from your bucket. Depending on which option you select any user could perform some actions.'
CHECK_REMEDIATION_extra73='You can enable block public access settings only for access points; buckets; and AWS accounts. Amazon S3 does not support block public access settings on a per-object basis. When you apply block public access settings to an account; the settings apply to all AWS Regions globally. The settings might not take effect in all Regions immediately or simultaneously; but they eventually propagate to all Regions.'
CHECK_DOC_extra73='https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html'
CHECK_CAF_EPIC_extra73='Data Protection'

# Verified with AWS support that if get-bucket-acl doesn't return a grant
# for All and get-bucket-policy-status returns IsPublic false or bad request
# (no policy) then the bucket can be considered not public - though
# individual objects may still be. If in addition put-public-access-block is
# used to set IgnorePublicAcls and RestrictPublicBuckets to true then that
# causes Amazon S3 to ignore all public ACLs on a bucket and any objects that
# it contains.
#
# This check does not address legacy ACLs or policies that would give
# public access if not blocked at account or bucket level, instead it tries
# to reward the use of more broadly restrictive controls with quicker and less
# computational intensive checks.
#
# If we are assembling an inventory then maybe that is not what we want but
# for day to day usage that is probably desirable.

extra73(){
  #
  # If public ACLs disabled at account level then look no further
  #
  ACCOUNT_PUBLIC_ACCESS_BLOCK=$($AWSCLI s3control get-public-access-block $PROFILE_OPT --region $REGION --account-id $ACCOUNT_NUM --output json 2>&1)
  if [[ $(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | grep AccessDenied) ]]; then
    textInfo "$REGION: Access Denied getting PublicAccessBlock configuration for AWS account" "$REGION" "$bucket"
    return
  fi
  if [[ $(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | grep NoSuchPublicAccessBlockConfiguration) ]]; then
    ACCOUNTIGNOREPUBLICACLS=""
    ACCOUNTRESTRICTPUBLICBUCKETS=""
  else
    ACCOUNTIGNOREPUBLICACLS=$(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.IgnorePublicAcls')
    ACCOUNTRESTRICTPUBLICBUCKETS=$(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.RestrictPublicBuckets')
  fi
  if [[ $ACCOUNTIGNOREPUBLICACLS == "true" && $ACCOUNTRESTRICTPUBLICBUCKETS == "true" ]]; then
    textPass "$REGION: All S3 public access blocked at account level" "$REGION" "$bucket"
    return
  fi

  #
  # Otherwise start to iterate bucket
  #
  ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --output text 2>&1)
  if [[ $(echo "$ALL_BUCKETS_LIST" | grep AccessDenied) ]]; then
    textInfo "$REGION: Access Denied Trying to List Buckets" "$REGION" "$bucket"
    return
  fi
  if [[ "$ALL_BUCKETS_LIST" == "" ]]; then
    textInfo "$REGION: No buckets found" "$REGION" "$bucket"
    return
  fi

  for bucket in $ALL_BUCKETS_LIST; do

    #
    # LOCATION - requests referencing buckets created after March 20, 2019
    # must be made to S3 endpoints in the same region as the bucket was
    # created.
    #
    BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $REGION --bucket $bucket --output text 2>&1)
    if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
      textInfo "$REGION: Access Denied Trying to Get Bucket Location for $bucket" "$REGION" "$bucket"
      continue
    fi
    if [[ $BUCKET_LOCATION == "None" ]]; then
      BUCKET_LOCATION="us-east-1"
    fi
    if [[ $BUCKET_LOCATION == "EU" ]]; then
      BUCKET_LOCATION="eu-west-1"
    fi
    #
    # If public ACLs disabled at bucket level then look no further
    #
    BUCKET_PUBLIC_ACCESS_BLOCK=$($AWSCLI s3api get-public-access-block $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output json 2>&1)
    if [[ $(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | grep AccessDenied) ]]; then
      textInfo "$BUCKET_LOCATION: Access Denied Trying to Get Public Access Block for $bucket" "$BUCKET_LOCATION" "$bucket"
      continue
    fi
    if [[ $(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | grep NoSuchPublicAccessBlockConfiguration) ]]; then
      BUCKETIGNOREPUBLICACLS=""
      BUCKETRESTRICTPUBLICBUCKETS=""
    else
      BUCKETIGNOREPUBLICACLS=$(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.IgnorePublicAcls')
      BUCKETRESTRICTPUBLICBUCKETS=$(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.RestrictPublicBuckets')
    fi
    if [[ $BUCKETIGNOREPUBLICACLS == "true" && $BUCKETRESTRICTPUBLICBUCKETS == "true" ]]; then
      textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION" "$bucket"
      continue
    fi

    #
    # Check for public ACL grants
    #
    BUCKET_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output json 2>&1)
    if [[ $(echo "$BUCKET_ACL" | grep AccessDenied) ]]; then
      textInfo "$BUCKET_LOCATION: Access Denied Trying to Get Bucket Acl for $bucket" "$BUCKET_LOCATION" "$bucket"
      continue
    fi

    ALLUSERS_ACL=$(echo "$BUCKET_ACL" | jq '.Grants[]|select(.Grantee.URI != null)|select(.Grantee.URI | endswith("/AllUsers"))')
    if [[ $ALLUSERS_ACL != "" ]]; then
      textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION" "$bucket"
      continue
    fi

    AUTHENTICATEDUSERS_ACL=$(echo "$BUCKET_ACL" | jq '.Grants[]|select(.Grantee.URI != null)|select(.Grantee.URI | endswith("/AuthenticatedUsers"))')
    if [[ $AUTHENTICATEDUSERS_ACL != "" ]]; then
      textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION" "$bucket"
      continue
    fi

    #
    # Check for public access in policy
    #
    BUCKET_POLICY_STATUS=$($AWSCLI s3api get-bucket-policy-status $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query PolicyStatus.IsPublic --output text 2>&1)
    if [[ $(echo "$BUCKET_POLICY_STATUS" | grep AccessDenied) ]]; then
      textInfo "$BUCKET_LOCATION: Access Denied Trying to Get Bucket Policy Status for $bucket" "$BUCKET_LOCATION" "$bucket"
      continue
    fi
    if [[ $(echo "$BUCKET_POLICY_STATUS" | grep NoSuchBucketPolicy) ]]; then
      BUCKET_POLICY_STATUS="False"
    fi

    if [[ $BUCKET_POLICY_STATUS != "" && $BUCKET_POLICY_STATUS != "False" ]]; then
      textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION" "$bucket"
      continue
    fi

    textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION" "$bucket"

  done
}

providers/aws/services/s3/check_extra725 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra725="7.25"
CHECK_TITLE_extra725="[extra725] Check if S3 buckets have Object-level logging enabled in CloudTrail"
CHECK_SCORED_extra725="NOT_SCORED"
CHECK_CIS_LEVEL_extra725="EXTRA"
CHECK_SEVERITY_extra725="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra725="AwsS3Bucket"
CHECK_ALTERNATE_check725="extra725"
CHECK_SERVICENAME_extra725="s3"
CHECK_RISK_extra725='If logs are not enabled; monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra725='Enable logs. Create an S3 lifecycle policy. Define use cases; metrics and automated responses where applicable.'
CHECK_DOC_extra725='https://docs.aws.amazon.com/AmazonS3/latest/userguide/enable-cloudtrail-logging-for-s3.html'
CHECK_CAF_EPIC_extra725='Logging and Monitoring'

# per Object-level logging is not configured at Bucket level but at CloudTrail trail level
extra725(){
  # "Check if S3 buckets have Object-level logging enabled in CloudTrail "
  LIST_OF_BUCKETS=$($AWSCLI s3api list-buckets $PROFILE_OPT --region $REGION --query 'Buckets[*].{Name:Name}' --output text 2>&1)
  if [[ $(echo "$LIST_OF_BUCKETS" | grep AccessDenied) ]]; then
    textInfo "$REGION: Access Denied trying to list buckets"
    return
  fi
  LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[].TrailARN' --output text 2>&1)
  if [[ $(echo "$LIST_OF_TRAILS" | grep AccessDenied) ]]; then
    textInfo "$REGION: Access Denied trying to describe trails"
    return
  fi
  if [[ $LIST_OF_BUCKETS ]]; then
    for bucketName in $LIST_OF_BUCKETS; do
      if [[ $LIST_OF_TRAILS ]]; then
        BUCKET_ENABLED_TRAILS=()

        for trail in $LIST_OF_TRAILS; do
          BUCKET_ENABLED_IN_TRAIL=$($AWSCLI cloudtrail get-event-selectors --region $REGION $PROFILE_OPT --trail-name $trail --query "EventSelectors[*].DataResources[?Type == \`AWS::S3::Object\`].Values" --output text |xargs -n1| grep -E "^arn:${AWS_PARTITION}:s3:::$bucketName/\S*$|^arn:${AWS_PARTITION}:s3$|^arn:${AWS_PARTITION}:s3:::$")
          if [[ $BUCKET_ENABLED_IN_TRAIL ]]; then
            BUCKET_ENABLED_TRAILS+=($trail)
          fi
        done

        if [[ ${#BUCKET_ENABLED_TRAILS[@]} -gt 0 ]]; then
          for trail in "${BUCKET_ENABLED_TRAILS[@]}"; do
            textPass "$REGION: S3 bucket $bucketName has Object-level logging enabled in trail $trail" "$REGION" "$bucketName"
          done
        else
          textFail "$REGION: S3 bucket $bucketName has Object-level logging disabled" "$REGION" "$bucketName"
        fi

      else
        textFail "$REGION: S3 bucket $bucketName is not being recorded no CloudTrail found!" "$REGION" "$bucketName"
      fi
    done
  else
    textInfo "$REGION: No S3 buckets found" "$REGION"
  fi
}

providers/aws/services/s3/check_extra718 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra718="7.18"
CHECK_TITLE_extra718="[extra718] Check if S3 buckets have server access logging enabled"
CHECK_SCORED_extra718="NOT_SCORED"
CHECK_CIS_LEVEL_extra718="EXTRA"
CHECK_SEVERITY_extra718="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra718="AwsS3Bucket"
CHECK_ALTERNATE_check718="extra718"
CHECK_SERVICENAME_extra718="s3"
CHECK_RISK_extra718='Server access logs can assist you in security and access audits; help you learn about your customer base; and understand your Amazon S3 bill.'
CHECK_REMEDIATION_extra718='Ensure that S3 buckets have Logging enabled. CloudTrail data events can be used in place of S3 bucket logging. If that is the case; this finding can be considered a false positive.'
CHECK_DOC_extra718='https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html'
CHECK_CAF_EPIC_extra718='Logging and Monitoring'

extra718(){
  # "Check if S3 buckets have server access logging enabled "
  LIST_OF_BUCKETS=$("${AWSCLI}" s3api list-buckets ${PROFILE_OPT} --query Buckets[*].Name --output text|xargs -n1)
  if [[ $LIST_OF_BUCKETS ]]; then
    for bucket in $LIST_OF_BUCKETS;do
      # Recover Bucket region
      BUCKET_REGION=$("${AWSCLI}" ${PROFILE_OPT} s3api get-bucket-location --bucket "${bucket}" --region "${REGION}" --query LocationConstraint --output text)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_REGION}"; then
          textInfo "${REGION}: Access Denied trying to get bucket location for ${bucket}" "${REGION}"
          continue
      fi
      # If None use default region
      if [[ "${BUCKET_REGION}" == "None" ]]; then
        BUCKET_REGION="${REGION}"
      fi

      BUCKET_SERVER_LOG_ENABLED=$("${AWSCLI}" s3api get-bucket-logging --bucket "${bucket}" ${PROFILE_OPT} --region "${BUCKET_REGION}" --query [LoggingEnabled] --output text 2>&1)
      if grep -q AccessDenied <<< "${BUCKET_SERVER_LOG_ENABLED}"; then
        textInfo "${BUCKET_REGION}: Access Denied Trying to Get Bucket Logging for ${bucket}" "${BUCKET_REGION}" "${bucket}"
        continue
      fi
      if grep -q "^None$" <<< "${BUCKET_SERVER_LOG_ENABLED}"; then
        textFail "${BUCKET_REGION}: Bucket ${bucket} has server access logging disabled!" "${BUCKET_REGION}" "${bucket}"
      else
        textPass "${BUCKET_REGION}: Bucket ${bucket} has server access logging enabled" "${BUCKET_REGION}" "${bucket}"
      fi
    done
  else
    textInfo "${REGION}: No S3 Buckets found" "${REGION}" "${bucket}"
  fi
}

providers/aws/services/s3/check_extra7172 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7172="7.172"
CHECK_TITLE_extra7172="[extra7172] Check if S3 buckets have ACLs enabled"
CHECK_SCORED_extra7172="NOT_SCORED"
CHECK_CIS_LEVEL_extra7172="EXTRA"
CHECK_SEVERITY_extra7172="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7172="AwsS3Bucket"
CHECK_ALTERNATE_check7172="extra7172"
CHECK_SERVICENAME_extra7172="s3"
CHECK_RISK_extra7172='S3 ACLs are a legacy access control mechanism that predates IAM. IAM and bucket policies are currently the preferred methods.'
CHECK_REMEDIATION_extra7172='Ensure that S3 ACLs are disabled (BucketOwnerEnforced). Use IAM policies and bucket policies to manage access.'
CHECK_DOC_extra7172='https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html'
CHECK_CAF_EPIC_extra7172='Logging and Monitoring'

extra7172(){
  # "Check if S3 buckets have server access logging enabled"
  LIST_OF_BUCKETS=$("${AWSCLI}" s3api list-buckets ${PROFILE_OPT} --query Buckets[*].Name --region "${REGION}" --output text 2>&1)
  if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "$LIST_OF_BUCKETS"; then
    textInfo "${REGION}: Access Denied Trying to list buckets" "${REGION}"
    exit
  fi
  if [[ $LIST_OF_BUCKETS ]]; then
    for bucket in $LIST_OF_BUCKETS;do
      # Recover Bucket region
      BUCKET_REGION=$("${AWSCLI}" ${PROFILE_OPT} s3api get-bucket-location --bucket "${bucket}" --region "${REGION}" --query LocationConstraint --output text)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_REGION}"; then
          textInfo "${REGION}: Access Denied trying to get bucket location for ${bucket}" "${REGION}"
      fi
      # If None use default region
      if [[ "${BUCKET_REGION}" == "None" ]]; then
        BUCKET_REGION="${REGION}"
      fi

      BUCKET_ACLS_DISABLED=$(${AWSCLI} ${PROFILE_OPT} s3api get-bucket-ownership-controls --bucket "${bucket}" --region "${BUCKET_REGION}" --output text 2>&1)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_ACLS_DISABLED}" ; then
        textInfo "${BUCKET_REGION}: Access Denied Trying to Get Bucket Ownership Controls for ${bucket}" "${BUCKET_REGION}" "${bucket}"
        continue
      elif grep -q -E 'IllegalLocationConstraintException' <<< "${BUCKET_ACLS_DISABLED}"; then
        textInfo "${BUCKET_REGION}: Location Constraint Trying to Get Bucket Ownership Controls for ${bucket}" "${BUCKET_REGION}" "${bucket}"
        continue
      fi
      if grep -q "BucketOwnerEnforced" <<< "${BUCKET_ACLS_DISABLED}"; then
        textPass "${BUCKET_REGION}: Bucket ${bucket} has bucket ACLs disabled!" "${BUCKET_REGION}" "${bucket}"
      elif grep -q "BucketOwnerPreferred" <<< "${BUCKET_ACLS_DISABLED}"; then
        textFail "${BUCKET_REGION}: Bucket ${bucket} has bucket ACLs enabled!" "${BUCKET_REGION}" "${bucket}"
      elif grep -q "OwnershipControlsNotFoundError" <<< "${BUCKET_ACLS_DISABLED}"; then
        textFail "${BUCKET_REGION}: Bucket ${bucket} has bucket ACLs enabled!" "${BUCKET_REGION}" "${bucket}"
      else
        textFail "${BUCKET_REGION}: Bucket ${bucket} returned an unknown error" "${BUCKET_REGION}" "${bucket}"
      fi
    done
  else
    textInfo "${REGION}: No S3 Buckets found" "${REGION}"
  fi
}

providers/aws/services/s3/check38 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name S3BucketConfigChanges \
#     --filter-pattern '{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }' \
#     --metric-transformations metricName=S3BucketEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name S3BucketConfigChangesAlarm \
#     --alarm-description "Triggered by AWS S3 Bucket config changes." \
#     --metric-name S3BucketEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check38="3.8"
CHECK_TITLE_check38="[check38] Ensure a log metric filter and alarm exist for S3 bucket policy changes"
CHECK_SCORED_check38="SCORED"
CHECK_CIS_LEVEL_check38="LEVEL1"
CHECK_SEVERITY_check38="Medium"
CHECK_ASFF_TYPE_check38="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check38="AwsCloudTrailTrail"
CHECK_ALTERNATE_check308="check38"
CHECK_SERVICENAME_check38="s3"
CHECK_RISK_check38='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check38='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check38='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check38='Logging and Monitoring'

check38(){
  check3x '\$\.eventSource\s*=\s*s3.amazonaws.com.+\$\.eventName\s*=\s*PutBucketAcl.+\$\.eventName\s*=\s*PutBucketPolicy.+\$\.eventName\s*=\s*PutBucketCors.+\$\.eventName\s*=\s*PutBucketLifecycle.+\$\.eventName\s*=\s*PutBucketReplication.+\$\.eventName\s*=\s*DeleteBucketPolicy.+\$\.eventName\s*=\s*DeleteBucketCors.+\$\.eventName\s*=\s*DeleteBucketLifecycle.+\$\.eventName\s*=\s*DeleteBucketReplication'
}

providers/aws/services/s3/check26 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check26="2.6"
CHECK_TITLE_check26="[check26] Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket"
CHECK_SCORED_check26="SCORED"
CHECK_CIS_LEVEL_check26="LEVEL1"
CHECK_SEVERITY_check26="Medium"
CHECK_ASFF_TYPE_check26="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check26="AwsS3Bucket"
CHECK_ALTERNATE_check206="check26"
CHECK_SERVICENAME_check26="s3"
CHECK_RISK_check26='Server access logs can assist you in security and access audits; help you learn about your customer base; and understand your Amazon S3 bill.'
CHECK_REMEDIATION_check26='Ensure that S3 buckets have Logging enabled. CloudTrail data events can be used in place of S3 bucket logging. If that is the case; this finding can be considered a false positive.'
CHECK_DOC_check26='https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html'
CHECK_CAF_EPIC_check26='Logging and Monitoring'

check26(){
  trail_count=0
  # "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)"
  for regx in $REGIONS; do
    TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr "	" ',')
    if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
      continue
    fi
    if [[ $TRAILS_AND_REGIONS ]]; then
      for reg_trail in $TRAILS_AND_REGIONS; do
        TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
        if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
          continue
        fi
        trail=$(echo $reg_trail | cut -d',' -f2)
        trail_count=$((trail_count + 1))

        CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].[S3BucketName]' --output text --trail-name-list $trail)
        if [[ -z $CLOUDTRAILBUCKET ]]; then
          textFail "$regx: Trail $trail does not publish to S3" "$TRAIL_REGION" "$trail"
          continue
        fi

        CLOUDTRAIL_ACCOUNT_ID=$(echo $trail | awk -F: '{ print $5 }')
        if [ "$CLOUDTRAIL_ACCOUNT_ID" != "$ACCOUNT_NUM" ]; then
          textInfo "$regx: Trail $trail S3 logging bucket $CLOUDTRAILBUCKET is not in current account" "$TRAIL_REGION" "$trail"
          continue
        fi

        #
        # LOCATION - requests referencing buckets created after March 20, 2019
        # must be made to S3 endpoints in the same region as the bucket was
        # created.
        #
        BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $CLOUDTRAILBUCKET --output text 2>&1)
        if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
          textInfo "$regx: Trail $trail Access Denied getting bucket location for $CLOUDTRAILBUCKET" "$TRAIL_REGION" "$trail"
          continue
        fi
        if [[ $BUCKET_LOCATION == "None" ]]; then
          BUCKET_LOCATION="us-east-1"
        fi
        if [[ $BUCKET_LOCATION == "EU" ]]; then
          BUCKET_LOCATION="eu-west-1"
        fi

        CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $CLOUDTRAILBUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'LoggingEnabled.TargetBucket' --output text 2>&1)
        if [[ $(echo "$CLOUDTRAILBUCKET_LOGENABLED" | grep AccessDenied) ]]; then
          textInfo "$regx: Trail $trail Access Denied getting bucket logging for $CLOUDTRAILBUCKET" "$TRAIL_REGION" "$trail"
          continue
        fi

        if [[ $CLOUDTRAILBUCKET_LOGENABLED != "None" ]]; then
          textPass "$regx: Trail $trail S3 bucket access logging is enabled for $CLOUDTRAILBUCKET" "$TRAIL_REGION" "$trail"
        else
          textFail "$regx: Trail $trail S3 bucket access logging is not enabled for $CLOUDTRAILBUCKET" "$TRAIL_REGION" "$trail"
        fi

      done
    fi
  done
  if [[ $trail_count == 0 ]]; then
    textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
  fi
}

providers/aws/services/route53/check_extra719 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra719="7.19"
CHECK_TITLE_extra719="[extra719] Check if Route53 public hosted zones are logging queries to CloudWatch Logs"
CHECK_SCORED_extra719="NOT_SCORED"
CHECK_CIS_LEVEL_extra719="EXTRA"
CHECK_SEVERITY_extra719="Medium"
CHECK_ALTERNATE_check719="extra719"
CHECK_ASFF_RESOURCE_TYPE_extra719="AwsRoute53HostedZone"
CHECK_SERVICENAME_extra719="route53"
CHECK_RISK_extra719='If logs are not enabled; monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra719='Enable CloudWatch logs and define metrics and uses cases for the events recorded.'
CHECK_DOC_extra719='https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/monitoring-hosted-zones-with-cloudwatch.html'
CHECK_CAF_EPIC_extra719='Logging and Monitoring'

extra719(){
  # You can't create a query logging config for a private hosted zone.
  LIST_OF_HOSTED_ZONES=$($AWSCLI route53 list-hosted-zones $PROFILE_OPT | jq -r ".HostedZones[] | select(.Config.PrivateZone == false) | .Id")
  if [[ $LIST_OF_HOSTED_ZONES ]]; then
    for hostedzoneid in $LIST_OF_HOSTED_ZONES;do
      HOSTED_ZONE_QUERY_LOG_ENABLED=$($AWSCLI route53 list-query-logging-configs --hosted-zone-id $hostedzoneid $PROFILE_OPT --query QueryLoggingConfigs[*].CloudWatchLogsLogGroupArn --output text|cut -d: -f7)
      if [[ $HOSTED_ZONE_QUERY_LOG_ENABLED ]];then
        textPass "$REGION: Route53 public hosted zone Id $hostedzoneid has query logging enabled in Log Group $HOSTED_ZONE_QUERY_LOG_ENABLED" "$REGION" "$hostedzoneid"
      else
        textFail "$REGION: Route53 public hosted zone Id $hostedzoneid has query logging disabled!" "$REGION" "$hostedzoneid"
      fi
    done
  else
    textInfo "$REGION: No Route53 hosted zones found" "$REGION"
  fi
}

providers/aws/services/route53/check_extra7153 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation:
#
#   https://docs.aws.amazon.com/cli/latest/reference/route53domains/enable-domain-transfer-lock.html
#
#   enable-domain-transfer-lock \
#   --domain-name example.com


CHECK_ID_extra7153="7.153"
CHECK_TITLE_extra7153="[extra7153] Enable Transfer Lock for a Route53 Domain (us-east-1 only)"
CHECK_SCORED_extra7153="NOT_SCORED"
CHECK_CIS_LEVEL_extra7153="EXTRA"
CHECK_SEVERITY_extra7153="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7153="AwsRoute53Domain"
CHECK_ALTERNATE_check7153="extra7153"
CHECK_SERVICENAME_extra7153="route53"
CHECK_RISK_extra7153='Without transfer lock enabled; a domain name could be incorrectly moved to a new registrar'
CHECK_REMEDIATION_extra7153='Ensure transfer lock is enabled'
CHECK_DOC_extra7153='https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-lock.html'
CHECK_CAF_EPIC_extra7153='Data Protection'

extra7153(){
    # Route53 is a global service, looking for domains in US-EAST-1
    # this is also valid for GovCloud https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/setting-up-route53.html
    DOMAIN_NAMES=$($AWSCLI route53domains list-domains $PROFILE_OPT --region us-east-1 --query 'Domains[*].DomainName' --output text )
    if [[ $DOMAIN_NAMES ]];then
      for domain_name in $DOMAIN_NAMES;do
        DOMAIN_DETAIL=$($AWSCLI route53domains get-domain-detail $PROFILE_OPT --region us-east-1 --query 'StatusList' --domain-name $domain_name)
        HAS_TRANSFER_LOCK=$( grep -o 'clientTransferProhibited' <<< $DOMAIN_DETAIL)
        if [[ $HAS_TRANSFER_LOCK ]]; then
          textPass "us-east-1: clientTransferProhibited found for: $domain_name" "us-east-1" "$domain_name"
          else
          textFail "us-east-1: clientTransferProhibited not found for: $domain_name" "us-east-1" "$domain_name"
        fi
      done
      else
      textInfo "us-east-1: No Domain Names found" "us-east-1"
    fi
}

providers/aws/services/route53/check_extra7152 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation:
#
#   https://docs.aws.amazon.com/cli/latest/reference/route53domains/update-domain-contact-privacy.html
#
#   update-domain-contact-privacy \
#   --region us-east-1 \
#   --domain-name example.com \
#   --admin-privacy \
#   --registrant-privacy \
#   --tech-privacy

CHECK_ID_extra7152="7.152"
CHECK_TITLE_extra7152="[extra7152] Enable Privacy Protection for for a Route53 Domain (us-east-1 only)"
CHECK_SCORED_extra7152="NOT_SCORED"
CHECK_CIS_LEVEL_extra7152="EXTRA"
CHECK_SEVERITY_extra7152="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7152="AwsRoute53Domain"
CHECK_ALTERNATE_check7152="extra7152"
CHECK_SERVICENAME_extra7152="route53"
CHECK_RISK_extra7152='Without privacy protection enabled; ones personal information is published to the public WHOIS database'
CHECK_REMEDIATION_extra7152='Ensure default Privacy is enabled'
CHECK_DOC_extra7152='https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-privacy-protection.html'
CHECK_CAF_EPIC_extra7152='Data Protection'

extra7152(){
    # Route53 is a global service, looking for domains in US-EAST-1
    # this is also valid for GovCloud https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/setting-up-route53.html
    DOMAIN_NAMES=$($AWSCLI route53domains list-domains $PROFILE_OPT --region us-east-1 --query 'Domains[*].DomainName' --output text )
    if [[ $DOMAIN_NAMES ]];then
      for domain_name in $DOMAIN_NAMES;do
        DOMAIN_DETAIL=$($AWSCLI route53domains get-domain-detail $PROFILE_OPT --region us-east-1 --query 'AdminPrivacy' --domain-name $domain_name)
        if [[ $DOMAIN_DETAIL == false ]]; then
          textFail "us-east-1: Contact information public for: $domain_name" "us-east-1" "$domain_name"
          else
          textPass "us-east-1: All contact information is private for: $domain_name" "us-east-1" "$domain_name"
        fi
      done
      else
      textInfo "us-east-1: No Domain Names found" "us-east-1"
    fi
}

providers/aws/services/redshift/check_extra721 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra721="7.21"
CHECK_TITLE_extra721="[extra721] Check if Redshift cluster has audit logging enabled"
CHECK_SCORED_extra721="NOT_SCORED"
CHECK_CIS_LEVEL_extra721="EXTRA"
CHECK_SEVERITY_extra721="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra721="AwsRedshiftCluster"
CHECK_ALTERNATE_check721="extra721"
CHECK_SERVICENAME_extra721="redshift"
CHECK_RISK_extra721='If logs are not enabled; monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra721='Enable logs. Create an S3 lifecycle policy. Define use cases; metrics and automated responses where applicable.'
CHECK_DOC_extra721='https://docs.aws.amazon.com/redshift/latest/mgmt/db-auditing.html'
CHECK_CAF_EPIC_extra721='Logging and Monitoring'

extra721(){
  # "Check if Redshift cluster has audit logging enabled "
  for regx in $REGIONS; do
    LIST_OF_REDSHIFT_CLUSTERS=$($AWSCLI redshift describe-clusters $PROFILE_OPT --region $regx --query 'Clusters[*].ClusterIdentifier' --output text 2>&1)
    if [[ $(echo "$LIST_OF_REDSHIFT_CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe clusters" "$regx"
        continue
    fi
    if [[ $LIST_OF_REDSHIFT_CLUSTERS ]]; then
      for redshiftcluster in $LIST_OF_REDSHIFT_CLUSTERS;do
        REDSHIFT_LOG_ENABLED=$($AWSCLI redshift describe-logging-status $PROFILE_OPT --region $regx --cluster-identifier $redshiftcluster --query LoggingEnabled --output text | grep True)
        if [[ $REDSHIFT_LOG_ENABLED ]];then
          REDSHIFT_LOG_ENABLED_BUCKET=$($AWSCLI redshift describe-logging-status $PROFILE_OPT --region $regx --cluster-identifier $redshiftcluster --query BucketName --output text)
          textPass "$regx: Redshift cluster $redshiftcluster has audit logging enabled to bucket $REDSHIFT_LOG_ENABLED_BUCKET" "$regx" "$redshiftcluster"
        else
          textFail "$regx: Redshift cluster $redshiftcluster logging disabled!" "$regx" "$redshiftcluster"
        fi
      done
    else
      textInfo "$regx: No Redshift cluster configured" "$regx"
    fi
  done
}

providers/aws/services/redshift/check_extra7160 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2021) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7160="7.160"
CHECK_TITLE_extra7160="[extra7160] Check if Redshift has automatic upgrades enabled"
CHECK_SCORED_extra7160="NOT_SCORED"
CHECK_CIS_LEVEL_extra7160="EXTRA"
CHECK_SEVERITY_extra7160="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7160="AwsRedshift"
CHECK_ALTERNATE_check7160="extra7160"
CHECK_SERVICENAME_extra7160="redshift"
CHECK_RISK_extra7160='Without automatic version upgrade enabled; a critical Redshift Cluster version can become severly out of date.'
CHECK_REMEDIATION_extra7160='Enabled AutomaticVersionUpgrade on Redshift Cluster'
CHECK_DOC_extra7160='https://docs.aws.amazon.com/redshift/latest/mgmt/managing-cluster-operations.html'
CHECK_CAF_EPIC_extra7160='Infrastructure Security'

extra7160(){
  for regx in $REGIONS; do
    LIST_OF_CLUSTERS=$($AWSCLI redshift describe-clusters $PROFILE_OPT --query 'Clusters[*].ClusterIdentifier' --region $regx --output text 2>&1)
    if [[ $(echo "$LIST_OF_CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe clusters" "$regx"
        continue
    fi
    if [[ $LIST_OF_CLUSTERS ]]; then
      for cluster in $LIST_OF_CLUSTERS; do
        AUTO_UPGRADE_ENABLED=$($AWSCLI redshift describe-clusters $PROFILE_OPT --cluster-identifier $cluster --query 'Clusters[*].AllowVersionUpgrade' --region $regx --output text)
        if [[ $AUTO_UPGRADE_ENABLED == "True" ]]; then
            textPass "$regx: $cluster has AllowVersionUpgrade enabled" "$regx" "$cluster"
        else
            textFail "$regx: $cluster has AllowVersionUpgrade disabled" "$regx" "$cluster"
        fi
      done
    else
      textInfo "$regx: No Redshift Clusters found" "$regx"
    fi
  done
}

providers/aws/services/redshift/check_extra7149 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7149="7.149"
CHECK_TITLE_extra7149="[extra7149] Check if Redshift Clusters have automated snapshots enabled"
CHECK_SCORED_extra7149="NOT_SCORED"
CHECK_CIS_LEVEL_extra7149="EXTRA"
CHECK_SEVERITY_extra7149="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7149="AwsRedshiftCluster"
CHECK_ALTERNATE_check7149="extra7149"
CHECK_SERVICENAME_extra7149="redshift"
CHECK_RISK_extra7149='If backup is not enabled; data is vulnerable. Human error or bad actors could erase or modify data.'
CHECK_REMEDIATION_extra7149='Enable automated backup for production data. Define a retention period and periodically test backup restoration. A Disaster Recovery process should be in place to govern Data Protection approach.'
CHECK_DOC_extra7149='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Redshift.html'
CHECK_CAF_EPIC_extra7149='Data Protection'

extra7149() {
    # "Check if Redshift cluster has audit logging enabled "
    for regx in $REGIONS; do
        LIST_OF_REDSHIFT_CLUSTERS=$($AWSCLI redshift describe-clusters $PROFILE_OPT --region $regx --query 'Clusters[*].ClusterIdentifier' --output text 2>&1)
        if [[ $(echo "$LIST_OF_REDSHIFT_CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to describe clusters" "$regx"
            continue
        fi
        if [[ $LIST_OF_REDSHIFT_CLUSTERS ]]; then
            for redshiftcluster in $LIST_OF_REDSHIFT_CLUSTERS; do
                REDSHIFT_SNAPSHOT_ENABLED=$($AWSCLI redshift describe-cluster-snapshots $PROFILE_OPT --region $regx --cluster-identifier $redshiftcluster --snapshot-type automated)
                if [[ $REDSHIFT_SNAPSHOT_ENABLED ]]; then
                    textPass "$regx: Redshift cluster $redshiftcluster has automated snapshots." "$regx" "$redshiftcluster"
                else
                    textFail "$regx: Redshift cluster $redshiftcluster has automated snapshots disabled!" "$regx" "$redshiftcluster"
                fi
            done
        else
            textInfo "$regx: No Redshift cluster configured" "$regx"
        fi
    done
}

providers/aws/services/redshift/check_extra711 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra711="7.11"
CHECK_TITLE_extra711="[extra711] Check for Publicly Accessible Redshift Clusters"
CHECK_SCORED_extra711="NOT_SCORED"
CHECK_CIS_LEVEL_extra711="EXTRA"
CHECK_SEVERITY_extra711="High"
CHECK_ASFF_RESOURCE_TYPE_extra711="AwsRedshiftCluster"
CHECK_ALTERNATE_check711="extra711"
CHECK_SERVICENAME_extra711="redshift"
CHECK_RISK_extra711='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra711='List all shared Redshift clusters and make sure there is a business reason for them.'
CHECK_DOC_extra711='https://docs.aws.amazon.com/redshift/latest/mgmt/managing-clusters-vpc.html'
CHECK_CAF_EPIC_extra711='Data Protection'

extra711(){
  # "Check for Publicly Accessible Redshift Clusters "
  for regx in $REGIONS; do
    LIST_OF_PUBLIC_REDSHIFT_CLUSTERS=$($AWSCLI redshift describe-clusters $PROFILE_OPT --region $regx --query 'Clusters[?PubliclyAccessible == `true`].[ClusterIdentifier,Endpoint.Address]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_PUBLIC_REDSHIFT_CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe clusters" "$regx"
        continue
    fi
    if [[ $LIST_OF_PUBLIC_REDSHIFT_CLUSTERS ]];then
      while read -r cluster;do
        CLUSTER_ID=$(echo $cluster | awk '{ print $1; }')
        CLUSTER_ENDPOINT=$(echo $cluster | awk '{ print $2; }')
        textFail "$regx: Cluster: $CLUSTER_ID at Endpoint: $CLUSTER_ENDPOINT is publicly accessible!" "$regx" "$CLUSTER_ID"
      done <<< "$LIST_OF_PUBLIC_REDSHIFT_CLUSTERS"
      else
        textPass "$regx: no Publicly Accessible Redshift Clusters found" "$regx" "$CLUSTER_ID"
    fi
  done
}

providers/aws/services/rds/check_extra78 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra78="7.8"
CHECK_TITLE_extra78="[extra78] Ensure there are no Public Accessible RDS instances"
CHECK_SCORED_extra78="NOT_SCORED"
CHECK_CIS_LEVEL_extra78="EXTRA"
CHECK_SEVERITY_extra78="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra78="AwsRdsDbInstance"
CHECK_ALTERNATE_extra708="extra78"
CHECK_ALTERNATE_check78="extra78"
CHECK_ALTERNATE_check708="extra78"
CHECK_SERVICENAME_extra78="rds"
CHECK_RISK_extra78='Publicly accessible databases could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra78='Using an AWS Config rule check for RDS public instances periodically and check there is a business reason for it.'
CHECK_DOC_extra78='https://docs.amazonaws.cn/en_us/config/latest/developerguide/rds-instance-public-access-check.html'
CHECK_CAF_EPIC_extra78='Data Protection'

extra78(){
  # "Ensure there are no Public Accessible RDS instances "
  for regx in $REGIONS; do
    LIST_OF_RDS_PUBLIC_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[?PubliclyAccessible==`true` && DBInstanceStatus==`"available"`].[DBInstanceIdentifier,Endpoint.Address]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_RDS_PUBLIC_INSTANCES" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied Trying to describe DB instances" "$regx"
      continue
    fi
    if [[ $LIST_OF_RDS_PUBLIC_INSTANCES ]];then
      while read -r rds_instance;do
        RDS_NAME=$(echo $rds_instance | awk '{ print $1; }')
        RDS_DNSNAME=$(echo $rds_instance | awk '{ print $2; }')
        textFail "$regx: RDS instance: $RDS_NAME at $RDS_DNSNAME is set as Publicly Accessible!" "$regx" "$RDS_NAME"
      done <<< "$LIST_OF_RDS_PUBLIC_INSTANCES"
      else
        textPass "$regx: no Publicly Accessible RDS instances found" "$regx" "$RDS_NAME"
    fi
  done
}

providers/aws/services/rds/check_extra747 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra747="7.47"
CHECK_TITLE_extra747="[extra747] Check if RDS instances is integrated with CloudWatch Logs"
CHECK_SCORED_extra747="NOT_SCORED"
CHECK_CIS_LEVEL_extra747="EXTRA"
CHECK_SEVERITY_extra747="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra747="AwsRdsDbInstance"
CHECK_ALTERNATE_check747="extra747"
CHECK_SERVICENAME_extra747="rds"
CHECK_RISK_extra747='If logs are not enabled; monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra747='Use CloudWatch Logs to perform real-time analysis of the log data. Create alarms and view metrics.'
CHECK_DOC_extra747='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/publishing_cloudwatchlogs.html'
CHECK_CAF_EPIC_extra747='Logging and Monitoring'

extra747(){
  for regx in $REGIONS; do
    LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].DBInstanceIdentifier' --output text 2>&1)
    if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
        continue
    fi
    if [[ $LIST_OF_RDS_INSTANCES ]];then
      for rdsinstance in $LIST_OF_RDS_INSTANCES; do
        # if retention is 0 then is disabled
        ENABLED_CLOUDWATCHLOGS_EXPORTS=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].EnabledCloudwatchLogsExports' --output text)
        if [[ $ENABLED_CLOUDWATCHLOGS_EXPORTS ]]; then
          textPass "$regx: RDS instance $rdsinstance is shipping $ENABLED_CLOUDWATCHLOGS_EXPORTS to CloudWatch Logs" "$regx" "$rdsinstance"
        else
          textFail "$regx: RDS instance $rdsinstance has no CloudWatch Logs enabled!" "$regx" "$rdsinstance"
        fi
      done
    else
      textInfo "$regx: No RDS instances found" "$regx" "$rdsinstance"
    fi
  done
}

providers/aws/services/rds/check_extra739 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra739="7.39"
CHECK_TITLE_extra739="[extra739] Check if RDS instances have backup enabled"
CHECK_SCORED_extra739="NOT_SCORED"
CHECK_CIS_LEVEL_extra739="EXTRA"
CHECK_SEVERITY_extra739="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra739="AwsRdsDbInstance"
CHECK_ALTERNATE_check739="extra739"
CHECK_SERVICENAME_extra739="rds"
CHECK_RISK_extra739='If backup is not enabled; data is vulnerable. Human error or bad actors could erase or modify data.'
CHECK_REMEDIATION_extra739='Enable automated backup for production data. Define a retention period and periodically test backup restoration. A Disaster Recovery process should be in place to govern Data Protection approach.'
CHECK_DOC_extra739='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html'
CHECK_CAF_EPIC_extra739='Data Protection'

extra739(){
  for regx in $REGIONS; do
    LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].DBInstanceIdentifier' --output text 2>&1)
    if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
        continue
    fi
    if [[ $LIST_OF_RDS_INSTANCES ]];then
      for rdsinstance in $LIST_OF_RDS_INSTANCES; do
        # if retention is 0 then is disabled
        BACKUP_RETENTION=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].BackupRetentionPeriod' --output text)
        if [[ $BACKUP_RETENTION == "0" ]]; then
          textFail "$regx: RDS instance $rdsinstance has not backup enabled!" "$regx" "$rdsinstance"
        else
          textPass "$regx: RDS instance $rdsinstance has backup enabled with retention period $BACKUP_RETENTION days" "$regx" "$rdsinstance"
        fi
      done
    else
      textInfo "$regx: No RDS instances found" "$regx" "$rdsinstance"
    fi
  done
}

providers/aws/services/rds/check_extra735 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra735="7.35"
CHECK_TITLE_extra735="[extra735] Check if RDS instances storage is encrypted"
CHECK_SCORED_extra735="NOT_SCORED"
CHECK_CIS_LEVEL_extra735="EXTRA"
CHECK_SEVERITY_extra735="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra735="AwsRdsDbInstance"
CHECK_ALTERNATE_check735="extra735"
CHECK_ASFF_COMPLIANCE_TYPE_extra735="ens-mp.info.3.aws.rds.1"
CHECK_SERVICENAME_extra735="rds"
CHECK_RISK_extra735='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra735='Enable Encryption. Use a CMK where possible. It will provide additional management and privacy benefits.'
CHECK_DOC_extra735='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html'
CHECK_CAF_EPIC_extra735='Data Protection'

extra735(){
  for regx in $REGIONS; do
    LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].DBInstanceIdentifier' --output text 2>&1)
    if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
        continue
    fi
    if [[ $LIST_OF_RDS_INSTANCES ]];then
      for rdsinstance in $LIST_OF_RDS_INSTANCES; do
        IS_ENCRYPTED=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].StorageEncrypted' --output text)
        if [[ $IS_ENCRYPTED == "False" ]]; then
          textFail "$regx: RDS instance $rdsinstance is not encrypted!" "$regx" "$rdsinstance"
        else
          textPass "$regx: RDS instance $rdsinstance is encrypted" "$regx" "$rdsinstance"
        fi
      done
    else
      textInfo "$regx: No RDS instances found" "$regx" "$rdsinstance"
    fi
  done
}

providers/aws/services/rds/check_extra723 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra723="7.23"
CHECK_TITLE_extra723="[extra723] Check if RDS Snapshots and Cluster Snapshots are public"
CHECK_SCORED_extra723="NOT_SCORED"
CHECK_CIS_LEVEL_extra723="EXTRA"
CHECK_SEVERITY_extra723="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra723="AwsRdsDbSnapshot"
CHECK_ALTERNATE_check723="extra723"
CHECK_SERVICENAME_extra723="rds"
CHECK_RISK_extra723='Publicly accessible services could expose sensitive data to bad actors. t is recommended that your RDS snapshots should not be public in order to prevent potential leak or misuse of sensitive data or any other kind of security threat. If your RDS snapshot is public; then the data which is backed up in that snapshot is accessible to all other AWS accounts.'
CHECK_REMEDIATION_extra723='Use AWS Config to identify any sanpshot that is public.'
CHECK_DOC_extra723='https://docs.aws.amazon.com/config/latest/developerguide/rds-snapshots-public-prohibited.html'
CHECK_CAF_EPIC_extra723='Data Protection'

extra723(){
  # "Check if RDS Snapshots are public "
  for regx in $REGIONS; do
    # RDS snapshots
    LIST_OF_RDS_SNAPSHOTS=$($AWSCLI rds describe-db-snapshots $PROFILE_OPT --region $regx --query DBSnapshots[*].DBSnapshotIdentifier --output text 2>&1)
    if [[ $(echo "$LIST_OF_RDS_SNAPSHOTS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe db snapshots" "$regx"
        continue
    fi
    if [[ $LIST_OF_RDS_SNAPSHOTS ]]; then
      for rdssnapshot in $LIST_OF_RDS_SNAPSHOTS;do
        SNAPSHOT_IS_PUBLIC=$($AWSCLI rds describe-db-snapshot-attributes $PROFILE_OPT --region $regx --db-snapshot-identifier $rdssnapshot --query DBSnapshotAttributesResult.DBSnapshotAttributes[*] --output text|grep ^ATTRIBUTEVALUES|cut -f2|grep all)
        if [[ $SNAPSHOT_IS_PUBLIC ]];then
          textFail "$regx: RDS Snapshot $rdssnapshot is public!" "$regx" "$rdssnapshot"
        else
          textPass "$regx: RDS Snapshot $rdssnapshot is not shared" "$regx" "$rdssnapshot"
        fi
      done
    else
      textInfo "$regx: No RDS Snapshots found" "$regx" "$rdssnapshot"
    fi
    # RDS cluster snapshots
    LIST_OF_RDS_CLUSTER_SNAPSHOTS=$($AWSCLI rds describe-db-cluster-snapshots $PROFILE_OPT --region $regx --query DBClusterSnapshots[*].DBClusterSnapshotIdentifier --output text)
    if [[ $LIST_OF_RDS_CLUSTER_SNAPSHOTS ]]; then
      for rdsclustersnapshot in $LIST_OF_RDS_CLUSTER_SNAPSHOTS;do
        CLUSTER_SNAPSHOT_IS_PUBLIC=$($AWSCLI rds describe-db-cluster-snapshot-attributes $PROFILE_OPT --region $regx --db-cluster-snapshot-identifier $rdsclustersnapshot --query DBClusterSnapshotAttributesResult.DBClusterSnapshotAttributes[*] --output text|grep ^ATTRIBUTEVALUES|cut -f2|grep all)
        if [[ $CLUSTER_SNAPSHOT_IS_PUBLIC ]];then
          textFail "$regx: RDS Cluster Snapshot $rdsclustersnapshot is public!" "$regx" "$rdsclustersnapshot"
        else
          textPass "$regx: RDS Cluster Snapshot $rdsclustersnapshot is not shared" "$regx" "$rdsclustersnapshot"
        fi
      done
    else
      textInfo "$regx: No RDS Cluster Snapshots found" "$regx" "$rdsclustersnapshot"
    fi
  done
}

providers/aws/services/rds/check_extra7133 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7133="7.133"
CHECK_TITLE_extra7133="[extra7133] Check if RDS instances have multi-AZ enabled"
CHECK_SCORED_extra7133="NOT_SCORED"
CHECK_CIS_LEVEL_extra7133="EXTRA"
CHECK_SEVERITY_extra7133="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7133="AwsRdsDbInstance"
CHECK_ALTERNATE_check7133="extra7133"
CHECK_SERVICENAME_extra7133="rds"
CHECK_RISK_extra7133='In case of failure; with a single-AZ deployment configuration; should an availability zone specific database failure occur; Amazon RDS can not automatically fail over to the standby availability zone.'
CHECK_REMEDIATION_extra7133='Enable multi-AZ deployment for production databases.'
CHECK_DOC_extra7133='https://aws.amazon.com/rds/features/multi-az/'
CHECK_CAF_EPIC_extra7133='Data Protection'

extra7133(){
  for regx in $REGIONS; do
    RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query "DBInstances[?Engine != 'docdb'].DBInstanceIdentifier" --output text 2>&1)
    if [[ $(echo "$RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
        continue
    fi
    if [[ $RDS_INSTANCES ]];then
      for rdsinstance in ${RDS_INSTANCES}; do
          RDS_NAME="$rdsinstance"
          MULTIAZ_FLAG=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].MultiAZ' --output text)
          if [[ $MULTIAZ_FLAG == "True" ]];then
              textPass "$regx: RDS instance: $RDS_NAME has multi-AZ enabled" "$regx" "$RDS_NAME"
          else
              textFail "$regx: RDS instance: $RDS_NAME has multi-AZ disabled!" "$regx" "$RDS_NAME"
          fi
      done
    else
      textInfo "$regx: no RDS instances found" "$regx" "$RDS_NAME"
    fi
  done
}

providers/aws/services/rds/check_extra7132 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7132="7.132"
CHECK_TITLE_extra7132="[extra7132] Check if RDS instances has enhanced monitoring enabled"
CHECK_SCORED_extra7132="NOT_SCORED"
CHECK_CIS_LEVEL_extra7132="EXTRA"
CHECK_SEVERITY_extra7132="Low"
CHECK_ASFF_RESOURCE_TYPE_extra7132="AwsRdsDbInstance"
CHECK_ALTERNATE_check7132="extra7132"
CHECK_SERVICENAME_extra7132="rds"
CHECK_RISK_extra7132='A smaller monitoring interval results in more frequent reporting of OS metrics.'
CHECK_REMEDIATION_extra7132='To use Enhanced Monitoring; you must create an IAM role; and then enable Enhanced Monitoring.'
CHECK_DOC_extra7132='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Monitoring.OS.html'
CHECK_CAF_EPIC_extra7132='Logging and Monitoring'

extra7132(){
  for regx in $REGIONS; do
    RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query "DBInstances[?Engine != 'docdb'].DBInstanceIdentifier" --output text 2>&1)
    if [[ $(echo "$RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
        continue
    fi
    if [[ $RDS_INSTANCES ]];then
      for rdsinstance in ${RDS_INSTANCES}; do
          RDS_NAME="$rdsinstance"
          MONITORING_FLAG=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].[EnhancedMonitoringResourceArn]' --output text)
          if [[ $MONITORING_FLAG == "None" ]];then
              textFail "$regx: RDS instance: $RDS_NAME has enhanced monitoring disabled!" "$rex" "$RDS_NAME"
          else
              textPass "$regx: RDS instance: $RDS_NAME has enhanced monitoring enabled." "$regx" "$RDS_NAME"
          fi
      done
    else
      textInfo "$regx: no RDS instances found" "$regx" "$RDS_NAME"
    fi
  done
}

providers/aws/services/rds/check_extra7131 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7131="7.131"
CHECK_TITLE_extra7131="[extra7131] Ensure RDS instances have minor version upgrade enabled"
CHECK_SCORED_extra7131="NOT_SCORED"
CHECK_CIS_LEVEL_extra7131="EXTRA"
CHECK_SEVERITY_extra7131="Low"
CHECK_ASFF_RESOURCE_TYPE_extra7131="AwsRdsDbInstance"
CHECK_ALTERNATE_check7131="extra7131"
CHECK_SERVICENAME_extra7131="rds"
CHECK_RISK_extra7131='Auto Minor Version Upgrade is a feature that you can enable to have your database automatically upgraded when a new minor database engine version is available. Minor version upgrades often patch security vulnerabilities and fix bugs; and therefor should be applied.'
CHECK_REMEDIATION_extra7131='Enable auto minor version upgrade for all databases and environments.'
CHECK_DOC_extra7131='https://aws.amazon.com/blogs/database/best-practices-for-upgrading-amazon-rds-to-major-and-minor-versions-of-postgresql/'
CHECK_CAF_EPIC_extra7131='Infrastructure Security'

extra7131(){
  for regx in $REGIONS; do
    # LIST_OF_RDS_PUBLIC_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[?PubliclyAccessible==`true` && DBInstanceStatus==`"available"`].[DBInstanceIdentifier,Endpoint.Address]' --output text)
    LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].[DBInstanceIdentifier,AutoMinorVersionUpgrade]'  --output text 2>&1)
    if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
        continue
    fi
    if [[ $LIST_OF_RDS_INSTANCES ]];then
      while read -r rds_instance;do
        RDS_NAME=$(echo $rds_instance | awk '{ print $1; }')
        RDS_AUTOMINORUPGRADE_FLAG=$(echo $rds_instance | awk '{ print $2; }')
        if [[ $RDS_AUTOMINORUPGRADE_FLAG == "True" ]];then
            textPass "$regx: RDS instance: $RDS_NAME is has minor version upgrade enabled" "$regx" "$RDS_NAME"
        else
            textFail "$regx: RDS instance: $RDS_NAME does not have minor version upgrade enabled" "$regx" "$RDS_NAME"
        fi
      done <<< "$LIST_OF_RDS_INSTANCES"
      else
        textInfo "$regx: no RDS instances found" "$regx" "$RDS_NAME"
    fi
  done
}

providers/aws/services/rds/check_extra7113 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation:
#
# https://www.cloudconformity.com/knowledge-base/aws/RDS/instance-deletion-protection.html
# https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
#
# aws rds modify-db-instance \
#	--region us-east-1 \
#	--db-instance-identifier test-db \
#	--deletion-protection \
#	[--apply-immediately | --no-apply-immediately]

CHECK_ID_extra7113="7.113"
CHECK_TITLE_extra7113="[extra7113] Check if RDS instances have deletion protection enabled "
CHECK_SCORED_extra7113="NOT_SCORED"
CHECK_CIS_LEVEL_extra7113="EXTRA"
CHECK_SEVERITY_extra7113="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7113="AwsRdsDbInstance"
CHECK_ALTERNATE_check7113="extra7113"
CHECK_SERVICENAME_extra7113="rds"
CHECK_RISK_extra7113='You can only delete instances that do not have deletion protection enabled.'
CHECK_REMEDIATION_extra7113='Enable deletion protection using the AWS Management Console for production DB instances.'
CHECK_DOC_extra7113='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_DeleteInstance.html'
CHECK_CAF_EPIC_extra7113='Data Protection'

extra7113(){
  for regx in $REGIONS; do
    LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query "DBInstances[?Engine != 'docdb'].DBInstanceIdentifier" --output text 2>&1)
    if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
        continue
    fi
    if [[ $LIST_OF_RDS_INSTANCES ]];then
      for rdsinstance in $LIST_OF_RDS_INSTANCES; do
        IS_DELETIONPROTECTION=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].DeletionProtection' --output text)
        if [[ $IS_DELETIONPROTECTION == "False" ]]; then
          textFail "$regx: RDS instance $rdsinstance deletion protection is not enabled!" "$regx" "$rdsinstance"
        else
          textPass "$regx: RDS instance $rdsinstance deletion protection is enabled" "$regx" "$rdsinstance"
        fi
      done
    else
      textInfo "$regx: No RDS instances found" "$regx"
    fi
  done
}

providers/aws/services/macie/check_extra712 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra712="7.12"
CHECK_TITLE_extra712="[extra712] Check if Amazon Macie is enabled"
CHECK_SCORED_extra712="NOT_SCORED"
CHECK_CIS_LEVEL_extra712="EXTRA"
CHECK_SEVERITY_extra712="Low"
CHECK_ALTERNATE_check712="extra712"
CHECK_ASFF_RESOURCE_TYPE_extra712="AwsMacieSession"
CHECK_SERVICENAME_extra712="macie"
CHECK_RISK_extra712='Amazon Macie is a fully managed data security and data privacy service that uses machine learning and pattern matching to help you discover; monitor; and protect your sensitive data in AWS.'
CHECK_REMEDIATION_extra712='Enable Amazon Macie and create appropriate jobs to discover sensitive data.'
CHECK_DOC_extra712='https://docs.aws.amazon.com/macie/latest/user/getting-started.html'
CHECK_CAF_EPIC_extra712='Data Protection'

 extra712(){
#     "No API commands available to check if Macie is enabled,"
#     "just looking if IAM Macie related permissions exist.  "
   MACIE_IAM_ROLES_CREATED=$($AWSCLI iam list-roles $PROFILE_OPT --query 'Roles[*].Arn'|grep AWSMacieServiceCustomer|wc -l)
   if [[ $MACIE_IAM_ROLES_CREATED -eq 2 ]];then
     textPass "$REGION: Macie related IAM roles exist so it might be enabled. Check it out manually" "$REGION"
else
     textFail "$REGION: No Macie related IAM roles found. It is most likely not to be enabled" "$REGION"
fi
}

providers/aws/services/lambda/check_extra798 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra798="7.98"
CHECK_TITLE_extra798="[extra798] Check if Lambda functions have resource-based policy set as Public"
CHECK_SCORED_extra798="NOT_SCORED"
CHECK_CIS_LEVEL_extra798="EXTRA"
CHECK_SEVERITY_extra798="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra798="AwsLambdaFunction"
CHECK_ALTERNATE_check798="extra798"
CHECK_SERVICENAME_extra798="lambda"
CHECK_RISK_extra798='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra798='Grant usage permission on a per-resource basis and applying least privilege principle.'
CHECK_DOC_extra798='https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html'
CHECK_CAF_EPIC_extra798='Infrastructure Security'

extra798(){
  for regx in $REGIONS; do
    LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --output text --query 'Functions[*].FunctionName' 2>&1)
    if [[ $(echo "$LIST_OF_FUNCTIONS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list functions" "$regx"
        continue
    fi
    if [[ $LIST_OF_FUNCTIONS ]]; then
      for lambdafunction in $LIST_OF_FUNCTIONS; do
        # get the policy per function
        FUNCTION_POLICY=$($AWSCLI lambda get-policy  $PROFILE_OPT --region $regx --function-name $lambdafunction --query Policy --output text 2>/dev/null)
        if [[ $FUNCTION_POLICY ]]; then
          FUNCTION_POLICY_ALLOW_ALL=$(echo $FUNCTION_POLICY \
            | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
          if [[ $FUNCTION_POLICY_ALLOW_ALL ]]; then
            textFail "$regx: Lambda function $lambdafunction has a policy with public access" "$regx" "$lambdafunction"
          else
            textPass "$regx: Lambda function $lambdafunction has a policy resource-based policy and is not public" "$regx" "$lambdafunction"
          fi
        else
          textPass "$regx: Lambda function $lambdafunction does not have resource-based policy" "$regx" "$lambdafunction"
        fi
      done
    else
      textInfo "$regx: No Lambda functions found" "$regx"
    fi
  done
}

providers/aws/services/lambda/check_extra762 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra762="7.62"
CHECK_TITLE_extra762="[extra762] Find obsolete Lambda runtimes "
CHECK_SCORED_extra762="NOT_SCORED"
CHECK_CIS_LEVEL_extra762="EXTRA"
CHECK_SEVERITY_extra762="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra762="AwsLambdaFunction"
CHECK_ALTERNATE_check762="extra762"
CHECK_SERVICENAME_extra762="lambda"
CHECK_RISK_extra762='If you have functions running on a runtime that will be deprecated in the next 60 days; Lambda notifies you by email that you should prepare by migrating your function to a supported runtime. In some cases; such as security issues that require a backwards-incompatible update; or software that does not support a long-term support (LTS) schedule; advance notice might not be possible. After a runtime is deprecated; Lambda might retire it completely at any time by disabling invocation. Deprecated runtimes are not eligible for security updates or technical support.'
CHECK_REMEDIATION_extra762='Test new runtimes as they are made available. Implement them in production as soon as possible.'
CHECK_DOC_extra762='https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html'
CHECK_CAF_EPIC_extra762='Infrastructure Security'

extra762(){

  # regex to match OBSOLETE runtimes in string functionName%runtime
  # https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html
  OBSOLETE='%(nodejs4.3|nodejs4.3-edge|nodejs6.10|nodejs8.10|dotnetcore1.0|dotnetcore2.0)'

  for regx in $REGIONS; do
    LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --output text --query 'Functions[*].{R:Runtime,N:FunctionName}' 2>&1| tr "\t" "%" )
    if [[ $(echo "$LIST_OF_FUNCTIONS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list functions" "$regx"
        continue
    fi
    if [[ $LIST_OF_FUNCTIONS ]]; then
      for lambdafunction in $LIST_OF_FUNCTIONS;do
        fname=$(echo "$lambdafunction" | cut -d'%' -f1)
        runtime=$(echo "$lambdafunction" | cut -d'%' -f2)
        if echo "$lambdafunction" | grep -Eq $OBSOLETE  ; then
          textFail "$regx: Obsolete runtime: ${runtime} used by: ${fname}" "$regx" "${fname}"
        else
          textPass "$regx: Supported runtime: ${runtime} used by: ${fname}" "$regx" "${fname}"
        fi
      done
    else
      textInfo "$regx: No Lambda functions found" "$regx"
    fi
  done
}

providers/aws/services/lambda/check_extra760 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra760="7.60"
CHECK_TITLE_extra760="[extra760] Find secrets in Lambda functions code "
CHECK_SCORED_extra760="NOT_SCORED"
CHECK_CIS_LEVEL_extra760="EXTRA"
CHECK_SEVERITY_extra760="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra760="AwsLambdaFunction"
CHECK_ALTERNATE_check760="extra760"
CHECK_SERVICENAME_extra760="lambda"
CHECK_RISK_extra760='The use of a hard-coded password increases the possibility of password guessing.  If hard-coded passwords are used; it is possible that malicious users gain access through the account in question.'
CHECK_REMEDIATION_extra760='Use Secrets Manager to securely provide database credentials to Lambda functions and secure the databases as well as use the credentials to connect and query them without hardcoding the secrets in code or passing them through environmental variables. '
CHECK_DOC_extra760='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html'
CHECK_CAF_EPIC_extra760='IAM'

extra760(){
  SECRETS_TEMP_FOLDER="${PROWLER_DIR}/secrets-${ACCOUNT_NUM}-${PROWLER_START_TIME}"
  if [[ ! -d "${SECRETS_TEMP_FOLDER}" ]]; then
    # this folder is deleted once this check is finished
    mkdir "${SECRETS_TEMP_FOLDER}"
  fi

  for regx in ${REGIONS}; do
    CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
    if [[ $? -eq 241 ]]; then
      textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
    else
      LIST_OF_FUNCTIONS=$("${AWSCLI}" lambda list-functions ${PROFILE_OPT} --region "${regx}" --query 'Functions[*].FunctionName' --output text 2>&1)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_FUNCTIONS}"; then
        textInfo "${regx}: Access Denied trying to list Lambda functions" "${regx}"
        continue
      fi
      if [[ -n "${LIST_OF_FUNCTIONS}" && $(tr '[:upper:]' '[:lower:]' <<< "${LIST_OF_FUNCTIONS}") != "none" ]]; then
        for lambdafunction in ${LIST_OF_FUNCTIONS}; do
          LAMBDA_FUNCTION_FOLDER="${SECRETS_TEMP_FOLDER}/extra760-${lambdafunction}-${regx}"
          LAMBDA_FUNCTION_FILE="${lambdafunction}-code.zip"
          LAMBDA_CODE_LOCATION=$("${AWSCLI}" lambda get-function ${PROFILE_OPT} --region "${regx}" --function-name "${lambdafunction}" --query 'Code.Location' --output text 2>&1)
          if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LAMBDA_CODE_LOCATION}"; then
            textInfo "${regx}: Access Denied trying to get Lambda functions" "${regx}" "${lambdafunction}"
            continue
          fi

          mkdir "${LAMBDA_FUNCTION_FOLDER}"

          # DOWNLOAD the code in a zip file
          CURL_ERROR=$(curl -s --show-error "${LAMBDA_CODE_LOCATION}" -o "${LAMBDA_FUNCTION_FOLDER}/${LAMBDA_FUNCTION_FILE}" 2>&1)
          if [[ -n "${CURL_ERROR}" ]]; then
            textInfo "${regx}: Error trying to get Lambda function code for ${lambdafunction} - ${CURL_ERROR}" "${regx}" "${lambdafunction}"
            # delete files to not leave trace, user must look at the function
            if [[ -d "${LAMBDA_FUNCTION_FOLDER}" ]]; then
              rm -fr "${LAMBDA_FUNCTION_FOLDER}"
            fi
            continue
          fi
          if ! grep -q 'Zip archive data' <(file "${LAMBDA_FUNCTION_FOLDER}/${LAMBDA_FUNCTION_FILE}"); then
            textInfo "${regx}: Error trying to get Lambda function code for ${lambdafunction}. File is not a Zip" "${regx}" "${lambdafunction}"
            # delete files to not leave trace, user must look at the function
            if [[ -d "${LAMBDA_FUNCTION_FOLDER}" ]]; then
              rm -fr "${LAMBDA_FUNCTION_FOLDER}"
            fi
            continue
          fi

          unzip -qq "${LAMBDA_FUNCTION_FOLDER}/${LAMBDA_FUNCTION_FILE}" -d "${LAMBDA_FUNCTION_FOLDER}" && {
            FINDINGS=$(secretsDetector folder "${LAMBDA_FUNCTION_FOLDER}")
            if [[ ${FINDINGS} -eq 0 ]]; then
              textPass "${regx}: No secrets found in Lambda function ${lambdafunction} code" "${regx}" "${lambdafunction}"
            else
              textFail "${regx}: Potential secret found in Lambda function ${lambdafunction} code" "${regx}" "${lambdafunction}"
            fi
          }

          # delete files to not leave trace, user must look at the function
          if [[ -d "${LAMBDA_FUNCTION_FOLDER}" ]]; then
            rm -fr "${LAMBDA_FUNCTION_FOLDER}"
          fi
        done
      else
        textInfo "${regx}: No Lambda functions found" "${regx}"
      fi
    fi
  done

  if [[ -d "${SECRETS_TEMP_FOLDER}" ]]; then
    rm -fr "${SECRETS_TEMP_FOLDER}"
  fi
}

providers/aws/services/lambda/check_extra759 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra759="7.59"
CHECK_TITLE_extra759="[extra759] Find secrets in Lambda functions variables "
CHECK_SCORED_extra759="NOT_SCORED"
CHECK_CIS_LEVEL_extra759="EXTRA"
CHECK_SEVERITY_extra759="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra759="AwsLambdaFunction"
CHECK_ALTERNATE_check759="extra759"
CHECK_SERVICENAME_extra759="lambda"
CHECK_RISK_extra759='The use of a hard-coded password increases the possibility of password guessing.  If hard-coded passwords are used; it is possible that malicious users gain access through the account in question.'
CHECK_REMEDIATION_extra759='Use Secrets Manager to securely provide database credentials to Lambda functions and secure the databases as well as use the credentials to connect and query them without hardcoding the secrets in code or passing them through environmental variables. '
CHECK_DOC_extra759='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html'
CHECK_CAF_EPIC_extra759='IAM'

extra759(){
  SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM-$PROWLER_START_TIME"
  if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then
    # this folder is deleted once this check is finished
    mkdir $SECRETS_TEMP_FOLDER
  fi

  for regx in $REGIONS; do
    CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
    if [[ $? -eq 241 ]]; then
      textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
    else
      LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query Functions[*].FunctionName --output text 2>&1)
      if [[ $(echo "$LIST_OF_FUNCTIONS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to list functions" "$regx"
          continue
      fi
      if [[ $LIST_OF_FUNCTIONS ]]; then
        for lambdafunction in $LIST_OF_FUNCTIONS;do
          LAMBDA_FUNCTION_VARIABLES_FILE="$SECRETS_TEMP_FOLDER/extra759-$lambdafunction-$regx-variables.txt"
          LAMBDA_FUNCTION_VARIABLES=$($AWSCLI lambda $PROFILE_OPT --region $regx get-function-configuration --function-name $lambdafunction --query 'Environment.Variables' --output json > $LAMBDA_FUNCTION_VARIABLES_FILE)
          if [ -s $LAMBDA_FUNCTION_VARIABLES_FILE ];then
          # Implementation using https://github.com/Yelp/detect-secrets
          FINDINGS=$(secretsDetector file $LAMBDA_FUNCTION_VARIABLES_FILE)
            if [[ $FINDINGS -eq 0 ]]; then
              textPass "$regx: No secrets found in Lambda function $lambdafunction variables" "$regx" "$lambdafunction"
              # delete file if nothing interesting is there
              rm -f $LAMBDA_FUNCTION_VARIABLES_FILE
            else
              textFail "$regx: Potential secret found in Lambda function $lambdafunction variables" "$regx" "$lambdafunction"
              # delete file to not leave trace, user must look at the function
              rm -f $LAMBDA_FUNCTION_VARIABLES_FILE
            fi
          else
            textInfo "$regx: Lambda function $stalambdafunction has not variables" "$regx"
          fi
        done
      else
        textInfo "$regx: No Lambda functions found" "$regx"
      fi
    fi
  done
  rm -rf $SECRETS_TEMP_FOLDER
}

providers/aws/services/lambda/check_extra720 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra720="7.20"
CHECK_TITLE_extra720="[extra720] Check if Lambda functions invoke API operations are being recorded by CloudTrail"
CHECK_SCORED_extra720="NOT_SCORED"
CHECK_CIS_LEVEL_extra720="EXTRA"
CHECK_SEVERITY_extra720="Low"
CHECK_ASFF_RESOURCE_TYPE_extra720="AwsLambdaFunction"
CHECK_ALTERNATE_check720="extra720"
CHECK_SERVICENAME_extra720="lambda"
CHECK_RISK_extra720='If logs are not enabled; monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra720='Make sure you are logging information about Lambda operations. Create a lifecycle and use cases for each trail.'
CHECK_DOC_extra720='https://docs.aws.amazon.com/lambda/latest/dg/logging-using-cloudtrail.html'
CHECK_CAF_EPIC_extra720='Logging and Monitoring'

extra720(){
  # "Check if Lambda functions invoke API operations are being recorded by CloudTrail "
  for regx in $REGIONS; do
    LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query 'Functions[*].FunctionName' --output text 2>&1)
    if [[ $(echo "$LIST_OF_FUNCTIONS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to list functions" "$regx"
      continue
    fi
    if [[ $LIST_OF_FUNCTIONS ]]; then
      LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].TrailARN' --output text 2>&1)
      if [[ $(echo "$LIST_OF_TRAILS" | grep AccessDenied) ]]; then
        textInfo "$regx: Access Denied trying to describe trails" "$regx"
        continue
      fi
      for lambdafunction in $LIST_OF_FUNCTIONS; do
        if [[ $LIST_OF_TRAILS ]]; then
          for trail in $LIST_OF_TRAILS; do
            FUNCTION_ENABLED_IN_TRAIL=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $regx --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:${AWS_PARTITION}:lambda.*function:$lambdafunction$|^arn:${AWS_PARTITION}:lambda$")
            if [[ $FUNCTION_ENABLED_IN_TRAIL ]]; then
              textPass "$regx: Lambda function $lambdafunction enabled in trail $trail" "$regx" "$trail"
            else
              textFail "$regx: Lambda function $lambdafunction NOT enabled in trail $trail" "$regx" "$trail"
            fi
          done
          # LIST_OF_MULTIREGION_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query "trailList[?IsMultiRegionTrail == \`true\`].Name" --output text)
          # if [[ $LIST_OF_MULTIREGION_TRAILS ]]; then
          #   for trail in $LIST_OF_MULTIREGION_TRAILS; do
          #     REGION_OF_TRAIL=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query "trailList[?IsMultiRegionTrail == \`true\` && Name == \`$trail\` ].HomeRegion" --output text)
          #     FUNCTION_ENABLED_IN_THIS_REGION=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $REGION_OF_TRAIL --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:aws:lambda.*function:$lambdafunction$")
          #     if [[ $FUNCTION_ENABLED_IN_THIS_REGION ]]; then
          #       textPass "$regx: Lambda function $lambdafunction enabled in trail $trail" "$regx"
          #     else
          #       textFail "$regx: Lambda function $lambdafunction NOT enabled in trail $trail" "$regx"
          #     fi
          #   done
          # else
          #   textFail "$regx: Lambda function $lambdafunction is not being recorded!" "$regx"
          # fi
        else
          textFail "$regx: Lambda function $lambdafunction is not being recorded no CloudTrail found!" "$regx" "$trail"
        fi
     done
    else
      textInfo "$regx: No Lambda functions found" "$regx"
    fi
  done
}

providers/aws/services/lambda/check_extra7180 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7180="7.180"
CHECK_TITLE_extra7180="[extra7180] Check Lambda Function URL CORS configuration"
CHECK_SCORED_extra7180="NOT_SCORED"
CHECK_CIS_LEVEL_extra7180="EXTRA"
CHECK_SEVERITY_extra7180="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7180="AwsLambdaFunction"
CHECK_ALTERNATE_check7180="extra7180"
CHECK_SERVICENAME_extra7180="lambda"
CHECK_RISK_extra7180='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra7180='Grant usage permission on a per-resource basis and applying least privilege principle.'
CHECK_DOC_extra7180='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html'
CHECK_CAF_EPIC_extra7180='Infrastructure Security'

extra7180(){
    for regx in ${REGIONS}; do
        LIST_OF_FUNCTIONS=$("${AWSCLI}" lambda list-functions ${PROFILE_OPT} \
            --region "${regx}" \
            --query 'Functions[*].FunctionName' \
            --output text 2>&1)
        # Check errors
        if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_FUNCTIONS}"; then
            textInfo "${regx}: Access Denied trying to list Lambda functions" "${regx}"
            continue
        fi
        if [[ -n "${LIST_OF_FUNCTIONS}" && $(tr '[:upper:]' '[:lower:]' <<< "${LIST_OF_FUNCTIONS}") != "none" ]]; then
            for lambda_function in ${LIST_OF_FUNCTIONS}; do
                # Check if Lambda function has an URL
                LAMBDA_FUNCTION_URL=$("${AWSCLI}" lambda list-function-url-configs ${PROFILE_OPT} \
                    --function-name "${lambda_function}" \
                    --region "${regx}" \
                    --query 'FunctionUrlConfigs[0].[FunctionUrl]' \
                    --output text)
                # Check errors
                if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then
                    textInfo "${regx}: Access Denied trying to get Lambda functions URLs" "${regx}"
                    continue
                fi

                if [[ "${LAMBDA_FUNCTION_URL}" != "None" ]]; then
                    # Check CORS configuration
                    CORS_ALLOW_ORIGINS=$("${AWSCLI}" lambda get-function-url-config ${PROFILE_OPT} \
                    --function-name "${lambda_function}" \
                    --region "${regx}" \
                    --query 'Cors.AllowOrigins' \
                    --output text)
                    # Check errors
                    if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then
                        textInfo "${regx}: Access Denied trying to get Lambda functions URLs configuration" "${regx}"
                        continue
                    fi

                    # The * is on purpose to check allowed origins
                    if [[ "${CORS_ALLOW_ORIGINS}" =~ "*" ]]; then
                        textFail "$regx: Lambda function ${lambda_function} URL has a wide CORS configuration" "${regx}" "${lambda_function}"
                    elif [[ "${CORS_ALLOW_ORIGINS}" == "None" ]]; then
                        textFail "${regx}: Lambda function ${lambda_function} URL has not CORS configured" "${regx}" "${lambda_function}"
                    else
                        textPass "${regx}: Lambda function ${lambda_function} has not a wide CORS configuration" "${regx}" "${lambda_function}"
                    fi

                else
                    textInfo "${regx}: Lambda function ${lambda_function} has not a function URL" "${regx}" "${lambda_function}"
                fi
            done
        else
            textInfo "${regx}: No Lambda functions found" "${regx}"
        fi
    done
}

providers/aws/services/lambda/check_extra7179 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7179="7.179"
CHECK_TITLE_extra7179="[extra7179] Check Public Lambda Function URL"
CHECK_SCORED_extra7179="NOT_SCORED"
CHECK_CIS_LEVEL_extra7179="EXTRA"
CHECK_SEVERITY_extra7179="High"
CHECK_ASFF_RESOURCE_TYPE_extra7179="AwsLambdaFunction"
CHECK_ALTERNATE_check7179="extra7179"
CHECK_SERVICENAME_extra7179="lambda"
CHECK_RISK_extra7179='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra7179='Grant usage permission on a per-resource basis and applying least privilege principle.'
CHECK_DOC_extra7179='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html'
CHECK_CAF_EPIC_extra7179='Infrastructure Security'

extra7179(){
    # Check if Lambda function URL is public
    # None --> Public
    local PUBLIC_AUTH_TYPE="NONE"
    # AWS_IAM --> Private
    local PRIVATE_AUTH_TYPE="AWS_IAM"

    for regx in ${REGIONS}; do
        LIST_OF_FUNCTIONS=$("${AWSCLI}" lambda list-functions ${PROFILE_OPT} \
            --region "${regx}" \
            --query 'Functions[*].FunctionName' \
            --output text 2>&1)
        # Check errors
        if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_FUNCTIONS}"; then
            textInfo "${regx}: Access Denied trying to list Lambda functions" "${regx}"
            continue
        fi
        if [[ -n "${LIST_OF_FUNCTIONS}" && $(tr '[:upper:]' '[:lower:]' <<< "${LIST_OF_FUNCTIONS}") != "none" ]]; then
            for lambda_function in ${LIST_OF_FUNCTIONS}; do
                AUTH_TYPE=$("${AWSCLI}" lambda list-function-url-configs ${PROFILE_OPT} \
                    --function-name "${lambda_function}" \
                    --region "${regx}" \
                    --query 'FunctionUrlConfigs[0].AuthType' \
                    --output text)
                # Check errors
                if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then
                    textInfo "${regx}: Access Denied trying to get Lambda functions URLs configuration" "${regx}"
                    continue
                fi

                if [[ "${AUTH_TYPE}" == "${PUBLIC_AUTH_TYPE}" ]]; then
                    textFail "${regx}: Lambda function ${lambda_function} has a publicly accessible function URL" "${regx}" "${lambda_function}"
                elif [[ "${AUTH_TYPE}" == "${PRIVATE_AUTH_TYPE}" ]]; then
                    textPass "${regx}: Lambda function ${lambda_function} has not a publicly accessible function URL" "${regx}" "${lambda_function}"
                else
                    textInfo "${regx}: Lambda function ${lambda_function} has not a function URL" "${regx}" "${lambda_function}"
                fi
            done
        else
            textInfo "${regx}: No Lambda functions found" "${regx}"
        fi
    done
}

providers/aws/services/lambda/check_extra7145 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7145="7.145"
CHECK_TITLE_extra7145="[extra7145] Check if Lambda functions have policies which allow access to any AWS account"
CHECK_SCORED_extra7145="NOT_SCORED"
CHECK_CIS_LEVEL_extra7145="EXTRA"
CHECK_SEVERITY_extra7145="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra7145="AwsLambda"
CHECK_ALTERNATE_check7145="extra7145"
CHECK_SERVICENAME_extra7145="lambda"
CHECK_RISK_extra7145='Lambda function access to any AWS account may result security issues'
CHECK_REMEDIATION_extra7145='Ensure Lambda function policiy does not allow access to any account'
CHECK_DOC_extra7145='https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html'
CHECK_CAF_EPIC_extra7145='IAM'

extra7145(){
  # "Check if lambda functions have policies which allow access to every aws account (Not Scored) (Not part of CIS benchmark)"
  for regx in $REGIONS; do
  LIST_OF_LAMBDA_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query Functions[*].FunctionName --output text 2>&1)
  if [[ $(echo "$LIST_OF_LAMBDA_FUNCTIONS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
      textInfo "$regx: Access Denied trying to list functions" "$regx"
      continue
  fi
  if [[ $LIST_OF_LAMBDA_FUNCTIONS ]]; then
    for lambdaFunction in $LIST_OF_LAMBDA_FUNCTIONS;do
      FUNCTION_POLICY_STATEMENTS=$($AWSCLI lambda $PROFILE_OPT get-policy --region $regx --function-name $lambdaFunction --output json --query Policy 2>&1)
      if [[ $FUNCTION_POLICY_STATEMENTS == *ResourceNotFoundException* ]]; then
        textInfo "$regx: Lambda function $lambdaFunction doesn't have any policy" "$regx" "$lambdaFunction"
      else
        FUNCTION_POLICY_BAD_STATEMENTS=$(echo $FUNCTION_POLICY_STATEMENTS | jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow")	 | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
        if [[ $FUNCTION_POLICY_BAD_STATEMENTS != "" ]]; then
          textFail "$regx: Lambda function $lambdaFunction allows public access to any AWS account" "$regx" "$lambdaFunction"
        else
          textPass "$regx: Lambda function $lambdaFunction has policy which doesn't allow access to everyone having an AWS account" "$regx" "$lambdaFunction"
        fi
      fi
    done

  else
    textInfo "$regx: No lambda functions found" "$regx"
  fi
 done
}

providers/aws/services/kms/check_extra736 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra736="7.36"
CHECK_TITLE_extra736="[extra736] Check exposed KMS keys"
CHECK_SCORED_extra736="NOT_SCORED"
CHECK_CIS_LEVEL_extra736="EXTRA"
CHECK_SEVERITY_extra736="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra736="AwsKmsKey"
CHECK_ALTERNATE_check736="extra736"
CHECK_ASFF_COMPLIANCE_TYPE_extra736="ens-op.exp.11.aws.kms.2"
CHECK_SERVICENAME_extra736="kms"
CHECK_RISK_extra736='Exposed KMS Keys or wide policy permissions my leave data unprotected.'
CHECK_REMEDIATION_extra736='To determine the full extent of who or what currently has access to a customer master key (CMK) in AWS KMS; you must examine the CMK key policy; all grants that apply to the CMK; and potentially all AWS Identity and Access Management (IAM) policies. You might do this to determine the scope of potential usage of a CMK.'
CHECK_DOC_extra736='https://docs.aws.amazon.com/kms/latest/developerguide/determining-access.html'
CHECK_CAF_EPIC_extra736='Data Protection'

extra736(){
  for regx in $REGIONS; do
    local CUSTOMER_MANAGED_KMS_KEYS=()
    # First, we need to recover every KMS key
    LIST_OF_KMS_KEYS=$($AWSCLI kms list-keys $PROFILE_OPT --region "${regx}" --query "Keys[].[KeyArn]" --output text)
    if [[ $LIST_OF_KMS_KEYS ]]
    then
      # Second, we need to check for Customer Managed KMS keys, with or without a configured alias
      for keyID in ${LIST_OF_KMS_KEYS}
      do
        KMS_KEY_MANAGER=$($AWSCLI kms describe-key $PROFILE_OPT --region "${regx}" --key-id "${keyID}" --query "KeyMetadata.KeyManager" --output text)
        if [[ "${KMS_KEY_MANAGER}" == "CUSTOMER" ]]
        then
          CUSTOMER_MANAGED_KMS_KEYS+=( "${keyID}" )
        fi
      done
    else
      textInfo "${regx}: No KMS keys found" "${regx}"
      continue
    fi

    # Third, we need to check the policy included in every Customer Managed KMS key
    if [[ "${CUSTOMER_MANAGED_KMS_KEYS[*]}" ]]
    then
      for keyID in "${CUSTOMER_MANAGED_KMS_KEYS[@]}"
      do
        CHECK_POLICY=$($AWSCLI kms get-key-policy --key-id "${keyID}" --policy-name default $PROFILE_OPT --region "${regx}" --output text | jq '.Statement[]|select(.Effect=="Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and .Condition == null)')
        if [[ $CHECK_POLICY ]]; then
          textFail "${regx}: KMS key ${keyID} may be publicly accessible!" "${regx}" "${keyID}"
        else
          textPass "${regx}: KMS key ${keyID} is not exposed to Public" "${regx}" "${keyID}"
        fi
      done
    else
      textInfo "${regx}: No Customer Managed KMS keys found" "${regx}"
    fi
  done
}

providers/aws/services/kms/check_extra7126 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7126="7.126"
CHECK_TITLE_extra7126="[extra7126] Check if there are CMK KMS keys not used"
CHECK_SCORED_extra7126="NOT_SCORED"
CHECK_CIS_LEVEL_extra7126="EXTRA"
CHECK_SEVERITY_extra7126="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7126="AwsKmsKey"
CHECK_ALTERNATE_check7126="extra7126"
CHECK_ASFF_COMPLIANCE_TYPE_extra7126="op.exp.11.aws.kms.2"
CHECK_SERVICENAME_extra7126="kms"
CHECK_RISK_extra7126='Unused keys may increase service cost.'
CHECK_REMEDIATION_extra7126='Before deleting a customer master key (CMK); you might want to know how many cipher-texts were encrypted under that key.  '
CHECK_DOC_extra7126='https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys-determining-usage.html'
CHECK_CAF_EPIC_extra7126='Data Protection'

extra7126(){
  for regx in $REGIONS; do
    LIST_OF_CUSTOMER_KMS_KEYS=$($AWSCLI kms list-aliases $PROFILE_OPT --region $regx --query "Aliases[].[AliasName,TargetKeyId]" --output text |grep -v ^alias/aws/ |awk '{ print $2 }')
    if [[ $LIST_OF_CUSTOMER_KMS_KEYS ]];then
      for key in $LIST_OF_CUSTOMER_KMS_KEYS; do
        CHECK_STATUS=$($AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --output json | jq -r '.KeyMetadata.KeyState')
        if [[ $CHECK_STATUS == "PendingDeletion" ]]; then
          textInfo "$regx: KMS key $key is pending deletion" "$regx"
        elif [[ $CHECK_STATUS == "Disabled"  ]]; then
          textInfo "$regx: KMS key $key is disabled" "$regx" "$key"
        else
          textPass "$regx: KMS key $key is not disabled or pending deletion" "$regx" "$key"
        fi
      done
    else
      textInfo "$regx: No KMS keys found" "$regx"
    fi
  done
}

providers/aws/services/kms/check37 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name AWSCMKChanges \
#     --filter-pattern '{ ($.eventSource = kms.amazonaws.com) && (($.eventName = DisableKey) || ($.eventName = ScheduleKeyDeletion)) }' \
#     --metric-transformations metricName=CMKEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name AWSCMKChangesAlarm \
#     --alarm-description "Triggered by AWS CMK changes." \
#     --metric-name CMKEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check37="3.7"
CHECK_TITLE_check37="[check37] Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created KMS CMKs"
CHECK_SCORED_check37="SCORED"
CHECK_CIS_LEVEL_check37="LEVEL2"
CHECK_SEVERITY_check37="Medium"
CHECK_ASFF_TYPE_check37="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check37="AwsCloudTrailTrail"
CHECK_ALTERNATE_check307="check37"
CHECK_ASFF_COMPLIANCE_TYPE_check37="ens-op.exp.11.aws.kms.1"
CHECK_SERVICENAME_check37="kms"
CHECK_RISK_check37='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check37='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check37='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check37='Logging and Monitoring'

check37(){
  check3x '\$\.eventSource\s*=\s*kms.amazonaws.com.+\$\.eventName\s*=\s*DisableKey.+\$\.eventName\s*=\s*ScheduleKeyDeletion'
}

providers/aws/services/kms/check28 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check28="2.8"
CHECK_TITLE_check28="[check28] Ensure rotation for customer created KMS CMKs is enabled"
CHECK_SCORED_check28="SCORED"
CHECK_CIS_LEVEL_check28="LEVEL2"
CHECK_SEVERITY_check28="Medium"
CHECK_ASFF_TYPE_check28="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check28="AwsKmsKey"
CHECK_ALTERNATE_check208="check28"
CHECK_SERVICENAME_check28="kms"
CHECK_RISK_check28='Cryptographic best practices discourage extensive reuse of encryption keys. Consequently; Customer Master Keys (CMKs) should be rotated to prevent usage of compromised keys.'
CHECK_REMEDIATION_check28='For every KMS Customer Master Keys (CMKs); ensure that Rotate this key every year is enabled.'
CHECK_DOC_check28='https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html'
CHECK_CAF_EPIC_check28='Data Protection'

check28(){
  # "Ensure rotation for customer created CMKs is enabled (Scored)"
  for regx in $REGIONS; do
    CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys $PROFILE_OPT --region $regx --output text --query 'Keys[*].KeyId' --output text 2>&1)
    if [[ $(echo "$CHECK_KMS_KEYLIST" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to list keys" "$regx" "$key"
      continue
    fi
    if [[ $CHECK_KMS_KEYLIST ]]; then
      cmk_count=0
      for key in $CHECK_KMS_KEYLIST; do
        KMSDETAILS=$($AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --query 'KeyMetadata.{key:KeyId,man:KeyManager,origin:Origin,spec:CustomerMasterKeySpec,state:KeyState}' --output text 2>&1 | grep SYMMETRIC)
        if [[ $(echo "$KMSDETAILS" | grep AccessDenied) ]]; then
           textInfo "$regx: Access Denied describing key $key" "$regx" "$key"
           continue
        fi

        KEYID=$(echo $KMSDETAILS | awk '{print $1}')
        KEYMANAGER=$(echo $KMSDETAILS | awk '{print $2}')
        KEYORIGIN=$(echo $KMSDETAILS | awk '{print $3}')
        KEYSTATE=$(echo $KMSDETAILS | awk '{print $5}')

        if [[ "$KEYMANAGER" == "AWS" ]]; then
          continue
        fi
        if [[ "$KEYSTATE" != "Enabled" ]]; then
          continue
        fi
        cmk_count=$((cmk_count + 1))

        if [[ "$KEYORIGIN" == "EXTERNAL" ]]; then
          textPass "$regx: Key $key uses imported key material" "$regx" "$key"
        else
          CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key $PROFILE_OPT --region $regx --output text 2>&1)
          if [[ $(echo "$CHECK_KMS_KEY_ROTATION" | grep AccessDenied) ]]; then
            textInfo "$regx: Access Denied getting key rotation status for $key " "$regx" "$key"
            continue
          fi
          if [[ "$CHECK_KMS_KEY_ROTATION" == "True" ]];then
            textPass "$regx: Key $key automatic rotation of the key material is enabled" "$regx" "$key"
          else
            textFail "$regx: Key $key automatic rotation of the key material is disabled" "$regx" "$key"
          fi
        fi
      done
      if [[ $cmk_count == 0 ]]; then
        textInfo "$regx: This region has no customer managed keys" "$regx" "$key"
      fi
    else
      textInfo "$regx: This region has no KMS keys" "$regx" "$key"
    fi
  done
}

providers/aws/services/iam/check_extra774 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra774="7.74"
CHECK_TITLE_extra774="[extra774] Ensure credentials unused for 30 days or greater are disabled"
CHECK_SCORED_extra774="NOT_SCORED"
CHECK_CIS_LEVEL_extra774="EXTRA"
CHECK_SEVERITY_extra774="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra774="AwsIamUser"
CHECK_ALTERNATE_check774="extra774"
CHECK_SERVICENAME_extra774="iam"
CHECK_RISK_extra774='To increase the security of your AWS account; remove IAM user credentials (that is; passwords and access keys) that are not needed. For example; when users leave your organization or no longer need AWS access.'
CHECK_REMEDIATION_extra774='Find the credentials that they were using and ensure that they are no longer operational. Ideally; you delete credentials if they are no longer needed. You can always recreate them at a later date if the need arises. At the very least; you should change the password or deactivate the access keys so that the former users no longer have access.'
CHECK_DOC_extra774='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_finding-unused.html'
CHECK_CAF_EPIC_extra774='IAM'

extra774(){
  check_creds_used_in_last_days 30
}

providers/aws/services/iam/check_extra733 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra733="7.33"
CHECK_TITLE_extra733="[extra733] Check if there are SAML Providers then STS can be used"
CHECK_SCORED_extra733="NOT_SCORED"
CHECK_CIS_LEVEL_extra733="EXTRA"
CHECK_SEVERITY_extra733="Low"
CHECK_ALTERNATE_check733="extra733"
CHECK_ASFF_COMPLIANCE_TYPE_extra733="ens-op.acc.1.aws.iam.1"
CHECK_SERVICENAME_extra733="iam"
CHECK_RISK_extra733='Without SAML provider users with AWS CLI or AWS API access can use IAM static credentials. SAML helps users to assume role by default each time they authenticate.'
CHECK_REMEDIATION_extra733='Enable SAML provider and use temporary credentials. You can use temporary security credentials to make programmatic requests for AWS resources using the AWS CLI or AWS API (using the AWS SDKs ). The temporary credentials provide the same permissions that you have with use long-term security credentials such as IAM user credentials. In case of not having SAML provider capabilities prevent usage of long-lived credentials.'
CHECK_DOC_extra733='https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithSAML.html'
CHECK_CAF_EPIC_extra733='IAM'

extra733(){
  LIST_SAML_PROV=$($AWSCLI iam list-saml-providers $PROFILE_OPT --query 'SAMLProviderList[*].Arn' --output text |grep -v ^None)
  if [[ $LIST_SAML_PROV ]]; then
    for provider in $LIST_SAML_PROV; do
      PROVIDER_NAME=$(echo $provider| cut -d/ -f2)
      textInfo "$REGION: SAML Provider $PROVIDER_NAME has been found" "$REGION" "$PROVIDER_NAME"
    done
  else
    textFail "$REGION: No SAML Provider found. Add one and use STS" "$REGION" "$PROVIDER_NAME"
  fi
}

providers/aws/services/iam/check_extra7125 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7125="7.125"
CHECK_TITLE_extra7125="[extra7125] Check if IAM users have Hardware MFA enabled."
CHECK_SCORED_extra7125="NOT_SCORED"
CHECK_CIS_LEVEL_extra7125="EXTRA"
CHECK_SEVERITY_extra7125="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7125="AwsIamUser"
CHECK_ALTERNATE_check7125="extra7125"
CHECK_ASFF_COMPLIANCE_TYPE_extra7125="ens-op.acc.5.aws.iam.2"
CHECK_SERVICENAME_extra7125="iam"
CHECK_RISK_extra7125='Hardware MFA is preferred over virtual MFA.'
CHECK_REMEDIATION_extra7125='Enable hardware MFA device for an IAM user from the AWS Management Console; the command line; or the IAM API.'
CHECK_DOC_extra7125='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_physical.html'
CHECK_CAF_EPIC_extra7125='IAM'

extra7125(){
  LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text $PROFILE_OPT --region $REGION)
  if [[ $LIST_USERS ]]; then
    # textFail "Users with access key 1 older than 90 days:"
    for user in $LIST_USERS; do
      # Would be virtual if sms-mfa or mfa, hardware is u2f or different.
      MFA_TYPE=$($AWSCLI iam list-mfa-devices --user-name $user $PROFILE_OPT --region $REGION --query MFADevices[].SerialNumber --output text | awk -F':' '{ print $6 }'| awk -F'/' '{ print $1 }')
      if [[ $MFA_TYPE == "mfa" || $MFA_TYPE == "sms-mfa" ]]; then
        textInfo "User $user has virtual MFA enabled"
      elif [[ $MFA_TYPE == "" ]]; then
        textFail "User $user has not hardware MFA enabled" "$REGION" "$user"
      else
        textPass "User $user has hardware MFA enabled" "$REGION" "$user"
      fi
    done
  else
    textPass "No users found"
  fi
}

providers/aws/services/iam/check_extra7123 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7123="7.123"
CHECK_TITLE_extra7123="[extra7123] Check if IAM users have two active access keys"
CHECK_SCORED_extra7123="NOT_SCORED"
CHECK_CIS_LEVEL_extra7123="EXTRA"
CHECK_SEVERITY_extra7123="Medium"
CHECK_ASFF_TYPE_extra7123="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_extra7123="AwsIamUser"
CHECK_ALTERNATE_check7123="extra7123"
CHECK_ASFF_COMPLIANCE_TYPE_extra7123="ens-op.acc.1.aws.iam.2"
CHECK_SERVICENAME_extra7123="iam"
CHECK_RISK_extra7123='Access Keys could be lost or stolen. It creates a critical risk.'
CHECK_REMEDIATION_extra7123='Avoid using long lived access keys.'
CHECK_DOC_extra7123='https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAccessKeys.html'
CHECK_CAF_EPIC_extra7123='IAM'

extra7123(){
  LIST_OF_USERS_WITH_2ACCESS_KEYS=$(cat $TEMP_REPORT_FILE| awk -F, '{ print $1, $9, $14 }' |grep "\ true\ true" | awk '{ print $1 }')
  if [[ $LIST_OF_USERS_WITH_2ACCESS_KEYS ]]; then
    # textFail "Users with access key 1 older than 90 days:"
    for user in $LIST_OF_USERS_WITH_2ACCESS_KEYS; do
      textFail "User $user has 2 active access keys" "$REGION" "$user"
    done
  else
    textPass "No users with 2 active access keys"
  fi
}

providers/aws/services/iam/check_extra7100 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# This check was contributed by Nick Malcolm (github.com/nickmalcolm), building
# on the hard work of others.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7100="7.100"
CHECK_TITLE_extra7100="[extra7100] Ensure that no custom IAM policies exist which allow permissive role assumption (e.g. sts:AssumeRole on *)"
CHECK_SCORED_extra7100="NOT_SCORED"
CHECK_CIS_LEVEL_extra7100="EXTRA"
CHECK_SEVERITY_extra7100="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra7100="AwsIamPolicy"
CHECK_ALTERNATE_check7100="extra7100"
CHECK_ASFF_COMPLIANCE_TYPE_extra7100="ens-op.acc.2.aws.iam.1"
CHECK_SERVICENAME_extra7100="iam"
CHECK_RISK_extra7100='If not restricted unintended access could happen.'
CHECK_REMEDIATION_extra7100='Use the least privilege principle when granting permissions.'
CHECK_DOC_extra7100='https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html'
CHECK_CAF_EPIC_extra7100='IAM'

extra7100(){
  # "Ensure that no custom policies exist which permit assuming any role (e.g. sts:AssumeRole on *)"
  #
  # A permissive STS Role assumption policy is one where the Resource (ARN) is not explicitly defined
  # This is most often seen as sts:assumeRole on *, but can take other forms.
  #
  # Learn more: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_permissions-to-switch.html#roles-usingrole-createpolicy
  LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text $PROFILE_OPT --region $REGION --scope Local --query 'Policies[*].[Arn,DefaultVersionId]' | grep -v -e '^None$' | awk -F '\t' '{print $1","$2"\n"}')
  if [[ $LIST_CUSTOM_POLICIES ]]; then
    for policy in $LIST_CUSTOM_POLICIES; do
      POLICY_ARN=$(echo $policy | awk -F ',' '{print $1}')
      POLICY_VERSION=$(echo $policy | awk -F ',' '{print $2}')

      POLICY_STATEMENTS_WITH_ALLOW=$($AWSCLI iam get-policy-version \
        --output json \
        --policy-arn $POLICY_ARN \
        --version-id $POLICY_VERSION \
        --query "[PolicyVersion.Document.Statement] | [] | [?Effect == 'Allow']" \
        $PROFILE_OPT \
        --region $REGION
      )

      # Identify permissive policies by:
      #   1 & 2) Casting all the Resource and Action keys to Arrays (sometimes they're a single string)
      #   3) Iterate over the policy statements
      #   4) Narrow the scope to Actions which are sts:* or sts:assumeRole(WithSAML|WithWebIdentity)
      #   5) Narrow the scope to Resources (IAM Roles) which include a wildcard
      POLICY_WITH_PERMISSIVE_STS=$(echo $POLICY_STATEMENTS_WITH_ALLOW \
        | jq 'map( .Resource |= (if type=="array" then . else [.] end) )' \
        | jq 'map( .Action |= (if type=="array" then . else [.] end) )' \
        | jq '.[]' \
        | jq 'select(.Action[] | contains("sts:AssumeRole") or contains("sts:*"))' \
        | jq 'select(.Resource[] | contains("*"))')

      if [[ $POLICY_WITH_PERMISSIVE_STS ]]; then
        PERMISSIVE_POLICIES_LIST="$PERMISSIVE_POLICIES_LIST $POLICY_ARN"
      fi

    done
    if [[ $PERMISSIVE_POLICIES_LIST ]]; then
      textInfo "STS AssumeRole Policies should only include the complete ARNs for the Roles that the user needs"
      textInfo "Learn more: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_permissions-to-switch.html#roles-usingrole-createpolicy"
      for policy in $PERMISSIVE_POLICIES_LIST; do
        textFail "$REGION: Policy $policy allows permissive STS Role assumption" "$REGION" "$policy"
      done
    else
      textPass "$REGION: No custom policies found that allow permissive STS Role assumption" "$REGION"
    fi
  else
    textPass "$REGION: No custom policies found" "$REGION"
  fi
}

providers/aws/services/iam/check_extra71 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra71="7.1"
CHECK_TITLE_extra71="[extra71] Ensure users of groups with AdministratorAccess policy have MFA tokens enabled"
CHECK_SCORED_extra71="NOT_SCORED"
CHECK_CIS_LEVEL_extra71="EXTRA"
CHECK_SEVERITY_extra71="High"
CHECK_ASFF_RESOURCE_TYPE_extra71="AwsIamUser"
CHECK_ALTERNATE_extra701="extra71"
CHECK_ALTERNATE_check71="extra71"
CHECK_ALTERNATE_check701="extra71"
CHECK_ASFF_COMPLIANCE_TYPE_extra71="ens-op.exp.10.aws.trail.2"
CHECK_SERVICENAME_extra71="iam"
CHECK_RISK_extra71='Policy "may" allow Anonymous users to perform actions.'
CHECK_REMEDIATION_extra71='Ensure this repository and its contents should be publicly accessible.'
CHECK_DOC_extra71='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html'
CHECK_CAF_EPIC_extra71='Infrastructure Security'

extra71(){
  # "Ensure users of groups with AdministratorAccess policy have MFA tokens enabled "
  ADMIN_GROUPS=''
  AWS_GROUPS=$($AWSCLI $PROFILE_OPT iam list-groups --output text --region $REGION --query 'Groups[].GroupName')
  for grp in $AWS_GROUPS; do
    # aws --profile onlinetraining iam list-attached-group-policies --group-name Administrators --query 'AttachedPolicies[].PolicyArn' | grep 'arn:aws:iam::aws:policy/AdministratorAccess'
    # list-attached-group-policies
    CHECK_ADMIN_GROUP=$($AWSCLI $PROFILE_OPT --region $REGION iam list-attached-group-policies --group-name $grp --output json --query 'AttachedPolicies[].PolicyArn' | grep  "arn:${AWS_PARTITION}:iam::aws:policy/AdministratorAccess")
    if [[ $CHECK_ADMIN_GROUP ]]; then
      ADMIN_GROUPS="$ADMIN_GROUPS $grp"
      textInfo "$REGION: $grp group provides administrative access" "$REGION" "$grp"
      ADMIN_USERS=$($AWSCLI $PROFILE_OPT iam get-group --region $REGION --group-name $grp --output json --query 'Users[].UserName' | grep '"' | cut -d'"' -f2 )
      for auser in $ADMIN_USERS; do
        # users in group are Administrators
        # users
        # check for user MFA device in credential report
        USER_MFA_ENABLED=$( cat $TEMP_REPORT_FILE | grep "^$auser," | cut -d',' -f8)
        if [[ "true" == $USER_MFA_ENABLED ]]; then
          textPass "$REGION: $auser / MFA Enabled / admin via group $grp" "$REGION" "$grp"
        else
          textFail "$REGION: $auser / MFA DISABLED / admin via group $grp" "$REGION" "$grp"
        fi
      done
    else
      textInfo "$REGION: $grp group provides non-administrative access" "$REGION" "$grp"
    fi
  done
}

providers/aws/services/iam/check36 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/MyCloudTrailLG \
#     --filter-name AWSConsoleSignInFailures \
#     --filter-pattern '{ ($.eventName = ConsoleLogin) && ($.errorMessage = "Failed authentication") }' \
#     --metric-transformations metricName=ConsoleSigninFailureCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name "Console Sign-in Failures" \
#     --alarm-description "AWS Management Console Sign-in Failure Alarm." \
#     --metric-name ConsoleSigninFailureCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 3 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check36="3.6"
CHECK_TITLE_check36="[check36] Ensure a log metric filter and alarm exist for AWS Management Console authentication failures"
CHECK_SCORED_check36="SCORED"
CHECK_CIS_LEVEL_check36="LEVEL2"
CHECK_SEVERITY_check36="Medium"
CHECK_ASFF_TYPE_check36="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check36="AwsCloudTrailTrail"
CHECK_ALTERNATE_check306="check36"
CHECK_ASFF_COMPLIANCE_TYPE_check36="ens-op.exp.8.aws.trail.3"
CHECK_SERVICENAME_check36="iam"
CHECK_RISK_check36='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check36='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check36='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check36='Logging and Monitoring'

check36(){
  check3x '\$\.eventName\s*=\s*ConsoleLogin.+\$\.errorMessage\s*=\s*"Failed authentication"'
}

providers/aws/services/iam/check34 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name IAMAuthConfigChanges \
#     --filter-pattern '{ ($.eventName = DeleteGroupPolicy) || ($.eventName = DeleteRolePolicy) || ($.eventName = DeleteUserPolicy) || ($.eventName = PutGroupPolicy) || ($.eventName = PutRolePolicy) || ($.eventName = PutUserPolicy) || ($.eventName = CreatePolicy) || ($.eventName = DeletePolicy) || ($.eventName = CreatePolicyVersion) || ($.eventName = DeletePolicyVersion) || ($.eventName = AttachRolePolicy) || ($.eventName = DetachRolePolicy) || ($.eventName = AttachUserPolicy) || ($.eventName = DetachUserPolicy) || ($.eventName = AttachGroupPolicy) || ($.eventName = DetachGroupPolicy) }' \
#     --metric-transformations metricName=IAMPolicyEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name IAMAuthorizationActivityAlarm \
#     --alarm-description "Triggered by AWS IAM authorization config changes." \
#     --metric-name IAMPolicyEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check34="3.4"
CHECK_TITLE_check34="[check34] Ensure a log metric filter and alarm exist for IAM policy changes"
CHECK_SCORED_check34="SCORED"
CHECK_CIS_LEVEL_check34="LEVEL1"
CHECK_SEVERITY_check34="Medium"
CHECK_ASFF_TYPE_check34="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check34="AwsCloudTrailTrail"
CHECK_ALTERNATE_check304="check34"
CHECK_ASFF_COMPLIANCE_TYPE_check34="ens-op.exp.8.aws.trail.6"
CHECK_SERVICENAME_check34="iam"
CHECK_RISK_check34='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check34='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check34='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check34='IAM'

check34(){
  check3x '\$\.eventName\s*=\s*DeleteGroupPolicy.+\$\.eventName\s*=\s*DeleteRolePolicy.+\$\.eventName\s*=\s*DeleteUserPolicy.+\$\.eventName\s*=\s*PutGroupPolicy.+\$\.eventName\s*=\s*PutRolePolicy.+\$\.eventName\s*=\s*PutUserPolicy.+\$\.eventName\s*=\s*CreatePolicy.+\$\.eventName\s*=\s*DeletePolicy.+\$\.eventName\s*=\s*CreatePolicyVersion.+\$\.eventName\s*=\s*DeletePolicyVersion.+\$\.eventName\s*=\s*AttachRolePolicy.+\$\.eventName\s*=\s*DetachRolePolicy.+\$\.eventName\s*=\s*AttachUserPolicy.+\$\.eventName\s*=\s*DetachUserPolicy.+\$\.eventName\s*=\s*AttachGroupPolicy.+\$\.eventName\s*=\s*DetachGroupPolicy'
}

providers/aws/services/iam/check33 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name RootAccountUsage \
#     --filter-pattern '{ $.userIdentity.type = "Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType != "AwsServiceEvent" }' \
#     --metric-transformations metricName=RootAccountUsageEventCount,metricNamespace=CloudTrailMetrics,metricValue=1 \
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name RootAccountUsageAlarm \
#     --alarm-description "Triggered by AWS Root Account usage." \
#     --metric-name RootAccountUsageEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check33="3.3"
CHECK_TITLE_check33="[check33] Ensure a log metric filter and alarm exist for usage of root account"
CHECK_SCORED_check33="SCORED"
CHECK_CIS_LEVEL_check33="LEVEL1"
CHECK_SEVERITY_check33="Medium"
CHECK_ASFF_TYPE_check33="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check33="AwsCloudTrailTrail"
CHECK_ALTERNATE_check303="check33"
CHECK_ASFF_COMPLIANCE_TYPE_check33="ens-op.exp.8.aws.trail.5"
CHECK_SERVICENAME_check33="iam"
CHECK_RISK_check33='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check33='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check33='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check33='Logging and Monitoring'

check33(){
  if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
    textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
  else
    check3x '\$\.userIdentity\.type\s*=\s*"Root".+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*"AwsServiceEvent"'
  fi
}

providers/aws/services/iam/check32 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name ConsoleSignInWithoutMfaCount \
#     --filter-pattern '{ $.eventName = "ConsoleLogin" && $.additionalEventData.MFAUsed != "Yes" }' \
#     --metric-transformations metricName=ConsoleSignInWithoutMfaCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name ConsoleSignInWithoutMfaAlarm \
#     --alarm-description "Triggered by sign-in requests made without MFA." \
#     --metric-name ConsoleSignInWithoutMfaCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check32="3.2"
CHECK_TITLE_check32="[check32] Ensure a log metric filter and alarm exist for Management Console sign-in without MFA"
CHECK_SCORED_check32="SCORED"
CHECK_CIS_LEVEL_check32="LEVEL1"
CHECK_SEVERITY_check32="Medium"
CHECK_ASFF_TYPE_check32="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check32="AwsCloudTrailTrail"
CHECK_ALTERNATE_check302="check32"
CHECK_ASFF_COMPLIANCE_TYPE_check32="ens-op.exp.8.aws.trail.4"
CHECK_SERVICENAME_check32="iam"
CHECK_RISK_check32='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check32='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check32='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check32='Logging and Monitoring'

check32(){
  check3x '\$\.eventName\s*=\s*"ConsoleLogin".+\$\.additionalEventData\.MFAUsed\s*!=\s*"Yes"'
}

providers/aws/services/iam/check31 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/MyCloudTrailLG \
#     --filter-name AWSAuthorizationFailures \
#     --filter-pattern '{ $.errorCode = "*UnauthorizedOperation" || $.errorCode = "AccessDenied*" }' \
#     --metric-transformations metricName=AuthorizationFailureCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name "Authorization Failures" \
#     --alarm-description "Alarm triggered when unauthorized API calls are made" \
#     --metric-name AuthorizationFailureCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check31="3.1"
CHECK_TITLE_check31="[check31] Ensure a log metric filter and alarm exist for unauthorized API calls"
CHECK_SCORED_check31="SCORED"
CHECK_CIS_LEVEL_check31="LEVEL1"
CHECK_SEVERITY_check31="Medium"
CHECK_ASFF_TYPE_check31="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check31="AwsCloudTrailTrail"
CHECK_ALTERNATE_check301="check31"
CHECK_ASFF_COMPLIANCE_TYPE_check31="ens-op.exp.8.aws.trail.2"
CHECK_SERVICENAME_check31="iam"
CHECK_RISK_check31='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check31='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check31='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check31='Logging and Monitoring'

check31(){
  check3x '\$\.errorCode\s*=\s*"\*UnauthorizedOperation".+\$\.errorCode\s*=\s*"AccessDenied\*"'
}

providers/aws/services/iam/check19 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check19="1.9"
CHECK_TITLE_check19="[check19] Ensure IAM password policy requires minimum length of 14 or greater"
CHECK_SCORED_check19="SCORED"
CHECK_CIS_LEVEL_check19="LEVEL1"
CHECK_SEVERITY_check19="Medium"
CHECK_ASFF_TYPE_check19="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check109="check19"
CHECK_SERVICENAME_check19="iam"
CHECK_RISK_check19='Password policies are used to enforce password complexity requirements. IAM password policies can be used to ensure password are comprised of different character sets. It is recommended that the password policy require at least one uppercase letter.'
CHECK_REMEDIATION_check19='Ensure "Minimum password length" is set to 14 or greater.'
CHECK_DOC_check19='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html'
CHECK_CAF_EPIC_check19='IAM'

check19(){
  # "Ensure IAM password policy requires minimum length of 14 or greater (Scored)"
  COMMAND19=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.MinimumPasswordLength' 2> /dev/null)
  if [[ $COMMAND19 -gt "13" ]];then
    textPass "$REGION: Password Policy requires more than 13 characters" "$REGION" "password policy"
  else
    textFail "$REGION: Password Policy missing or weak length requirement" "$REGION" "password policy"
  fi
}

providers/aws/services/iam/check18 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check18="1.8"
CHECK_TITLE_check18="[check18] Ensure IAM password policy require at least one number"
CHECK_SCORED_check18="SCORED"
CHECK_CIS_LEVEL_check18="LEVEL1"
CHECK_SEVERITY_check18="Medium"
CHECK_ASFF_TYPE_check18="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check108="check18"
CHECK_SERVICENAME_check18="iam"
CHECK_RISK_check18='Password policies are used to enforce password complexity requirements. IAM password policies can be used to ensure password are comprised of different character sets. It is recommended that the password policy require at least one uppercase letter.'
CHECK_REMEDIATION_check18='Ensure "Require at least one number " is checked under "Password Policy".'
CHECK_DOC_check18='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html'
CHECK_CAF_EPIC_check18='IAM'

check18(){
  # "Ensure IAM password policy require at least one number (Scored)"
  COMMAND18=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireNumbers' 2> /dev/null) # must be true
  if [[ "$COMMAND18" == "true" ]];then
    textPass "$REGION: Password Policy requires number" "$REGION" "password policy"
  else
    textFail "$REGION: Password Policy missing number requirement" "$REGION" "password policy"
  fi
}

providers/aws/services/iam/check17 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check17="1.7"
CHECK_TITLE_check17="[check17] Ensure IAM password policy require at least one symbol"
CHECK_SCORED_check17="SCORED"
CHECK_CIS_LEVEL_check17="LEVEL1"
CHECK_SEVERITY_check17="Medium"
CHECK_ASFF_TYPE_check17="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check107="check17"
CHECK_SERVICENAME_check17="iam"
CHECK_RISK_check17='Password policies are used to enforce password complexity requirements. IAM password policies can be used to ensure password are comprised of different character sets. It is recommended that the password policy require at least one uppercase letter.'
CHECK_REMEDIATION_check17='Ensure "Require at least one non-alphanumeric character" is checked under "Password Policy".'
CHECK_DOC_check17='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html'
CHECK_CAF_EPIC_check17='IAM'

check17(){
  # "Ensure IAM password policy require at least one symbol (Scored)"
  COMMAND17=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireSymbols' 2> /dev/null) # must be true
  if [[ "$COMMAND17" == "true" ]];then
    textPass "$REGION: Password Policy requires symbol" "$REGION" "password policy"
  else
    textFail "$REGION: Password Policy missing symbol requirement" "$REGION" "password policy"
  fi
}

providers/aws/services/iam/check16 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check16="1.6"
CHECK_TITLE_check16="[check16] Ensure IAM password policy require at least one lowercase letter"
CHECK_SCORED_check16="SCORED"
CHECK_CIS_LEVEL_check16="LEVEL1"
CHECK_SEVERITY_check16="Medium"
CHECK_ASFF_TYPE_check16="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check106="check16"
CHECK_SERVICENAME_check16="iam"
CHECK_RISK_check16='Password policies are used to enforce password complexity requirements. IAM password policies can be used to ensure password are comprised of different character sets. It is recommended that the password policy require at least one uppercase letter.'
CHECK_REMEDIATION_check16='Ensure "Requires at least one lowercase letter" is checked under "Password Policy".'
CHECK_DOC_check16='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html'
CHECK_CAF_EPIC_check16='IAM'

check16(){
  # "Ensure IAM password policy require at least one lowercase letter (Scored)"
  COMMAND16=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireLowercaseCharacters' 2> /dev/null) # must be true
  if [[ "$COMMAND16" == "true" ]];then
    textPass "$REGION: Password Policy requires lower case" "$REGION" "password policy"
  else
    textFail "$REGION: Password Policy missing lower-case requirement" "$REGION" "password policy"
  fi
}

providers/aws/services/iam/check15 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check15="1.5"
CHECK_TITLE_check15="[check15] Ensure IAM password policy requires at least one uppercase letter"
CHECK_SCORED_check15="SCORED"
CHECK_CIS_LEVEL_check15="LEVEL1"
CHECK_SEVERITY_check15="Medium"
CHECK_ASFF_TYPE_check15="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check105="check15"
CHECK_SERVICENAME_check15="iam"
CHECK_RISK_check15='Password policies are used to enforce password complexity requirements. IAM password policies can be used to ensure password are comprised of different character sets. It is recommended that the password policy require at least one uppercase letter.'
CHECK_REMEDIATION_check15='Ensure "Requires at least one uppercase letter" is checked under "Password Policy".'
CHECK_DOC_check15='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html'
CHECK_CAF_EPIC_check15='IAM'

check15(){
  # "Ensure IAM password policy requires at least one uppercase letter (Scored)"
  COMMAND15=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireUppercaseCharacters' 2> /dev/null) # must be true
  if [[ "$COMMAND15" == "true" ]];then
    textPass "$REGION: Password Policy requires upper case" "$REGION" "password policy"
  else
    textFail "$REGION: Password Policy missing upper-case requirement" "$REGION" "password policy"
  fi
}

providers/aws/services/iam/check14 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check14="1.4"
CHECK_TITLE_check14="[check14] Ensure access keys are rotated every 90 days or less"
CHECK_SCORED_check14="SCORED"
CHECK_CIS_LEVEL_check14="LEVEL1"
CHECK_SEVERITY_check14="Medium"
CHECK_ASFF_TYPE_check14="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check14="AwsIamUser"
CHECK_ALTERNATE_check104="check14"
CHECK_ASFF_COMPLIANCE_TYPE_check14="ens-op.acc.1.aws.iam.4 ens-op.acc.5.aws.iam.3"
CHECK_SERVICENAME_check14="iam"
CHECK_RISK_check14='Access keys consist of an access key ID and secret access key which are used to sign programmatic requests that you make to AWS. AWS users need their own access keys to make programmatic calls to AWS from the AWS Command Line Interface (AWS CLI)- Tools for Windows PowerShell- the AWS SDKs- or direct HTTP calls using the APIs for individual AWS services. It is recommended that all access keys be regularly rotated.'
CHECK_REMEDIATION_check14='Use the credential report to  ensure  access_key_X_last_rotated  is less than 90 days ago.'
CHECK_DOC_check14='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html'
CHECK_CAF_EPIC_check14='IAM'

check14(){
  # "Ensure access keys are rotated every 90 days or less (Scored)" # also checked by Security Monkey
  LIST_OF_USERS_WITH_ACCESS_KEY1=$(cat $TEMP_REPORT_FILE| awk -F, '{ print $1, $9 }' |grep "\ true" | awk '{ print $1 }')
  LIST_OF_USERS_WITH_ACCESS_KEY2=$(cat $TEMP_REPORT_FILE| awk -F, '{ print $1, $14 }' |grep "\ true" | awk '{ print $1 }')
  C14_NUM_USERS1=0
  C14_NUM_USERS2=0
    if [[ $LIST_OF_USERS_WITH_ACCESS_KEY1 ]]; then
      # textFail "Users with access key 1 older than 90 days:"
      for user in $LIST_OF_USERS_WITH_ACCESS_KEY1; do
        # check access key 1
        DATEROTATED1=$(cat $TEMP_REPORT_FILE | grep -v user_creation_time | grep "^${user},"| awk -F, '{ print $10 }' | grep -v "N/A" | awk -F"T" '{ print $1 }')
        HOWOLDER=$(how_older_from_today $DATEROTATED1)

        if [ $HOWOLDER -gt "90" ];then
          textFail "$REGION: $user has not rotated access key 1 in over 90 days" "$REGION" "$user"
          C14_NUM_USERS1=$(expr $C14_NUM_USERS1 + 1)
        fi
      done
      if [[ $C14_NUM_USERS1 -eq 0 ]]; then
        textPass "$REGION: No users with access key 1 older than 90 days" "$REGION" "$user"
      fi
    else
      textPass "$REGION: No users with access key 1" "$REGION" "$user"
    fi

    if [[ $LIST_OF_USERS_WITH_ACCESS_KEY2 ]]; then
      # textFail "Users with access key 2 older than 90 days:"
      for user in $LIST_OF_USERS_WITH_ACCESS_KEY2; do
        # check access key 2
        DATEROTATED2=$(cat $TEMP_REPORT_FILE | grep -v user_creation_time | grep "^${user},"| awk -F, '{ print $15 }' | grep -v "N/A" | awk -F"T" '{ print $1 }')
        HOWOLDER=$(how_older_from_today $DATEROTATED2)
        if [ $HOWOLDER -gt "90" ];then
          textFail "$REGION: $user has not rotated access key 2 in over 90 days" "$REGION" "$user"
          C14_NUM_USERS2=$(expr $C14_NUM_USERS2 + 1)
        fi
      done
      if [[ $C14_NUM_USERS2 -eq 0 ]]; then
        textPass "$REGION: No users with access key 2 older than 90 days" "$REGION" "$user"
      fi
    else
      textPass "$REGION: No users with access key 2" "$REGION" "$user"
    fi
}

providers/aws/services/iam/check13 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check13="1.3"
CHECK_TITLE_check13="[check13] Ensure credentials unused for 90 days or greater are disabled"
CHECK_SCORED_check13="SCORED"
CHECK_CIS_LEVEL_check13="LEVEL1"
CHECK_SEVERITY_check13="Medium"
CHECK_ASFF_TYPE_check13="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check13="AwsIamUser"
CHECK_ALTERNATE_check103="check13"
CHECK_ASFF_COMPLIANCE_TYPE_check13="ens-op.acc.1.aws.iam.3 ens-op.acc.5.aws.iam.4"
CHECK_SERVICENAME_check13="iam"
CHECK_RISK_check13='AWS IAM users can access AWS resources using different types of credentials (passwords or access keys). It is recommended that all credentials that have been unused in 90 or greater days be removed or deactivated.'
CHECK_REMEDIATION_check13='Use the credential report to  ensure password_last_changed is less than 90 days ago.'
CHECK_DOC_check13='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html'
CHECK_CAF_EPIC_check13='IAM'

check13(){
  check_creds_used_in_last_days 90
}

providers/aws/services/iam/check122 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check122="1.22"
CHECK_TITLE_check122="[check122] Ensure IAM policies that allow full \"*:*\" administrative privileges are not created"
CHECK_SCORED_check122="SCORED"
CHECK_CIS_LEVEL_check122="LEVEL1"
CHECK_SEVERITY_check122="Medium"
CHECK_ASFF_TYPE_check122="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check122="AwsIamPolicy"
CHECK_ALTERNATE_check122="check122"
CHECK_SERVICENAME_check122="iam"
CHECK_RISK_check122='IAM policies are the means by which privileges are granted to users; groups; or roles. It is recommended and considered a standard security advice to grant least privilege—that is; granting only the permissions required to perform a task. Determine what users need to do and then craft policies for them that let the users perform only those tasks instead of allowing full administrative privileges. Providing full administrative privileges instead of restricting to the minimum set of permissions that the user is required to do exposes the resources to potentially unwanted actions.'
CHECK_REMEDIATION_check122='It is more secure to start with a minimum set of permissions and grant additional permissions as necessary; rather than starting with permissions that are too lenient and then trying to tighten them later. List policies an analyze if permissions are the least possible to conduct business activities.'
CHECK_DOC_check122='http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html'
CHECK_CAF_EPIC_check122='IAM'

check122(){
  # "Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)"
  LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text $PROFILE_OPT --region $REGION --scope Local --query 'Policies[*].[Arn,DefaultVersionId]' | grep -v -e '^None$' | awk -F '\t' '{print $1","$2"\n"}')
  if [[ $LIST_CUSTOM_POLICIES ]]; then
    for policy in $LIST_CUSTOM_POLICIES; do
      POLICY_ARN=$(awk 'BEGIN{FS=OFS=","}{NF--; print}' <<< "${policy}")
      POLICY_VERSION=$(awk -F ',' '{print $(NF)}' <<< "${policy}")
      POLICY_WITH_FULL=$($AWSCLI iam get-policy-version --output text --policy-arn $POLICY_ARN --version-id $POLICY_VERSION --query "[PolicyVersion.Document.Statement] | [] | [?Action!=null] | [?Effect == 'Allow' && Resource == '*' && Action == '*']" $PROFILE_OPT --region $REGION)
      if [[ $POLICY_WITH_FULL ]]; then
        POLICIES_ALLOW_LIST="$POLICIES_ALLOW_LIST $POLICY_ARN"
      else
        textPass "$REGION: Policy ${policy//,/[comma]} that does not allow full \"*:*\" administrative privileges" "${REGION}" "${policy}"
      fi
    done
    if [[ $POLICIES_ALLOW_LIST ]]; then
      for policy in $POLICIES_ALLOW_LIST; do
        textFail "$REGION: Policy ${policy//,/[comma]} allows \"*:*\"" "${REGION}" "${policy}"
      done
    fi
  else
    textPass "$REGION: No custom policies found" "$REGION"
  fi
}

providers/aws/services/iam/check121 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check121="1.21"
CHECK_TITLE_check121="[check121] Do not setup access keys during initial user setup for all IAM users that have a console password"
CHECK_SCORED_check121="NOT_SCORED"
CHECK_CIS_LEVEL_check121="LEVEL1"
CHECK_SEVERITY_check121="Medium"
CHECK_ASFF_TYPE_check121="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check121="AwsIamUser"
CHECK_ALTERNATE_check121="check121"
CHECK_ASFF_COMPLIANCE_TYPE_check121="ens-op.acc.1.aws.iam.5"
CHECK_SERVICENAME_check121="iam"
CHECK_RISK_check121='AWS console defaults the checkbox for creating access keys to enabled. This results in many access keys being generated unnecessarily. In addition to unnecessary credentials; it also generates unnecessary management work in auditing and rotating these keys. Requiring that additional steps be taken by the user after their profile has been created will give a stronger indication of intent that access keys are (a) necessary for their work and (b) once the access key is established on an account that the keys may be in use somewhere in the organization.'
CHECK_REMEDIATION_check121='From the IAM console: generate credential report and disable not required keys.'
CHECK_DOC_check121='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html'
CHECK_CAF_EPIC_check121='IAM'

check121(){
  # "Do not setup access keys during initial user setup for all IAM users that have a console password (Not Scored)"
  LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text $PROFILE_OPT --region $REGION)
  # List of USERS with KEY1 last_used_date as N/A
  LIST_USERS_KEY1_NA=$(for user in $LIST_USERS; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$11 }'|grep N/A |awk '{ print $1 }'; done)
  # List of USERS with KEY1 active, last_used_date as N/A and have a console password
  LIST_USERS_KEY1_ACTIVE=$(for user in $LIST_USERS_KEY1_NA; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$4,$9 }'|grep "true true$"|awk '{ print $1 }'|sed 's/[[:blank:]]+/,/g' ; done)
  if [[ $LIST_USERS_KEY1_ACTIVE ]]; then
    for user in $LIST_USERS_KEY1_ACTIVE; do
      textFail "$REGION: User $user has never used access key 1" "$REGION" "$user"
    done
  else
    textPass "$REGION: No users found with access key 1 never used" "$REGION" "$user"
  fi
  # List of USERS with KEY2 last_used_date as N/A
  LIST_USERS_KEY2_NA=$(for user in $LIST_USERS; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$16 }'|grep N/A |awk '{ print $1 }' ; done)
  # List of USERS with KEY2 active, last_used_date as N/A and have a console password
  LIST_USERS_KEY2_ACTIVE=$(for user in $LIST_USERS_KEY2_NA; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$4,$14 }'|grep "true true$" |awk '{ print $1 }' ; done)
  if [[ $LIST_USERS_KEY2_ACTIVE ]]; then
    for user in $LIST_USERS_KEY2_ACTIVE; do
      textFail "$REGION: User $user has never used access key 2" "$REGION" "$user"
    done
  else
    textPass "$REGION: No users found with access key 2 never used" "$REGION" "$user"
  fi
}

providers/aws/services/iam/check120 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check120="1.20"
CHECK_TITLE_check120="[check120] Ensure a support role has been created to manage incidents with AWS Support"
CHECK_SCORED_check120="SCORED"
CHECK_CIS_LEVEL_check120="LEVEL1"
CHECK_SEVERITY_check120="Medium"
CHECK_ASFF_TYPE_check120="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check120="AwsIamRole"
CHECK_ALTERNATE_check120="check120"
CHECK_ASFF_COMPLIANCE_TYPE_check120="ens-op.acc.1.aws.iam.4"
CHECK_SERVICENAME_check120="iam"
CHECK_RISK_check120='AWS provides a support center that can be used for incident notification and response; as well as technical support and customer services. Create an IAM Role to allow authorized users to manage incidents with AWS Support.'
CHECK_REMEDIATION_check120='Create an IAM role for managing incidents with AWS.'
CHECK_DOC_check120='https://docs.aws.amazon.com/awssupport/latest/user/using-service-linked-roles-sup.html'
CHECK_CAF_EPIC_check120='IAM'

check120(){
  # "Ensure a support role has been created to manage incidents with AWS Support (Scored)"
  SUPPORTPOLICYARN=$($AWSCLI iam list-policies --query "Policies[?PolicyName == 'AWSSupportAccess'].Arn" $PROFILE_OPT --region $REGION --output text)
  if [[ $SUPPORTPOLICYARN ]];then
    for policyarn in $SUPPORTPOLICYARN;do
      POLICYROLES=$($AWSCLI iam list-entities-for-policy --policy-arn $policyarn $PROFILE_OPT --region $REGION --output text | awk -F$'\t' '{ print $3 }')
      if [[ $POLICYROLES ]];then
        for name in $POLICYROLES; do
          textPass "$REGION: Support Policy attached to $name" "$REGION" "$name"
        done
        # for user in $(echo "$POLICYUSERS" | grep UserName | cut -d'"' -f4) ; do
        #   textInfo "User $user has support access via $policyarn"
        # done
      else
        textFail "$REGION: Support Policy not applied to any Role" "$REGION" "$name"
      fi
    done
  else
    textFail "$REGION: No Support Policy found" "$REGION" "$name"
  fi
}

providers/aws/services/iam/check12 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check12="1.2"
CHECK_TITLE_check12="[check12] Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password"
CHECK_SCORED_check12="SCORED"
CHECK_CIS_LEVEL_check12="LEVEL1"
CHECK_SEVERITY_check12="High"
CHECK_ASFF_TYPE_check12="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check12="AwsIamUser"
CHECK_ALTERNATE_check102="check12"
CHECK_ASFF_COMPLIANCE_TYPE_check12="ens-op.acc.5.aws.iam.1"
CHECK_SERVICENAME_check12="iam"
CHECK_RISK_check12='Unauthorized access to this critical account if password is not secure or it is disclosed in any way.'
CHECK_REMEDIATION_check12='Enable MFA for root account. MFA is a simple best practice that adds an extra layer of protection on top of your user name and password. Recommended to use hardware keys over virtual MFA.'
CHECK_DOC_check12='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html'
CHECK_CAF_EPIC_check12='IAM'

check12(){
  # "Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password (Scored)"
  # List users with password enabled
  COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$4 }' |grep 'true$' | awk '{ print $1 }')
  COMMAND12=$(
    for i in $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED; do
      cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$8 }' |grep "^$i " |grep false | awk '{ print $1 }'
    done)
    if [[ $COMMAND12 ]]; then
      for u in $COMMAND12; do
        textFail "$REGION: User $u has Password enabled but MFA disabled" "$REGION" "$u"
      done
    else
      textPass "$REGION: No users found with Password enabled and MFA disabled" "$REGION" "$u"
    fi
}

providers/aws/services/iam/check116 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check116="1.16"
CHECK_TITLE_check116="[check116] Ensure IAM policies are attached only to groups or roles"
CHECK_SCORED_check116="SCORED"
CHECK_CIS_LEVEL_check116="LEVEL1"
CHECK_SEVERITY_check116="Low"
CHECK_ASFF_TYPE_check116="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check116="AwsIamUser"
CHECK_ALTERNATE_check116="check116"
CHECK_ASFF_COMPLIANCE_TYPE_check116="ens-op.acc.3.aws.iam.1"
CHECK_SERVICENAME_check116="iam"
CHECK_RISK_check116='By default IAM users; groups; and roles have no access to AWS resources. IAM policies are the means by which privileges are granted to users; groups; or roles. It is recommended that IAM policies be applied directly to groups and roles but not users. Assigning privileges at the group or role level reduces the complexity of access management as the number of users grow. Reducing access management complexity may in-turn reduce opportunity for a principal to inadvertently receive or retain excessive privileges.'
CHECK_REMEDIATION_check116='Remove any policy attached directly to the user. Use groups or roles instead.'
CHECK_DOC_check116='https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html'
CHECK_CAF_EPIC_check116='IAM'

check116(){
  # "Ensure IAM policies are attached only to groups or roles (Scored)"
  LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text $PROFILE_OPT --region $REGION)
  for user in $LIST_USERS;do
    USER_ATTACHED_POLICY=$($AWSCLI iam list-attached-user-policies --output text $PROFILE_OPT --region $REGION --user-name $user)
    USER_INLINE_POLICY=$($AWSCLI iam list-user-policies --output text $PROFILE_OPT --region $REGION --user-name $user)
    if [[ $USER_ATTACHED_POLICY ]] || [[ $USER_INLINE_POLICY ]]
    then
      if [[ $USER_ATTACHED_POLICY ]]
      then
        textFail "$REGION: $user has managed policy directly attached" "$REGION" "$user"
      fi
      if [[ $USER_INLINE_POLICY ]]
      then
        textFail "$REGION: $user has inline policy directly attached" "$REGION" "$user"
      fi
    else
      textPass "$REGION: No policies attached to user $user" "$REGION" "$user"
    fi
  done
}

providers/aws/services/iam/check114 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check114="1.14"
CHECK_TITLE_check114="[check114] Ensure hardware MFA is enabled for the root account"
CHECK_SCORED_check114="SCORED"
CHECK_CIS_LEVEL_check114="LEVEL2"
CHECK_SEVERITY_check114="Critical"
CHECK_ASFF_TYPE_check114="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check114="check114"
CHECK_SERVICENAME_check114="iam"
CHECK_RISK_check114='The root account is the most privileged user in an AWS account. MFA adds an extra layer of protection on top of a user name and password. With MFA enabled when a user signs in to an AWS website they will be prompted for their user name and password as well as for an authentication code from their AWS MFA device. For Level 2 it is recommended that the root account be protected with a hardware MFA.'
CHECK_REMEDIATION_check114='Using IAM console navigate to Dashboard and expand Activate MFA on your root account.'
CHECK_DOC_check114='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#id_root-user_manage_mfa'
CHECK_CAF_EPIC_check114='IAM'

check114(){
  if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
    textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
  else
    # "Ensure hardware MFA is enabled for the root account (Scored)"
    COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled')
    if [ "$COMMAND113" == "1" ]; then
      COMMAND114=$($AWSCLI iam list-virtual-mfa-devices $PROFILE_OPT --region $REGION --output text --assignment-status Assigned --query 'VirtualMFADevices[*].[SerialNumber]' | grep "^arn:${AWS_PARTITION}:iam::[0-9]\{12\}:mfa/root-account-mfa-device$")
      if [[ "$COMMAND114" ]]; then
        textFail "$REGION: Only Virtual MFA is enabled for root" "$REGION" "MFA"
      else
        textPass "$REGION: Hardware MFA is enabled for root" "$REGION" "MFA"
      fi
    else
      textFail "$REGION: MFA is not ENABLED for root account" "$REGION" "MFA"
    fi
  fi
}

providers/aws/services/iam/check113 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check113="1.13"
CHECK_TITLE_check113="[check113] Ensure MFA is enabled for the root account"
CHECK_SCORED_check113="SCORED"
CHECK_CIS_LEVEL_check113="LEVEL1"
CHECK_SEVERITY_check113="Critical"
CHECK_ASFF_TYPE_check113="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check113="check113"
CHECK_SERVICENAME_check113="iam"
CHECK_RISK_check113='The root account is the most privileged user in an AWS account. MFA adds an extra layer of protection on top of a user name and password. With MFA enabled when a user signs in to an AWS website they will be prompted for their user name and password as well as for an authentication code from their AWS MFA device. When virtual MFA is used for root accounts it is recommended that the device used is NOT a personal device but rather a dedicated mobile device (tablet or phone) that is managed to be kept charged and secured independent of any individual personal devices. ("non-personal virtual MFA") This lessens the risks of losing access to the MFA due to device loss / trade-in or if the individual owning the device is no longer employed at the company.'
CHECK_REMEDIATION_check113='Using IAM console navigate to Dashboard and expand Activate MFA on your root account.'
CHECK_DOC_check113='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#id_root-user_manage_mfa'
CHECK_CAF_EPIC_check113='IAM'

check113(){
  if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
    textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
  else
    # "Ensure MFA is enabled for the root account (Scored)"
    COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled')
    if [ "$COMMAND113" == "1" ]; then
      textPass "$REGION: Virtual MFA is enabled for root" "$REGION" "MFA"
    else
      textFail "$REGION: MFA is not ENABLED for root account" "$REGION" "MFA"
    fi
  fi
}

providers/aws/services/iam/check112 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check112="1.12"
CHECK_TITLE_check112="[check112] Ensure no root account access key exists"
CHECK_SCORED_check112="SCORED"
CHECK_CIS_LEVEL_check112="LEVEL1"
CHECK_SEVERITY_check112="Critical"
CHECK_ASFF_TYPE_check112="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check112="check112"
CHECK_SERVICENAME_check112="iam"
CHECK_RISK_check112='The root account is the most privileged user in an AWS account. AWS Access Keys provide programmatic access to a given AWS account. It is recommended that all access keys associated with the root account be removed. Removing access keys associated with the root account limits vectors by which the account can be compromised. Removing the root access keys encourages the creation and use of role based accounts that are least privileged.'
CHECK_REMEDIATION_check112='Use the credential report to  that the user and ensure the access_key_1_active and access_key_2_active fields are set to FALSE .'
CHECK_DOC_check112='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html'
CHECK_CAF_EPIC_check112='IAM'

check112(){
  # "Ensure no root account access key exists (Scored)"
  # ensure the access_key_1_active and access_key_2_active fields are set to FALSE.
  ROOTKEY1=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $9 }')
  ROOTKEY2=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $14 }')
  if [ "$ROOTKEY1" == "false" ];then
    textPass "$REGION: No access key 1 found for root" "$REGION" "root access key1"
  else
    textFail "$REGION: Found access key 1 for root" "$REGION" "root access key1"
  fi
  if [ "$ROOTKEY2" == "false" ];then
    textPass "$REGION: No access key 2 found for root" "$REGION" "root access key2"
  else
    textFail "$REGION: Found access key 2 for root" "$REGION" "root access key2"
  fi
}

providers/aws/services/iam/check111 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check111="1.11"
CHECK_TITLE_check111="[check111] Ensure IAM password policy expires passwords within 90 days or less"
CHECK_SCORED_check111="SCORED"
CHECK_CIS_LEVEL_check111="LEVEL1"
CHECK_SEVERITY_check111="Medium"
CHECK_ASFF_TYPE_check111="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check111="check111"
CHECK_SERVICENAME_check111="iam"
CHECK_RISK_check111='Password policies are used to enforce password complexity requirements. IAM password policies can be used to ensure password are comprised of different character sets. It is recommended that the password policy require at least one uppercase letter.'
CHECK_REMEDIATION_check111='Ensure "Password expiration period (in days):" is set to 90 or less.'
CHECK_DOC_check111='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html'
CHECK_CAF_EPIC_check111='IAM'

check111(){
  # "Ensure IAM password policy expires passwords within 90 days or less (Scored)"
  COMMAND111=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --query PasswordPolicy.MaxPasswordAge --output text 2> /dev/null)
  if [[ $COMMAND111 == [0-9]* ]];then
    if [[ "$COMMAND111" -le "90" ]];then
      textPass "$REGION: Password Policy includes expiration (Value: $COMMAND111)" "$REGION" "password policy"
    else
      textFail "$REGION: Password expiration is set greater than 90 days" "$REGION" "password policy"
    fi
  else
    textFail "$REGION: Password expiration is not set" "$REGION" "password policy"
  fi
}

providers/aws/services/iam/check110 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check110="1.10"
CHECK_TITLE_check110="[check110] Ensure IAM password policy prevents password reuse: 24 or greater"
CHECK_SCORED_check110="SCORED"
CHECK_CIS_LEVEL_check110="LEVEL1"
CHECK_SEVERITY_check110="Medium"
CHECK_ASFF_TYPE_check110="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check110="check110"
CHECK_SERVICENAME_check110="iam"
CHECK_RISK_check110='Password policies are used to enforce password complexity requirements. IAM password policies can be used to ensure password are comprised of different character sets. It is recommended that the password policy require at least one uppercase letter.'
CHECK_REMEDIATION_check110='Ensure "Number of passwords to remember" is set to 24.'
CHECK_DOC_check110='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html'
CHECK_CAF_EPIC_check110='IAM'

check110(){
  # "Ensure IAM password policy prevents password reuse: 24 or greater (Scored)"
  COMMAND110=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --query 'PasswordPolicy.PasswordReusePrevention' --output text 2> /dev/null)
  if [[ $COMMAND110 ]];then
    if [[ $COMMAND110 -gt "23" ]];then
      textPass "$REGION: Password Policy limits reuse" "$REGION" "password policy"
    else
      textFail "$REGION: Password Policy has weak reuse requirement (lower than 24)" "$REGION" "password policy"
    fi
  else
    textFail "$REGION: Password Policy missing reuse requirement" "$REGION" "password policy"
  fi
}

providers/aws/services/iam/check11 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check11="1.1"
CHECK_TITLE_check11="[check11] Avoid the use of the root account"
CHECK_SCORED_check11="SCORED"
CHECK_CIS_LEVEL_check11="LEVEL1"
CHECK_SEVERITY_check11="High"
CHECK_ASFF_TYPE_check11="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check101="check11"
CHECK_SERVICENAME_check11="iam"
CHECK_RISK_check11='The "root" account has unrestricted access to all resources in the AWS account. It is highly recommended that the use of this account be avoided.'
CHECK_REMEDIATION_check11='Follow the remediation instructions of the Ensure IAM policies are attached only to groups or roles recommendation.'
CHECK_DOC_check11='http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html'
CHECK_CAF_EPIC_check11='IAM'

check11(){
  if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
        textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
  else
    # "Avoid the use of the root account (Scored)."
    MAX_DAYS=-1
    last_login_dates=$(cat $TEMP_REPORT_FILE | awk -F, '{ print $1,$5,$11,$16 }' | grep '<root_account>' | cut -d' ' -f2,3,4)

    failures=0
    for date in $last_login_dates; do
      if [[ ${date%T*} =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]];then
        days_not_in_use=$(how_many_days_from_today ${date%T*})
        if [ "$days_not_in_use" -gt "$MAX_DAYS" ];then
            failures=1
            textFail "$REGION: Root user in the account was last accessed ${MAX_DAYS#-} day ago" "$REGION" "root"
            break
        fi
      fi
    done

    if [[ $failures == 0 ]]; then
        textPass "$REGION: Root user in the account wasn't accessed in the last ${MAX_DAYS#-} days" "$REGION" "root"
    fi
  fi
}

providers/aws/services/guardduty/check_extra7139 METASEP
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7139="7.139"
CHECK_TITLE_extra7139="[extra7139] There are High severity GuardDuty findings "
CHECK_SCORED_extra7139="NOT_SCORED"
CHECK_CIS_LEVEL_extra7139="EXTRA"
CHECK_SEVERITY_extra7139="High"
CHECK_ASFF_RESOURCE_TYPE_extra7139="AwsGuardDutyDetector"
CHECK_ALTERNATE_check7139="extra7139"
CHECK_SERVICENAME_extra7139="guardduty"
CHECK_RISK_extra7139='If critical findings are not addressed threats can spread in the environment.'
CHECK_REMEDIATION_extra7139='Review and remediate critical GuardDuty findings as quickly as possible.'
CHECK_DOC_extra7139='https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html'
CHECK_CAF_EPIC_extra7139='Incident Response'
extra7139(){

  for regx in $REGIONS; do
    DETECTORS_LIST=""
    DETECTORS_LIST=$($AWSCLI guardduty list-detectors --query DetectorIds $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$DETECTORS_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list detectors" "$regx"
        continue
    fi
    if [[ $DETECTORS_LIST ]];then
      for DETECTOR in $DETECTORS_LIST;do
        FINDINGS_COUNT=""
        FINDINGS_COUNT=$($AWSCLI $PROFILE_OPT --region $regx --output text guardduty list-findings --detector-id $DETECTOR --finding-criteria '{"Criterion":{"severity": {"Eq":["8"]}, "service.archived": {"Eq": ["false"]}}}' 2> /dev/null | wc -l | xargs) # Severity LOW=2, MED=4, HIGH=8
          if [[ $FINDINGS_COUNT -gt 0 ]];then
            textFail "$regx: GuardDuty has $FINDINGS_COUNT high severity findings." "$regx"
          else
            textPass "$regx: GuardDuty has no high severity findings." "$regx"
          fi
      done
    else
      textInfo "$regx: No GuardDuty detectors found." "$regx"
    fi
  done
}

providers/aws/services/guardduty/check_extra713 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra713="7.13"
CHECK_TITLE_extra713="[extra713] Check if GuardDuty is enabled"
CHECK_SCORED_extra713="NOT_SCORED"
CHECK_CIS_LEVEL_extra713="EXTRA"
CHECK_SEVERITY_extra713="High"
CHECK_ALTERNATE_check713="extra713"
CHECK_ASFF_COMPLIANCE_TYPE_extra713="ens-op.mon.1.aws.duty.1"
CHECK_ASFF_RESOURCE_TYPE_extra713="AwsGuardDutyDetector"
CHECK_SERVICENAME_extra713="guardduty"
CHECK_RISK_extra713='Amazon GuardDuty is a continuous security monitoring service that analyzes and processes several datasources.'
CHECK_REMEDIATION_extra713='Enable GuardDuty and analyze its findings.'
CHECK_DOC_extra713='https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_settingup.html'
CHECK_CAF_EPIC_extra713='Data Protection'

extra713(){
  # "Check if GuardDuty is enabled "
  for regx in $REGIONS; do
    LIST_OF_GUARDDUTY_DETECTORS=$($AWSCLI guardduty list-detectors $PROFILE_OPT --region $regx --output text --query DetectorIds[*] 2> /dev/null)
    RESULT=$?
    if [ $RESULT -eq 0 ];then
      if [[ $LIST_OF_GUARDDUTY_DETECTORS ]];then
        while read -r detector;do
          DETECTOR_ENABLED=$($AWSCLI guardduty get-detector --detector-id $detector $PROFILE_OPT --region $regx --query "Status" --output text|grep ENABLED)
          if [[ $DETECTOR_ENABLED ]]; then
            textPass "$regx: GuardDuty detector $detector enabled" "$regx" "$detector"
          else
            textFail "$regx: GuardDuty detector $detector configured but suspended" "$regx" "$detector"
          fi
        done <<< "$LIST_OF_GUARDDUTY_DETECTORS"
        else
          textFail "$regx: GuardDuty detector not configured!" "$regx"
      fi
      else
      # if list-detectors return any error
      textInfo "$regx: GuardDuty not checked or Access Denied trying to get detector" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7122 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7122="7.122"
CHECK_TITLE_extra7122="[extra7122] Check if Glue ETL Jobs have Job bookmark encryption enabled."
CHECK_SCORED_extra7122="NOT_SCORED"
CHECK_CIS_LEVEL_extra7122="EXTRA"
CHECK_SEVERITY_extra7122="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7122="AwsGlue"
CHECK_ALTERNATE_check7122="extra7122"
CHECK_SERVICENAME_extra7122="glue"
CHECK_RISK_extra7122='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra7122='Enable Encryption in the Security configurations.'
CHECK_DOC_extra7122='https://docs.aws.amazon.com/glue/latest/dg/console-security-configurations.html'
CHECK_CAF_EPIC_extra7122='Data Protection'

extra7122(){
  for regx in $REGIONS; do
    JOB_LIST=$($AWSCLI glue get-jobs $PROFILE_OPT --region $regx --output json --query 'Jobs[*].{Name:Name,SecurityConfiguration:SecurityConfiguration}' 2>&1)
    if [[ $(echo "$JOB_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get jobs" "$regx"
        continue
    fi
    if [[ $JOB_LIST != '[]' ]]; then
      for job in $(echo "${JOB_LIST}" | jq -r '.[] | @base64'); do
          JOB_NAME=$(echo $job | base64 --decode | jq -r '.Name')
          SECURITY_CONFIGURATION=$(echo $job | base64 --decode | jq -r '.SecurityConfiguration // empty')
          if [[ ! -z "$SECURITY_CONFIGURATION" ]]; then
            JOB_BOOKMARK_ENCRYPTION=$($AWSCLI glue get-security-configuration --name "${SECURITY_CONFIGURATION}" $PROFILE_OPT --region $regx --output text --query 'SecurityConfiguration.EncryptionConfiguration.JobBookmarksEncryption.JobBookmarksEncryptionMode')
            if [[ "$JOB_BOOKMARK_ENCRYPTION" == "DISABLED" ]]; then
              textFail "$regx: Glue job $JOB_NAME does not have Job bookmark encryption enabled" "$regx" "$JOB_NAME"
            else
              textPass "$regx: Glue job $JOB_NAME does have $JOB_BOOKMARK_ENCRYPTION for Job bookmark encryption enabled" "$regx" "$JOB_NAME"
            fi
          else
            textFail "$regx: Glue job $JOB_NAME does not have Job bookmark encryption enabled" "$regx" "$JOB_NAME"
          fi
      done
    else
      textInfo "$regx: There are no Glue jobs" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7121 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7121="7.121"
CHECK_TITLE_extra7121="[extra7121] Check if Glue development endpoints have Job bookmark encryption enabled."
CHECK_SCORED_extra7121="NOT_SCORED"
CHECK_CIS_LEVEL_extra7121="EXTRA"
CHECK_SEVERITY_extra7121="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7121="AwsGlue"
CHECK_ALTERNATE_check7121="extra7121"
CHECK_SERVICENAME_extra7121="glue"
CHECK_RISK_extra7121='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra7121='Enable Encryption in the Security configurations.'
CHECK_DOC_extra7121='https://docs.aws.amazon.com/glue/latest/dg/console-security-configurations.html'
CHECK_CAF_EPIC_extra7121='Data Protection'

extra7121(){
  for regx in $REGIONS; do
    LIST_EP_SC=$($AWSCLI glue get-dev-endpoints $PROFILE_OPT --region $regx --query 'DevEndpoints[*].{Name:EndpointName,Security:SecurityConfiguration}' --output json 2>&1)
    if [[ $(echo "$LIST_EP_SC" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get dev endpoints" "$regx"
        continue
    fi
    if [[ $LIST_EP_SC != '[]' ]]; then
      for ep in $(echo "${LIST_EP_SC}"| jq -r '.[] | @base64');do
        ENDPOINT_NAME=$(echo $ep | base64 --decode | jq -r '.Name')
        ENDPOINT_SC=$(echo $ep | base64 --decode | jq -r '.Security // empty')
        if [[ ! -z "$ENDPOINT_SC" ]]; then
          ENDPOINT_SC_ENCRYPTION=$($AWSCLI glue get-security-configuration --name "${ENDPOINT_SC}" $PROFILE_OPT --region $regx --query 'SecurityConfiguration.EncryptionConfiguration.JobBookmarksEncryption.JobBookmarksEncryptionMode' --output text)
          if [[ "$ENDPOINT_SC_ENCRYPTION" == "DISABLED" ]]; then
            textFail "$regx: Glue development endpoint $ENDPOINT_NAME does not have Job Bookmark encryption enabled!" "$regx" "$ENDPOINT_NAME"
          else
            textPass "$regx: Glue development endpoint $ENDPOINT_NAME has Job Bookmark encryption enabled" "$regx" "$ENDPOINT_NAME"
          fi
        else
          textFail "$regx: Glue development endpoint $ENDPOINT_NAME does not have security configuration" "$regx" "$ENDPOINT_NAME"
        fi
      done
    else
      textInfo "$regx: There are no Glue development endpoints" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7120 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7120="7.120"
CHECK_TITLE_extra7120="[extra7120] Check if Glue ETL Jobs have CloudWatch Logs encryption enabled."
CHECK_SCORED_extra7120="NOT_SCORED"
CHECK_CIS_LEVEL_extra7120="EXTRA"
CHECK_SEVERITY_extra7120="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7120="AwsGlue"
CHECK_ALTERNATE_check7120="extra7120"
CHECK_SERVICENAME_extra7120="glue"
CHECK_RISK_extra7120='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra7120='Enable Encryption in the Security configurations.'
CHECK_DOC_extra7120='https://docs.aws.amazon.com/glue/latest/dg/console-security-configurations.html'
CHECK_CAF_EPIC_extra7120='Logging and Monitoring'

extra7120(){
  for regx in $REGIONS; do
    JOB_LIST=$($AWSCLI glue get-jobs $PROFILE_OPT --region $regx --output json --query 'Jobs[*].{Name:Name,SecurityConfiguration:SecurityConfiguration}' 2>&1)
    if [[ $(echo "$JOB_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get jobs" "$regx"
        continue
    fi
    if [[ $JOB_LIST != '[]' ]]; then
      for job in $(echo "${JOB_LIST}" | jq -r '.[] | @base64'); do
          JOB_NAME=$(echo $job | base64 --decode | jq -r '.Name')
          SECURITY_CONFIGURATION=$(echo $job | base64 --decode | jq -r '.SecurityConfiguration  // empty')
          if [[ ! -z "$SECURITY_CONFIGURATION" ]]; then
            CLOUDWATCH_ENCRYPTION=$($AWSCLI glue get-security-configuration --name "${SECURITY_CONFIGURATION}" $PROFILE_OPT --region $regx --output text --query 'SecurityConfiguration.EncryptionConfiguration.CloudWatchEncryption.CloudWatchEncryptionMode')
            if [[ "$CLOUDWATCH_ENCRYPTION" == "DISABLED" ]]; then
              textFail "$regx: Glue job $JOB_NAME does not have CloudWatch Logs encryption enabled" "$regx" "$JOB_NAME"
            else
              textPass "$regx: Glue job $JOB_NAME does have $CLOUDWATCH_ENCRYPTION CloudWatch Logs encryption enabled" "$regx" "$JOB_NAME"
            fi
          else
            textFail "$regx: Glue job $JOB_NAME does not have CloudWatch Logs encryption enabled" "$regx" "$JOB_NAME"
          fi
      done
    else
      textInfo "$regx: There are no Glue jobs" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7119 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7119="7.119"
CHECK_TITLE_extra7119="[extra7119] Check if Glue development endpoints have CloudWatch logs encryption enabled."
CHECK_SCORED_extra7119="NOT_SCORED"
CHECK_CIS_LEVEL_extra7119="EXTRA"
CHECK_SEVERITY_extra7119="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7119="AwsGlue"
CHECK_ALTERNATE_check7119="extra7119"
CHECK_SERVICENAME_extra7119="glue"
CHECK_RISK_extra7119='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra7119='Enable Encryption in the Security configurations.'
CHECK_DOC_extra7119='https://docs.aws.amazon.com/glue/latest/dg/console-security-configurations.html'
CHECK_CAF_EPIC_extra7119='Logging and Monitoring'

extra7119(){
  for regx in $REGIONS; do
    LIST_EP_SC=$($AWSCLI glue get-dev-endpoints $PROFILE_OPT --region $regx --query 'DevEndpoints[*].{Name:EndpointName,Security:SecurityConfiguration}' --output json 2>&1)
    if [[ $(echo "$LIST_EP_SC" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get dev endpoints" "$regx"
        continue
    fi
    if [[ $LIST_EP_SC != '[]' ]]; then
      for ep in $(echo "${LIST_EP_SC}"| jq -r '.[] | @base64');do
        ENDPOINT_NAME=$(echo $ep | base64 --decode | jq -r '.Name')
        ENDPOINT_SC=$(echo $ep | base64 --decode | jq -r '.Security // empty')
        if [[ ! -z "$ENDPOINT_SC" ]]; then
          ENDPOINT_SC_ENCRYPTION=$($AWSCLI glue get-security-configuration --name "${ENDPOINT_SC}" $PROFILE_OPT --region $regx --query 'SecurityConfiguration.EncryptionConfiguration.CloudWatchEncryption.CloudWatchEncryptionMode' --output text)
          if [[ $ENDPOINT_SC_ENCRYPTION == "DISABLED" ]]; then
            textFail "$regx: Glue development endpoint $ENDPOINT_NAME does not have CloudWatch logs encryption enabled!" "$regx" "$ENDPOINT_NAME"
          else
            textPass "$regx: Glue development endpoint $ENDPOINT_NAME has CloudWatch logs encryption enabled" "$regx" "$ENDPOINT_NAME"
          fi
        else
          textFail "$regx: Glue development endpoint $ENDPOINT_NAME does not have security configuration" "$regx" "$ENDPOINT_NAME"
        fi
      done
    else
      textInfo "$regx: There are no Glue development endpoints" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7118 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7118="7.118"
CHECK_TITLE_extra7118="[extra7118] Check if Glue ETL Jobs have S3 encryption enabled."
CHECK_SCORED_extra7118="NOT_SCORED"
CHECK_CIS_LEVEL_extra7118="EXTRA"
CHECK_SEVERITY_extra7118="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7118="AwsGlue"
CHECK_ALTERNATE_check7118="extra7118"
CHECK_SERVICENAME_extra7118="glue"
CHECK_RISK_extra7118='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra7118='Provide the encryption properties that are used by crawlers; jobs; and development endpoints.'
CHECK_DOC_extra7118='https://docs.aws.amazon.com/glue/latest/dg/console-security-configurations.html'
CHECK_CAF_EPIC_extra7118='Data Protection'

extra7118(){
  for regx in $REGIONS; do
    JOB_LIST=$($AWSCLI glue get-jobs $PROFILE_OPT --region $regx --output json --query 'Jobs[*].{Name:Name,SecurityConfiguration:SecurityConfiguration,JobEncryption:DefaultArguments."--encryption-type"}' 2>&1)
    if [[ $(echo "$JOB_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get jobs" "$regx"
        continue
    fi
    if [[ $JOB_LIST != '[]' ]]; then
      for job in $(echo "${JOB_LIST}" | jq -r '.[] | @base64'); do
          JOB_NAME=$(echo $job | base64 --decode | jq -r '.Name')
          SECURITY_CONFIGURATION=$(echo $job | base64 --decode | jq -r '.SecurityConfiguration // empty')
          JOB_ENCRYPTION=$(echo $job | base64 --decode | jq -r '.JobEncryption // empty')
          if [[ ! -z "$SECURITY_CONFIGURATION" ]]; then
            S3_ENCRYPTION=$($AWSCLI glue get-security-configuration --name "${SECURITY_CONFIGURATION}" $PROFILE_OPT --region $regx --output text --query 'SecurityConfiguration.EncryptionConfiguration.S3Encryption[0].S3EncryptionMode')
            if [[ "$S3_ENCRYPTION" == "DISABLED" ]]; then
              if [[ ! -z "$JOB_ENCRYPTION" ]]; then
                textPass "$regx: Glue job $JOB_NAME does have $JOB_ENCRYPTION for S3 encryption enabled" "$regx" "$JOB_NAME"
              else
                textFail "$regx: Glue job $JOB_NAME does not have S3 encryption enabled" "$regx" "$JOB_NAME"
              fi
            else
              textPass "$regx: Glue job $JOB_NAME does have $S3_ENCRYPTION for S3 encryption enabled" "$regx" "$JOB_NAME"
            fi
          elif [[ ! -z "$JOB_ENCRYPTION" ]]; then
            textPass "$regx: Glue job $JOB_NAME does have $JOB_ENCRYPTION for S3 encryption enabled" "$regx" "$JOB_NAME"
          else
            textFail "$regx: Glue job $JOB_NAME does not have S3 encryption enabled" "$regx" "$JOB_NAME"
          fi
      done
    else
      textInfo "$regx: There are no Glue jobs" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7117 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7117="7.117"
CHECK_TITLE_extra7117="[extra7117] Check if Glue data catalog settings have encrypt connection password enabled."
CHECK_SCORED_extra7117="NOT_SCORED"
CHECK_CIS_LEVEL_extra7117="EXTRA"
CHECK_SEVERITY_extra7117="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7117="AwsGlue"
CHECK_ALTERNATE_check7117="extra7117"
CHECK_SERVICENAME_extra7117="glue"
CHECK_RISK_extra7117='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra7117='On the AWS Glue console; you can enable this option on the Data catalog settings page.'
CHECK_DOC_extra7117='https://docs.aws.amazon.com/glue/latest/dg/encrypt-connection-passwords.html'
CHECK_CAF_EPIC_extra7117='Data Protection'

extra7117(){
  for regx in $REGIONS; do
    CONNECTION_LIST=$($AWSCLI glue get-connections $PROFILE_OPT --region $regx  --output text --query 'ConnectionList[*]' 2>&1)
    if [[ $(echo "$CONNECTION_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get connections" "$regx"
        continue
    fi
    if [[ ! -z $CONNECTION_LIST ]]; then
      METADATA_ENCRYPTED=$($AWSCLI glue get-data-catalog-encryption-settings $PROFILE_OPT --region $regx --output text --query "DataCatalogEncryptionSettings.ConnectionPasswordEncryption.ReturnConnectionPasswordEncrypted")
      if [[ "$METADATA_ENCRYPTED" == "False" ]]; then
        textFail "$regx: Glue data catalog connection password is not encrypted" "$regx"
      else
        textPass "$regx: Glue data catalog connection password is encrypted" "$regx"
      fi
    else
      textInfo "$regx: Glue data catalog connection password encryption does not apply since there are no connections" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7116 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7116="7.116"
CHECK_TITLE_extra7116="[extra7116] Check if Glue data catalog settings have metadata encryption enabled."
CHECK_SCORED_extra7116="NOT_SCORED"
CHECK_CIS_LEVEL_extra7116="EXTRA"
CHECK_SEVERITY_extra7116="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7116="AwsGlue"
CHECK_ALTERNATE_check7116="extra7116"
CHECK_SERVICENAME_extra7116="glue"
CHECK_RISK_extra7116='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra7116='Enable Encryption. Use a CMK where possible. It will provide additional management and privacy benefits.'
CHECK_DOC_extra7116='https://docs.aws.amazon.com/glue/latest/dg/encrypt-glue-data-catalog.html'
CHECK_CAF_EPIC_extra7116='Data Protection'

extra7116(){
  for regx in $REGIONS; do
    TABLE_LIST=$($AWSCLI glue search-tables --max-results 1 $PROFILE_OPT --region $regx  --output text --query 'TableList[*]' 2>&1)
    if [[ $(echo "$TABLE_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to search tables" "$regx"
        continue
    fi
    if [[ ! -z $TABLE_LIST ]]; then
      METADATA_ENCRYPTED=$($AWSCLI glue get-data-catalog-encryption-settings $PROFILE_OPT --region $regx --output text --query "DataCatalogEncryptionSettings.EncryptionAtRest.CatalogEncryptionMode")
      if [[ "$METADATA_ENCRYPTED" == "DISABLED" ]]; then
        textFail "$regx: Glue data catalog settings have metadata encryption disabled" "$regx"
      else
        textPass "$regx: Glue data catalog settings have metadata encryption enabled" "$regx"
      fi
    else
      textInfo "$regx: Glue data catalog settings metadata encryption does not apply since there are no tables" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7115 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7115="7.115"
CHECK_TITLE_extra7115="[extra7115] Check if Glue database connection has SSL connection enabled."
CHECK_SCORED_extra7115="NOT_SCORED"
CHECK_CIS_LEVEL_extra7115="EXTRA"
CHECK_SEVERITY_extra7115="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7115="AwsGlue"
CHECK_ALTERNATE_check7115="extra7115"
CHECK_SERVICENAME_extra7115="glue"
CHECK_RISK_extra7115='Data exfiltration could happen if information is not protected in transit.'
CHECK_REMEDIATION_extra7115='Configure encryption settings for crawlers; ETL jobs; and development endpoints using security configurations in AWS Glue.'
CHECK_DOC_extra7115='https://docs.aws.amazon.com/glue/latest/dg/encryption-in-transit.html'
CHECK_CAF_EPIC_extra7115='Data Protection'

extra7115(){
  for regx in $REGIONS; do
    CONNECTION_LIST=$($AWSCLI glue get-connections $PROFILE_OPT --region $regx  --output json --query 'ConnectionList[*].{Name:Name,SSL:ConnectionProperties.JDBC_ENFORCE_SSL}' 2>&1)
    if [[ $(echo "$CONNECTION_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get connections" "$regx"
        continue
    fi
    if [[ $CONNECTION_LIST != '[]' ]]; then
      for connection in $(echo "${CONNECTION_LIST}" | jq -r '.[] | @base64'); do
          CONNECTION_NAME=$(echo $connection | base64 --decode   | jq -r '.Name' )
          CONNECTION_SSL_STATE=$(echo $connection | base64 --decode  | jq -r '.SSL')
          if [[ "$CONNECTION_SSL_STATE" == "false" ]]; then
              textFail "$regx: Glue connection $CONNECTION_NAME has SSL connection disabled"  "$regx" "$CONNECTION_NAME"
          else
              textPass "$regx: Glue connection $CONNECTION_NAME has SSL connection enabled"  "$regx" "$CONNECTION_NAME"
          fi
      done
    else
      textInfo "$regx: There are no Glue connections" "$regx"
    fi
  done
}

providers/aws/services/glue/check_extra7114 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7114="7.114"
CHECK_TITLE_extra7114="[extra7114] Check if Glue development endpoints have S3 encryption enabled."
CHECK_SCORED_extra7114="NOT_SCORED"
CHECK_CIS_LEVEL_extra7114="EXTRA"
CHECK_SEVERITY_extra7114="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7114="AwsGlue"
CHECK_ALTERNATE_check7114="extra7114"
CHECK_SERVICENAME_extra7114="glue"
CHECK_RISK_extra7114='Data exfiltration could happen if information is not protected. KMS keys provide additional security level to IAM policies.'
CHECK_REMEDIATION_extra7114='Specify AWS KMS keys to use for input and output from S3 and EBS.'
CHECK_DOC_extra7114='https://docs.aws.amazon.com/glue/latest/dg/encryption-security-configuration.html'
CHECK_CAF_EPIC_extra7114='Data Protection'

extra7114(){
  for regx in $REGIONS; do
    LIST_EP_SC=$($AWSCLI glue get-dev-endpoints $PROFILE_OPT --region $regx --query 'DevEndpoints[*].{Name:EndpointName,Security:SecurityConfiguration}' --output json 2>&1)
    if [[ $(echo "$LIST_EP_SC" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get dev endpoints" "$regx"
        continue
    fi
    if [[ $LIST_EP_SC != '[]' ]]; then
      for ep in $(echo "${LIST_EP_SC}"| jq -r '.[] | @base64');do
        ENDPOINT_NAME=$(echo $ep | base64 --decode | jq -r '.Name')
        ENDPOINT_SC=$(echo $ep | base64 --decode | jq -r '.Security // empty')
        if [[ ! -z "$ENDPOINT_SC" ]]; then
          ENDPOINT_SC_ENCRYPTION=$($AWSCLI glue get-security-configuration --name "${ENDPOINT_SC}" $PROFILE_OPT --region $regx --query 'SecurityConfiguration.EncryptionConfiguration.S3Encryption[0].S3EncryptionMode' --output text)
          if [[ "$ENDPOINT_SC_ENCRYPTION" == "DISABLED" ]]; then
            textFail "$regx: Glue development endpoint $ENDPOINT_NAME does not have S3 encryption enabled!" "$regx" "$ENDPOINT_NAME"
          else
            textPass "$regx: Glue development endpoint $ENDPOINT_NAME has S3 encryption enabled" "$regx" "$ENDPOINT_NAME"
          fi
        else
          textFail "$regx: Glue development endpoint $ENDPOINT_NAME does not have security configuration" "$regx" "$ENDPOINT_NAME"
        fi
      done
    else
      textInfo "$regx: There are no Glue development endpoints" "$regx"
    fi
  done
}

providers/aws/services/glacier/check_extra7147 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7147="7.147"
CHECK_TITLE_extra7147="[extra7147] Check if S3 Glacier vaults have policies which allow access to everyone"
CHECK_SCORED_extra7147="NOT_SCORED"
CHECK_CIS_LEVEL_extra7147="EXTRA"
CHECK_SEVERITY_extra7147="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra7147="AwsGlacierVault"
CHECK_ALTERNATE_check7147="extra7147"
CHECK_SERVICENAME_extra7147="glacier"
CHECK_RISK_extra7147='Vaults accessible to everyone could expose sensitive data to bad actors'
CHECK_REMEDIATION_extra7147='Ensure vault policy does not have principle as *'
CHECK_DOC_extra7147='https://docs.aws.amazon.com/amazonglacier/latest/dev/access-control-overview.html'
CHECK_CAF_EPIC_extra7147='Data Protection'

extra7147(){
  for regx in $REGIONS; do
  LIST_OF_VAULTS=$($AWSCLI glacier list-vaults ${PROFILE_OPT} --region "${regx}" --account-id "${ACCOUNT_NUM}" --query VaultList[*].VaultName --output text 2>&1|xargs -n1)
  if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "$LIST_OF_VAULTS"; then
      textInfo "$regx: Access Denied trying to list vaults" "${regx}"
      continue
  fi
  # Check for unsupported regions
  if grep -q -E 'error' <<< "${LIST_OF_VAULTS}"; then
      textInfo "$regx: An error occurred when calling the ListVaults operation - check if this region is supported" "${regx}"
      continue
  fi
  if [[ $LIST_OF_VAULTS ]]; then
    for vault in $LIST_OF_VAULTS;do
      VAULT_POLICY_STATEMENTS=$($AWSCLI glacier ${PROFILE_OPT} get-vault-access-policy --region "${regx}" --account-id "${ACCOUNT_NUM}" --vault-name "${vault}" --output json --query policy.Policy 2>&1)
      if [[ $VAULT_POLICY_STATEMENTS == *GetVaultAccessPolicy* ]]; then
        textInfo "${regx}: Vault $vault doesn't have any policy" "${regx}" "$vault"
      else
        VAULT_POLICY_BAD_STATEMENTS=$(jq '. | fromjson' <<< "${VAULT_POLICY_STATEMENTS}" | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
        if [[ $VAULT_POLICY_BAD_STATEMENTS != "" ]]; then
          textFail "${regx}: Vault $vault has policy which allows access to everyone" "${regx}" "$vault"
        else
          textPass "${regx}: Vault $vault has policy which does not allow access to everyone" "${regx}" "$vault"
        fi
      fi
    done
  else
    textInfo "${regx}: No Glacier vaults found" "${regx}"
  fi
 done
}

providers/aws/services/es/check_extra788 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra788="7.88"
CHECK_TITLE_extra788="[extra788] Check connection and authentication for Internet exposed Amazon Elasticsearch Service (ES) domains"
CHECK_SCORED_extra788="NOT_SCORED"
CHECK_CIS_LEVEL_extra788="EXTRA"
CHECK_SEVERITY_extra788="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra788="AwsElasticsearchDomain"
CHECK_ALTERNATE_check788="extra788"
CHECK_SERVICENAME_extra788="es"
CHECK_RISK_extra788='Internet exposed services increases the risk of unauthorised.'
CHECK_REMEDIATION_extra788='Placing an Amazon ES domain within a VPC enables secure communication between Amazon ES and other services within the VPC without the need for an internet gateway; NAT device; or VPN connection. All traffic remains securely within the AWS Cloud.'
CHECK_DOC_extra788='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-vpc.html'
CHECK_CAF_EPIC_extra788='Infrastructure Security'

extra788(){
  # Prowler will try to access each ElasticSearch server to the public URI endpoint.
  # That is from the host where Prowler is running and will try to read indices or get kibana status

  # "Check if Elasticsearch Service domains allow open access "
  for regx in $REGIONS; do
    LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
      textInfo "$regx: Access Denied trying to list domain names" "$regx"
      continue
    fi
    if [[ $LIST_OF_DOMAINS ]]; then
      TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.policy.XXXXXXXXXX)
      for domain in $LIST_OF_DOMAINS;do
        # get endpoint or vpc endpoints
        ES_DOMAIN_INFO=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.[Endpoint, Endpoints.vpc, VPCOptions.VPCId]' --output text 2>&1)
        if [[ $(echo "$ES_DOMAIN_INFO" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to get domain $domain" "$regx"
          continue
        fi
        read ES_DOMAIN_ENDPOINT ES_DOMAIN_ENDPOINT_VPC ES_DOMAIN_VPC <<< "$ES_DOMAIN_INFO" &&
        # If the endpoint starts with "vpc-" it is in a VPC then it is fine.
        if [[ "${ES_DOMAIN_ENDPOINT_VPC:0:3}" == "vpc" ]]; then
          textInfo "$regx: Amazon ES domain $domain is in VPC $ES_DOMAIN_VPC run extra779 to make sure it is not exposed using custom proxy" "$regx"
        else
          $AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainConfig.AccessPolicies.Options' --output text > $TEMP_POLICY_FILE 2>&1
          if [[ $(grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' $TEMP_POLICY_FILE) ]]; then
            textInfo "$regx: Access Denied trying to get domain config for $domain" "$regx"
            continue
          fi
          CHECK_ES_DOMAIN_POLICY_OPEN=$(cat $TEMP_POLICY_FILE | jq -r '. | .Statement[] | select(.Effect == "Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and select(has("Condition") | not))')
          CHECK_ES_DOMAIN_POLICY_HAS_CONDITION=$(cat $TEMP_POLICY_FILE | jq -r '. | .Statement[] | select(.Effect == "Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and select(has("Condition")))' )
          if [[ $CHECK_ES_DOMAIN_POLICY_HAS_CONDITION ]]; then
            # get content of IpAddress."aws:SourceIp" and get a clean list
            LIST_CONDITION_IPS=$(cat $TEMP_POLICY_FILE | jq '.Statement[0] .Condition.IpAddress."aws:SourceIp"'| awk -F'"' '{print $2}' | tr -d '",^$' | sed '/^$/d')
            unset CONDITION_HAS_PUBLIC_IP_ARRAY
            for condition_ip in "${LIST_CONDITION_IPS}";do
              CONDITION_HAS_PRIVATE_IP=$(echo "${condition_ip}" | grep -E '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.)')
              if [[ $CONDITION_HAS_PRIVATE_IP ]];then
                CONDITION_HAS_PRIVATE_IP_ARRAY+=($condition_ip)
              fi
              CONDITION_HAS_PUBLIC_IP=$(echo "${condition_ip}" | grep -vE '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|0\.0\.0\.0|\*)')
              if [[ $CONDITION_HAS_PUBLIC_IP ]];then
                CONDITION_HAS_PUBLIC_IP_ARRAY+=($condition_ip)
              fi
              CONDITION_HAS_ZERO_NET=$(echo "${condition_ip}" | grep -E '^(0\.0\.0\.0)')
              CONDITION_HAS_STAR=$(echo "${condition_ip}" | grep -E '^\*')
            done
            CHECK_ES_DOMAIN_POLICY_CONDITION_PRIVATE_IP=${CONDITION_HAS_PRIVATE_IP_ARRAY[@]}
            CHECK_ES_DOMAIN_POLICY_CONDITION_PUBLIC_IP=${CONDITION_HAS_PUBLIC_IP_ARRAY[@]}
            CHECK_ES_DOMAIN_POLICY_CONDITION_ZERO=$CONDITION_HAS_ZERO_NET
            CHECK_ES_DOMAIN_POLICY_CONDITION_STAR=$CONDITION_HAS_STAR
          fi
          if [[ $CHECK_ES_DOMAIN_POLICY_OPEN || $CHECK_ES_DOMAIN_POLICY_CONDITION_ZERO || $CHECK_ES_DOMAIN_POLICY_CONDITION_STAR || ${CHECK_ES_DOMAIN_POLICY_CONDITION_PUBLIC_IP[@]} ]];then
            #Prowler will check to read indices or kibaba status if no conditions, condition IP is *, 0.0.0.0/0, 0.0.0.0/8 or any public IP.
            # check for REST API on port 443
            CHECH_ES_HTTPS=$(curl -m 2 -s -w "%{http_code}" -o /dev/null -X GET "https://$ES_DOMAIN_ENDPOINT/_cat/indices")
            httpStatus $CHECH_ES_HTTPS
            if [[ $CHECH_ES_HTTPS -eq "200" ]];then
              textFail "$regx: Amazon ES domain $domain policy allows Anonymous access and ES service endpoint $ES_DOMAIN_ENDPOINT responded $SERVER_RESPONSE" "$regx"
            else
              textInfo "$regx: Amazon ES domain $domain policy allows Anonymous access but ES service endpoint $ES_DOMAIN_ENDPOINT responded $SERVER_RESPONSE" "$regx"
            fi
            # check for Kibana on port 443
            CHECH_KIBANA_HTTPS=$(curl -m 2 -s -w "%{http_code}" -o /dev/null -X GET "https://$ES_DOMAIN_ENDPOINT/_plugin/kibana")
            httpStatus $CHECH_KIBANA_HTTPS
            if [[ $CHECH_KIBANA_HTTPS -eq "200" || $CHECH_KIBANA_HTTPS -eq "301" || $CHECH_KIBANA_HTTPS -eq "302" ]];then
              textFail "$regx: Amazon ES domain $domain policy allows Anonymous access and Kibana service endpoint $ES_DOMAIN_ENDPOINT responded $SERVER_RESPONSE" "$regx" "$domain"
            else
              textInfo "$regx: Amazon ES domain $domain policy allows Anonymous access but Kibana service endpoint $ES_DOMAIN_ENDPOINT responded $SERVER_RESPONSE" "$regx" "$domain"
            fi
          else
            if [[ $CHECK_ES_DOMAIN_POLICY_HAS_CONDITION && ${CHECK_ES_DOMAIN_POLICY_CONDITION_PRIVATE_IP[@]} ]];then
              textInfo "$regx: Amazon ES domain $domain policy allows access from a Private IP or CIDR RFC1918 $(echo ${CONDITION_HAS_PRIVATE_IP_ARRAY[@]})" "$regx"
            else
              textPass "$regx: Amazon ES domain $domain does not allow Anonymous cross account access" "$regx" "$domain"
            fi
          fi
        fi
      done
      [[ -f "${TEMP_POLICY_FILE}" ]] && rm -f $TEMP_POLICY_FILE
    else
      textInfo "$regx: No Amazon ES domain found" "$regx"
    fi
  done
}

providers/aws/services/es/check_extra787 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra787="7.87"
CHECK_TITLE_extra787="[extra787] Check connection and authentication for Internet exposed Elasticsearch/Kibana ports"
CHECK_SCORED_extra787="NOT_SCORED"
CHECK_CIS_LEVEL_extra787="EXTRA"
CHECK_SEVERITY_extra787="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra787="AwsEc2Instance"
CHECK_ALTERNATE_check787="extra787"
CHECK_SERVICENAME_extra787="es"
CHECK_RISK_extra787='Internet exposed services increases the risk of unauthorised.'
CHECK_REMEDIATION_extra787='Placing an Amazon ES domain within a VPC enables secure communication between Amazon ES and other services within the VPC without the need for an internet gateway; NAT device; or VPN connection. All traffic remains securely within the AWS Cloud.'
CHECK_DOC_extra787='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-vpc.html'
CHECK_CAF_EPIC_extra787='Infrastructure Security'

extra787(){
  # Prowler will try to access each ElasticSearch server to port:
  # 9200 API, 9300 Communcation and 5601 Kibana to figure out if authentication is enabled.
  # That is from the host where Prowler is running and will try to read indices or get kibana status
  ES_API_PORT="9200"
  ES_DATA_PORT="9300"
  ES_KIBANA_PORT="5601"

  for regx in $REGIONS; do
    # create a list of SG open to the world with port $ES_API_PORT or $ES_DATA_PORT or $ES_KIBANA_PORT
    SG_LIST=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --output text \
      --query "SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=\`$ES_API_PORT\` && ToPort>=\`$ES_API_PORT\`) || (FromPort<=\`$ES_DATA_PORT\` && ToPort>=\`$ES_DATA_PORT\`) || (FromPort<=\`$ES_KIBANA_PORT\` && ToPort>=\`$ES_KIBANA_PORT\`)) && (contains(IpRanges[].CidrIp, \`0.0.0.0/0\`) || contains(Ipv6Ranges[].CidrIpv6, \`::/0\`))]) > \`0\`].{GroupId:GroupId}" 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    # in case of open security groups goes through each one
    if [[ $SG_LIST ]];then
      for sg in $SG_LIST;do
        # temp file store the list of instances IDs and public IP address if found
        TEMP_EXTRA787_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.EXTRA787.XXXXXXXXXX)
        # finds instances with that open security group attached and get its public ip address (if it has one)
        $AWSCLI $PROFILE_OPT --region $regx ec2 describe-instances --filters Name=instance.group-id,Values=$sg --query 'Reservations[*].Instances[*].[InstanceId,PublicIpAddress]' --output text > $TEMP_EXTRA787_FILE
        # in case of exposed instances it does access checks
        if [[ -s "$TEMP_EXTRA787_FILE" ]];then
          while read instance eip ; do
            if [[ "$eip" != "None" ]];then
              # check for Elasticsearch on port $ES_API_PORT, rest API HTTP.
              CHECH_HTTP_ES_API=$(curl -m 2 -s -w "%{http_code}" -o /dev/null -X GET "http://$eip:$ES_API_PORT/_cat/indices")
              httpStatus $CHECH_HTTP_ES_API
              if [[ $CHECH_HTTP_ES_API -eq "200" ]];then
                textFail "$regx: Found instance $instance with public IP $eip on Security Group: $sg with Elasticsearch port $ES_API_PORT response $SERVER_RESPONSE" "$regx" "$instance"
              else
                textInfo "$regx: Found instance $instance with public IP $eip on Security Group: $sg with Elasticsearch port $ES_API_PORT response $SERVER_RESPONSE" "$regx" "$instance"
              fi
              # check for port $ES_DATA_PORT TCP, this is the communication port, not:
              # test_tcp_connectivity is in include/os_detector
              # syntax is 'test_tcp_connectivity $HOST $PORT $TIMEOUT' (in seconds)
              CHECH_HTTP_ES_DATA=$(test_tcp_connectivity $eip $ES_DATA_PORT 2)
              # Using HTTP error codes here as well to reuse httpStatus function
              # codes for better handling, so 200 is open and 000 is not responding
              httpStatus $CHECH_HTTP_ES_DATA
              if [[ $CHECH_HTTP_ES_DATA -eq "200" ]];then
                textFail "$regx: Found instance $instance with public IP $eip on Security Group: $sg with Elasticsearch port $ES_DATA_PORT response $SERVER_RESPONSE" "$regx" "$instance"
              else
                textInfo "$regx: Found instance $instance with public IP $eip on Security Group: $sg with Elasticsearch port $ES_DATA_PORT response $SERVER_RESPONSE" "$regx" "$instance"
              fi
              # check for Kibana on port $ES_KIBANA_PORT
              CHECH_HTTP_ES_KIBANA=$(curl -m 2 -s -w "%{http_code}" -o /dev/null -X GET "http://$eip:$ES_KIBANA_PORT/api/status")
              httpStatus $CHECH_HTTP_ES_KIBANA
              if [[ $CHECH_AUTH_5601 -eq "200" ]];then
                textFail "$regx: Found instance $instance with public IP $eip on Security Group: $sg with Kibana on port $ES_KIBANA_PORT response $SERVER_RESPONSE" "$regx" "$instance"
              else
                textInfo "$regx: Found instance $instance with public IP $eip on Security Group: $sg with Kibana on port $ES_KIBANA_PORT response $SERVER_RESPONSE" "$regx" "$instance"
              fi
            else
              textInfo "$regx: Found instance $instance with private IP on Security Group: $sg" "$regx"
            fi
          done < <(cat $TEMP_EXTRA787_FILE)
        fi
        rm -rf $TEMP_EXTRA787_FILE
      done
    else
      textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Elasticsearch/Kibana ports" "$regx"
    fi
  done
}

providers/aws/services/es/check_extra785 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra785="7.85"
CHECK_TITLE_extra785="[extra785] Check if Amazon Elasticsearch Service (ES) domains have updates available"
CHECK_SCORED_extra785="NOT_SCORED"
CHECK_CIS_LEVEL_extra785="EXTRA"
CHECK_SEVERITY_extra785="Low"
CHECK_ASFF_RESOURCE_TYPE_extra785="AwsElasticsearchDomain"
CHECK_ALTERNATE_check785="extra785"
CHECK_SERVICENAME_extra785="es"
CHECK_RISK_extra785='Amazon ES regularly releases system software updates that add features or otherwise improve your domains.'
CHECK_REMEDIATION_extra785='The Notifications panel in the console is the easiest way to see if an update is available or check the status of an update. You can also receive these notifications through Amazon EventBridge. If you take no action on required updates; Amazon ES still updates your domain service software automatically after a certain timeframe (typically two weeks). In this situation; Amazon ES sends notifications when it starts the update and when the update is complete.'
CHECK_DOC_extra785='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-service-software.html'
CHECK_CAF_EPIC_extra785='Infrastructure Security'

# NOTE!
# API does not properly shows if an update is available while it is a new version available
# that can be done using the Console but not the API, not sure if it is a bug
# I have to investigate further

extra785(){
  for regx in ${REGIONS}; do
    LIST_OF_DOMAINS=$("${AWSCLI}" es list-domain-names ${PROFILE_OPT} --region "${regx}" --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "${LIST_OF_DOMAINS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "${regx}: Access Denied trying to list domain names" "${regx}"
        continue
    fi
    if [[ "${LIST_OF_DOMAINS}" ]]; then
      for domain in ${LIST_OF_DOMAINS}; do
        CHECK_IF_UPDATE_AVAILABLE_AND_VERSION=$("${AWSCLI}" es describe-elasticsearch-domain --domain-name "${domain}" ${PROFILE_OPT} --region "${regx}" --query 'DomainStatus.[ServiceSoftwareOptions.UpdateAvailable,ElasticsearchVersion]' --output text 2>&1)
        if [[ $(echo "${CHECK_IF_UPDATE_AVAILABLE_AND_VERSION}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "${regx}: Access Denied trying to get ES domain ${domain}" "${regx}"
            continue
        fi
        read -r update_status es_version <<< "${CHECK_IF_UPDATE_AVAILABLE_AND_VERSION}" &&
        if [[ $(tr '[:upper:]' '[:lower:]' <<< "${update_status}") != "false" ]]; then
          textInfo "${regx}: Amazon ES domain ${domain} v${es_version} has updates available" "${regx}" "${domain}"
        else
          textPass "${regx}: Amazon ES domain ${domain} v${es_version} does not have have updates available" "${regx}" "${domain}"
        fi
      done
    else
      textInfo "${regx}: No Amazon ES domain found" "${regx}"
    fi
  done
}

providers/aws/services/es/check_extra784 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra784="7.84"
CHECK_TITLE_extra784="[extra784] Check if Amazon Elasticsearch Service (ES) domains internal user database enabled"
CHECK_SCORED_extra784="NOT_SCORED"
CHECK_CIS_LEVEL_extra784="EXTRA"
CHECK_SEVERITY_extra784="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra784="AwsElasticsearchDomain"
CHECK_ALTERNATE_check784="extra784"
CHECK_SERVICENAME_extra784="es"
CHECK_RISK_extra784='Internal User Database is convenient for demos; for production environment use Federated authentication.'
CHECK_REMEDIATION_extra784='Remove users from internal user database and uso Cognito instead.'
CHECK_DOC_extra784='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/fgac.html'
CHECK_CAF_EPIC_extra784='IAM'

extra784(){
  for regx in $REGIONS; do
    LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list domain names" "$regx"
        continue
    fi
    if [[ $LIST_OF_DOMAINS ]]; then
      for domain in $LIST_OF_DOMAINS;do
        CHECK_IF_INTERNALDB_ENABLED=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.AdvancedSecurityOptions.InternalUserDatabaseEnabled' --output text 2>&1)
        if [[ $(echo "$CHECK_IF_INTERNALDB_ENABLED" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to get ES domain $domain" "$regx"
            continue
        fi
        if [[ $(echo "$CHECK_IF_INTERNALDB_ENABLED" | grep -i true) ]];then
          textFail "$regx: Amazon ES domain $domain has internal user database enabled" "$regx" "$domain"
        else
          textPass "$regx: Amazon ES domain $domain does not have internal user database enabled" "$regx" "$domain"
        fi
      done
    else
      textInfo "$regx: No Amazon ES domain found" "$regx"
    fi
  done
}

providers/aws/services/es/check_extra783 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra783="7.83"
CHECK_TITLE_extra783="[extra783] Check if Amazon Elasticsearch Service (ES) domains has enforce HTTPS enabled"
CHECK_SCORED_extra783="NOT_SCORED"
CHECK_CIS_LEVEL_extra783="EXTRA"
CHECK_SEVERITY_extra783="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra783="AwsElasticsearchDomain"
CHECK_ALTERNATE_check783="extra783"
CHECK_SERVICENAME_extra783="es"
CHECK_RISK_extra783='If not enable unauthorized access to your data could risk increases.'
CHECK_REMEDIATION_extra783='When creating ES Domains; enable "Require HTTPS fo all traffic to the domain".'
CHECK_DOC_extra783='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-createupdatedomains.html'
CHECK_CAF_EPIC_extra783='Data Protection'

extra783(){
  for regx in $REGIONS; do
    LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list domain names" "$regx"
        continue
    fi
    if [[ $LIST_OF_DOMAINS ]]; then
      for domain in $LIST_OF_DOMAINS;do
        CHECK_IF_ENFORCEHTTPS_ENABLED=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.DomainEndpointOptions.EnforceHTTPS' --output text 2>&1)
        if [[ $(echo "$CHECK_IF_ENFORCEHTTPS_ENABLED" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to get ES domain $domain" "$regx"
            continue
        fi
        if [[ $(echo "$CHECK_IF_ENFORCEHTTPS_ENABLED" | grep -i true) ]];then
          textPass "$regx: Amazon ES domain $domain has enforce HTTPS enabled" "$regx" "$domain"
        else
          textFail "$regx: Amazon ES domain $domain does not have enforce HTTPS enabled" "$regx" "$domain"
        fi
      done
    else
      textInfo "$regx: No Amazon ES domain found" "$regx"
    fi
  done
}

providers/aws/services/es/check_extra782 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra782="7.82"
CHECK_TITLE_extra782="[extra782] Check if Amazon Elasticsearch Service (ES) domains has node-to-node encryption enabled"
CHECK_SCORED_extra782="NOT_SCORED"
CHECK_CIS_LEVEL_extra782="EXTRA"
CHECK_SEVERITY_extra782="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra782="AwsElasticsearchDomain"
CHECK_ALTERNATE_check782="extra782"
CHECK_SERVICENAME_extra782="es"
CHECK_RISK_extra782='Node-to-node encryption provides an additional layer of security on top of the default features of Amazon ES. This architecture prevents potential attackers from intercepting traffic between Elasticsearch nodes and keeps the cluster secure.'
CHECK_REMEDIATION_extra782='Node-to-node encryption on new domains requires Elasticsearch 6.0 or later. Enabling the feature on existing domains requires Elasticsearch 6.7 or later. Choose the existing domain in the AWS console; Actions; and Modify encryption.'
CHECK_DOC_extra782='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/ntn.html'
CHECK_CAF_EPIC_extra782='Data Protection'

extra782(){
  for regx in $REGIONS; do
    LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list domain names" "$regx"
        continue
    fi
    if [[ $LIST_OF_DOMAINS ]]; then
      for domain in $LIST_OF_DOMAINS;do
        CHECK_IF_NODETOENCR_ENABLED=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.NodeToNodeEncryptionOptions.Enabled' --output text 2>&1)
        if [[ $(echo "$CHECK_IF_NODETOENCR_ENABLED" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to get ES domain $domain" "$regx"
            continue
        fi
        if [[ $(echo "$CHECK_IF_NODETOENCR_ENABLED" | grep -i true) ]];then
          textPass "$regx: Amazon ES domain $domain has node-to-node encryption enabled" "$regx" "$domain"
        else
          textFail "$regx: Amazon ES domain $domain does not have node-to-node encryption enabled" "$regx" "$domain"
        fi
      done
    else
      textInfo "$regx: No Amazon ES domain found" "$regx"
    fi
  done
}

providers/aws/services/es/check_extra781 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra781="7.81"
CHECK_TITLE_extra781="[extra781] Check if Amazon Elasticsearch Service (ES) domains has encryption at-rest enabled"
CHECK_SCORED_extra781="NOT_SCORED"
CHECK_CIS_LEVEL_extra781="EXTRA"
CHECK_SEVERITY_extra781="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra781="AwsElasticsearchDomain"
CHECK_ALTERNATE_check781="extra781"
CHECK_ASFF_COMPLIANCE_TYPE_extra781="ens-mp.info.3.aws.au.1"
CHECK_SERVICENAME_extra781="es"
CHECK_RISK_extra781='If not enable unauthorized access to your data could risk increases.'
CHECK_REMEDIATION_extra781='Enable encryption at rest using AWS KMS to store and manage your encryption keys and the Advanced Encryption Standard algorithm with 256-bit keys (AES-256) to perform the encryption.'
CHECK_DOC_extra781='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/encryption-at-rest.html'
CHECK_CAF_EPIC_extra781='Data Protection'

extra781(){
  for regx in $REGIONS; do
    LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list domain names" "$regx"
        continue
    fi
    if [[ $LIST_OF_DOMAINS ]]; then
      for domain in $LIST_OF_DOMAINS;do
        CHECK_IF_ENCREST_ENABLED=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.EncryptionAtRestOptions.Enabled' --output text 2>&1)
        if [[ $(echo "$CHECK_IF_ENCREST_ENABLED" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to get ES domain $domain" "$regx"
            continue
        fi
        if [[ $(echo "$CHECK_IF_ENCREST_ENABLED" | grep -i true) ]];then
          textPass "$regx: Amazon ES domain $domain has encryption at-rest enabled" "$regx" "$domain"
        else
          textFail "$regx: Amazon ES domain $domain does not have encryption at-rest enabled" "$regx" "$domain"
        fi
      done
    else
      textInfo "$regx: No Amazon ES domain found" "$regx"
    fi
  done
}

providers/aws/services/es/check_extra780 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra780="7.80"
CHECK_TITLE_extra780="[extra780] Check if Amazon Elasticsearch Service (ES) domains has Amazon Cognito authentication for Kibana enabled"
CHECK_SCORED_extra780="NOT_SCORED"
CHECK_CIS_LEVEL_extra780="EXTRA"
CHECK_SEVERITY_extra780="High"
CHECK_ASFF_RESOURCE_TYPE_extra780="AwsElasticsearchDomain"
CHECK_ALTERNATE_check780="extra780"
CHECK_SERVICENAME_extra780="es"
CHECK_RISK_extra780='Amazon Elasticsearch Service supports Amazon Cognito for Kibana authentication. '
CHECK_REMEDIATION_extra780='If you do not configure Amazon Cognito authentication; you can still protect Kibana using an IP-based access policy and a proxy server; HTTP basic authentication; or SAML.'
CHECK_DOC_extra780='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-ac.html'
CHECK_CAF_EPIC_extra780='IAM'

extra780(){
  for regx in ${REGIONS}; do
    LIST_OF_DOMAINS=$("${AWSCLI}" es list-domain-names ${PROFILE_OPT} --region "${regx}" --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "${LIST_OF_DOMAINS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "${regx}: Access Denied trying to list domain names" "${regx}"
        continue
    fi
    if [[ "${LIST_OF_DOMAINS}" ]]; then
      for domain in ${LIST_OF_DOMAINS}; do
        CHECK_IF_COGNITO_ENABLED=$("${AWSCLI}" es describe-elasticsearch-domain --domain-name "${domain}" ${PROFILE_OPT} --region "${regx}" --query 'DomainStatus.CognitoOptions.Enabled' --output text 2>&1)
        if [[ $(echo "${CHECK_IF_COGNITO_ENABLED}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "${regx}: Access Denied trying to get ES domain ${domain}" "${regx}"
            continue
        fi
        if [[ $(tr '[:upper:]' '[:lower:]' <<< "${CHECK_IF_COGNITO_ENABLED}") == "true" ]]; then
          textPass "${regx}: Amazon ES domain ${domain} has Amazon Cognito authentication for Kibana enabled" "${regx}" "${domain}"
        else
          textFail "${regx}: Amazon ES domain ${domain} does not have Amazon Cognito authentication for Kibana enabled" "${regx}" "${domain}"
        fi
      done
    else
      textInfo "${regx}: No Amazon ES domain found" "${regx}"
    fi
  done
}

providers/aws/services/es/check_extra716 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra716="7.16"
CHECK_TITLE_extra716="[extra716] Check if Amazon Elasticsearch Service (ES) domains are set as Public or if it has open policy access"
CHECK_SCORED_extra716="NOT_SCORED"
CHECK_CIS_LEVEL_extra716="EXTRA"
CHECK_SEVERITY_extra716="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra716="AwsElasticsearchDomain"
CHECK_ALTERNATE_check716="extra716"
CHECK_SERVICENAME_extra716="es"
CHECK_RISK_extra716='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra716='Use  VPC endpoints for internal services.'
CHECK_DOC_extra716='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-vpc.html'
CHECK_CAF_EPIC_extra716='Infrastructure Security'

extra716(){
  for regx in $REGIONS; do
    LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to list domain names" "$regx"
        continue
    fi
    if [[ $LIST_OF_DOMAINS ]]; then
      TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.policy.XXXXXXXXXX)
      for domain in $LIST_OF_DOMAINS;do
        # get endpoint or vpc endpoints
        ES_DOMAIN_INFO=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.[Endpoints.vpc, VPCOptions.VPCId]' --output text 2>&1)
        if [[ $(echo "$ES_DOMAIN_INFO" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to get domain $domain" "$regx"
          continue
        fi
        read ES_DOMAIN_ENDPOINT_VPC ES_DOMAIN_VPC <<< "$ES_DOMAIN_INFO" &&
        # If the endpoint starts with "vpc-" it is in a VPC then it is fine.
        if [[ "${ES_DOMAIN_ENDPOINT_VPC:0:3}" == "vpc" ]]; then
          textInfo "$regx: Amazon ES domain $domain is in VPC $ES_DOMAIN_VPC run extra779 to make sure it is not exposed using custom proxy" "$regx" "$domain"
        else
          $AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainConfig.AccessPolicies.Options' --output text > $TEMP_POLICY_FILE 2>&1
          if [[ $(grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' $TEMP_POLICY_FILE) ]]; then
            textInfo "$regx: Access Denied trying to get domain config for $domain" "$regx"
            continue
          fi
          # check if the policy has a principal set up
          CHECK_ES_POLICY_PRINCIPAL=$(cat $TEMP_POLICY_FILE | jq -r '. | .Statement[] | select(.Effect == "Allow" and (((.Principal|type == "object") and .Principal.AWS != "*") or ((.Principal|type == "string") and .Principal != "*")) and select(has("Condition") | not))')
          if [[ $CHECK_ES_POLICY_PRINCIPAL ]]; then
            textPass "$regx: Amazon ES domain $domain does have a Principal set up" "$regx" "$domain"
          fi
          CHECK_ES_DOMAIN_POLICY_OPEN=$(cat $TEMP_POLICY_FILE | jq -r '. | .Statement[] | select(.Effect == "Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and select(has("Condition") | not))')
          CHECK_ES_DOMAIN_POLICY_HAS_CONDITION=$(cat $TEMP_POLICY_FILE | jq -r '. | .Statement[] | select(.Effect == "Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and select(has("Condition")))' )
          if [[ $CHECK_ES_DOMAIN_POLICY_HAS_CONDITION ]]; then
            # get content of IpAddress."aws:SourceIp" and get a clean list
            LIST_CONDITION_IPS=$(cat $TEMP_POLICY_FILE | jq '.Statement[0] .Condition.IpAddress."aws:SourceIp"'| awk -F'"' '{print $2}' | tr -d '",^$' | sed '/^$/d')
            unset CONDITION_HAS_PUBLIC_IP_ARRAY
            for condition_ip in "${LIST_CONDITION_IPS}";do
              CONDITION_HAS_PRIVATE_IP=$(echo "${condition_ip}" | grep -E '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.)')
              if [[ $CONDITION_HAS_PRIVATE_IP ]];then
                CONDITION_HAS_PRIVATE_IP_ARRAY+=($condition_ip)
              fi
              CONDITION_HAS_PUBLIC_IP=$(echo "${condition_ip}" | grep -vE '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|0\.0\.0\.0|\*)')
              if [[ $CONDITION_HAS_PUBLIC_IP ]];then
                CONDITION_HAS_PUBLIC_IP_ARRAY+=($condition_ip)
              fi
              CONDITION_HAS_ZERO_NET=$(echo "${condition_ip}" | grep -E '^(0\.0\.0\.0)')
              CONDITION_HAS_STAR=$(echo "${condition_ip}" | grep -E '^\*')
            done
            CHECK_ES_DOMAIN_POLICY_CONDITION_PRIVATE_IP=${CONDITION_HAS_PRIVATE_IP_ARRAY[@]}
            CHECK_ES_DOMAIN_POLICY_CONDITION_PUBLIC_IP=${CONDITION_HAS_PUBLIC_IP_ARRAY[@]}
            CHECK_ES_DOMAIN_POLICY_CONDITION_ZERO=$CONDITION_HAS_ZERO_NET
            CHECK_ES_DOMAIN_POLICY_CONDITION_STAR=$CONDITION_HAS_STAR
          fi
          if [[ $CHECK_ES_DOMAIN_POLICY_OPEN || $CHECK_ES_DOMAIN_POLICY_CONDITION_ZERO || $CHECK_ES_DOMAIN_POLICY_CONDITION_STAR || ${CHECK_ES_DOMAIN_POLICY_CONDITION_PUBLIC_IP[@]} ]];then
            if [[ $CHECK_ES_DOMAIN_POLICY_OPEN ]];then
              textFail "$regx: Amazon ES domain $domain policy allows access (Principal: \"*\") - use extra788 to test AUTH" "$regx" "$domain"
            fi
            if [[ $CHECK_ES_DOMAIN_POLICY_HAS_CONDITION && $CHECK_ES_DOMAIN_POLICY_CONDITION_ZERO ]];then
              textFail "$regx: Amazon ES domain $domain policy allows access (Principal: \"*\" and network 0.0.0.0) - use extra788 to test AUTH" "$regx" "$domain"
            fi
            if [[ $CHECK_ES_DOMAIN_POLICY_HAS_CONDITION && $CHECK_ES_DOMAIN_POLICY_CONDITION_STAR ]];then
              textFail "$regx: Amazon ES domain $domain policy allows access (Principal: \"*\" and network \"*\") - use extra788 to test AUTH" "$regx" "$domain"
            fi
            if [[ $CHECK_ES_DOMAIN_POLICY_HAS_CONDITION && ${CHECK_ES_DOMAIN_POLICY_CONDITION_PUBLIC_IP[@]} ]];then
              textInfo "$regx: Amazon ES domain $domain policy allows access (Principal: \"*\" and Public IP or Network $(echo ${CONDITION_HAS_PUBLIC_IP_ARRAY[@]})) - use extra788 to test AUTH" "$regx" "$domain"
            fi
          else
            if [[ $CHECK_ES_DOMAIN_POLICY_HAS_CONDITION && ${CHECK_ES_DOMAIN_POLICY_CONDITION_PRIVATE_IP[@]} ]];then
              textInfo "$regx: Amazon ES domain $domain policy allows access from a Private IP or CIDR RFC1918 $(echo ${CONDITION_HAS_PRIVATE_IP_ARRAY[@]})" "$regx" "$domain"
            else
              textPass "$regx: Amazon ES domain $domain does not allow anonymous access" "$regx" "$domain"
            fi
          fi
        fi
      done
      [[ -f "${TEMP_POLICY_FILE}" ]] && rm -f $TEMP_POLICY_FILE
    else
      textInfo "$regx: No Amazon ES domain found" "$regx"
    fi
  done
}

providers/aws/services/es/check_extra715 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra715="7.15"
CHECK_TITLE_extra715="[extra715] Check if Amazon Elasticsearch Service (ES) domains have logging enabled"
CHECK_SCORED_extra715="NOT_SCORED"
CHECK_CIS_LEVEL_extra715="EXTRA"
CHECK_SEVERITY_extra715="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra715="AwsElasticsearchDomain"
CHECK_ALTERNATE_check715="extra715"
CHECK_SERVICENAME_extra715="es"
CHECK_RISK_extra715='Amazon ES exposes four Elasticsearch logs through Amazon CloudWatch Logs: error logs; search slow logs; index slow logs; and audit logs. '
CHECK_REMEDIATION_extra715='Enable Elasticsearch log. Create use cases for them. Using audit logs check for access denied events.'
CHECK_DOC_extra715='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-createdomain-configure-slow-logs.html'
CHECK_CAF_EPIC_extra715='Logging and Monitoring'

extra715(){
  for regx in ${REGIONS}; do
    LIST_OF_DOMAINS=$("${AWSCLI}" es list-domain-names ${PROFILE_OPT} --region "${regx}" --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "${LIST_OF_DOMAINS}" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "${regx}: Access Denied trying to list domain names" "${regx}"
        continue
    fi
    if [[ "${LIST_OF_DOMAINS}" ]]; then
      for domain in ${LIST_OF_DOMAINS}; do
        SLOWLOG_ENABLED=$("${AWSCLI}" es describe-elasticsearch-domain-config --domain-name "${domain}" ${PROFILE_OPT} --region "${regx}" --query 'DomainConfig.LogPublishingOptions.Options.[SEARCH_SLOW_LOGS.Enabled, INDEX_SLOW_LOGS.Enabled]' --output text 2>&1)
        if [[ $(echo "${SLOWLOG_ENABLED}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "${regx}: Access Denied trying to get ES domain config for ${domain}" "${regx}"
            continue
        fi
        read -r SEARCH_SLOWLOG_ENABLED INDEX_SLOWLOG_ENABLED <<< "${SLOWLOG_ENABLED}" && {
          if [[ $(tr '[:upper:]' '[:lower:]' <<< "${SEARCH_SLOWLOG_ENABLED}") == "true" ]]; then
            textPass "${regx}: Amazon ES domain ${domain} SEARCH_SLOW_LOGS enabled" "${regx}" "${domain}"
          else
            textFail "${regx}: Amazon ES domain ${domain} SEARCH_SLOW_LOGS disabled!" "${regx}" "${domain}"
          fi

          if [[ $(tr '[:upper:]' '[:lower:]' <<< "${INDEX_SLOWLOG_ENABLED}") == "true" ]]; then
            textPass "${regx}: Amazon ES domain ${domain} INDEX_SLOW_LOGS enabled" "${regx}" "${domain}"
          else
            textFail "${regx}: Amazon ES domain ${domain} INDEX_SLOW_LOGS disabled!" "${regx}" "${domain}"
          fi
        }
      done
    else
      textInfo "${regx}: No Amazon ES domain found" "${regx}"
    fi
  done
}

providers/aws/services/es/check_extra7101 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7101="7.101"
CHECK_TITLE_extra7101="[extra7101] Check if Amazon Elasticsearch Service (ES) domains have audit logging enabled"
CHECK_SCORED_extra7101="NOT_SCORED"
CHECK_CIS_LEVEL_extra7101="EXTRA"
CHECK_SEVERITY_extra7101="Low"
CHECK_ASFF_RESOURCE_TYPE_extra7101="AwsElasticsearchDomain"
CHECK_ALTERNATE_check7101="extra7101"
CHECK_SERVICENAME_extra7101="es"
CHECK_RISK_extra7101='If logs are not enabled; monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra7101='Make sure you are logging information about Amazon Elasticsearch Service operations.'
CHECK_DOC_extra7101='https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/audit-logs.html'
CHECK_CAF_EPIC_extra7101='Logging and Monitoring'

extra7101(){
  for regx in ${REGIONS}; do
    LIST_OF_DOMAINS=$("${AWSCLI}" es list-domain-names ${PROFILE_OPT} --region "${regx}" --query 'DomainNames[].DomainName' --output text 2>&1)
    if [[ $(echo "${LIST_OF_DOMAINS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "${regx}: Access Denied trying to list domain names" "${regx}"
        continue
    fi
    if [[ "${LIST_OF_DOMAINS}" ]]; then
      for domain in ${LIST_OF_DOMAINS}; do
        AUDIT_LOGS_ENABLED=$("${AWSCLI}" es describe-elasticsearch-domain-config --domain-name "${domain}" ${PROFILE_OPT} --region "${regx}" --query 'DomainConfig.LogPublishingOptions.Options.AUDIT_LOGS.Enabled' --output text 2>&1)
        if [[ $(echo "${AUDIT_LOGS_ENABLED}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "${regx}: Access Denied trying to get ES domain config for ${domain}" "${regx}"
            continue
        fi
        if [[ $(tr '[:upper:]' '[:lower:]' <<< "${AUDIT_LOGS_ENABLED}") == "true" ]]; then
          textPass "${regx}: Amazon ES domain ${domain} AUDIT_LOGS enabled" "${regx}" "${domain}"
        else
          textFail "${regx}: Amazon ES domain ${domain} AUDIT_LOGS disabled!" "${regx}" "${domain}"
        fi
      done
    else
      textInfo "${regx}: No Amazon ES domain found" "${regx}"
    fi
  done
}

providers/aws/services/emr/check_extra7178 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.


CHECK_ID_extra7178="7.178"
CHECK_TITLE_extra7178="[extra7178] EMR Account Public Access Block enabled"
CHECK_SCORED_extra7178="NOT_SCORED"
CHECK_CIS_LEVEL_extra7178="EXTRA"
CHECK_SEVERITY_extra7178="High"
CHECK_ASFF_TYPE_extra7178="AwsEMR"
CHECK_ALTERNATE_check7178="extra7178"
CHECK_SERVICENAME_extra7178="emr"
CHECK_RISK_extra7178='EMR Clusters must have Account Public Access Block enabled'
CHECK_REMEDIATION_extra7178='Enable EMR Account Public Access Block'
CHECK_DOC_extra7178='https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-block-public-access.html'
CHECK_CAF_EPIC_extra7178='Infrastructure Security'

extra7178(){
    for regx in ${REGIONS}; do
        block_public_access=$("${AWSCLI}" emr get-block-public-access-configuration ${PROFILE_OPT} --region "${regx}" --query 'BlockPublicAccessConfiguration.BlockPublicSecurityGroupRules' --output json 2>&1)
        if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${block_public_access}"; then
            textInfo "${regx}: Access Denied trying to get block public access configuration for EMR clusters" "${regx}"
            continue
        fi
        if [[ "${block_public_access}" == "true"  ]]; then
            textPass "${regx}: EMR Account has Block Public Access enabled" "${regx}"
        else
            textFail "${regx}: EMR Account has Block Public Access disabled" "${regx}"
        fi
    done
}

providers/aws/services/emr/check_extra7177 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.


CHECK_ID_extra7177="7.177"
CHECK_TITLE_extra7177="[extra7177] Publicly accessible EMR Cluster"
CHECK_SCORED_extra7177="NOT_SCORED"
CHECK_CIS_LEVEL_extra7177="EXTRA"
CHECK_SEVERITY_extra7177="High"
CHECK_ASFF_TYPE_extra7177="AwsEMR"
CHECK_ALTERNATE_check7177="extra7177"
CHECK_SERVICENAME_extra7177="emr"
CHECK_RISK_extra7177='EMR Clusters should not be publicly accessible'
CHECK_REMEDIATION_extra7177='Only make acceptable EMR clusters public'
CHECK_DOC_extra7177='https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-block-public-access.html'
CHECK_CAF_EPIC_extra7177='Infrastructure Security'

extra7177(){
    for regx in ${REGIONS}; do
        # List only EMR clusters with the following states: STARTING, BOOTSTRAPPING, RUNNING, WAITING, TERMINATING
        # [NOT TERMINATED AND TERMINATED_WITH_ERRORS]
        LIST_OF_CLUSTERS=$("${AWSCLI}" emr list-clusters ${PROFILE_OPT} --region "${regx}" --query 'Clusters[?(Status.State!=`TERMINATED` && Status.State!=`TERMINATED_WITH_ERRORS`)].Id' --output text 2>&1)
        if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_CLUSTERS}"; then
            textInfo "${regx}: Access Denied trying to list EMR clusters" "${regx}"
            continue
        fi
        if [[ "${LIST_OF_CLUSTERS}" ]]
        then
            for cluster_id in ${LIST_OF_CLUSTERS}; do
                master_public_dns=$("${AWSCLI}" emr describe-cluster ${PROFILE_OPT} --cluster-id "${cluster_id}" --query 'Cluster.MasterPublicDnsName' --region "${regx}" --output text 2>&1)
                if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${master_public_dns}"; then
                    textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
                    continue
                fi
                if [[ $master_public_dns != None && $master_public_dns != *.internal ]];then
                    # If EMR cluster is Public, it is required to check their Security Groups for the Master, the Slaves and the additional ones

                    # Retrive EMR Master Node Security Groups rules
                    master_node_sg=$("${AWSCLI}" emr describe-cluster --cluster-id "${cluster_id}" ${PROFILE_OPT} --region "${regx}" --query 'Cluster.Ec2InstanceAttributes.EmrManagedMasterSecurityGroup' --output text 2>&1)
                    if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${master_node_sg}"; then
                        textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
                        continue
                    fi
                    master_node_sg_internet_open=$("${AWSCLI}" ec2 describe-security-groups --group-ids "${master_node_sg}" --query 'SecurityGroups[?length(IpPermissions[?(contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
                    if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${master_node_sg_internet_open}"; then
                        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
                        continue
                    fi

                    # Retrive EMR Slave Node Security Groups rules
                    slave_node_sg=$("${AWSCLI}" emr describe-cluster --cluster-id "${cluster_id}" ${PROFILE_OPT} --region "${regx}" --query 'Cluster.Ec2InstanceAttributes.EmrManagedSlaveSecurityGroup' --output text 2>&1)
                    if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg}"; then
                        textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
                        continue
                    fi
                    slave_node_sg_internet_open=$("${AWSCLI}" ec2 describe-security-groups --group-ids "${slave_node_sg}" --query 'SecurityGroups[?length(IpPermissions[?(contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
                    if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg_internet_open}"; then
                        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
                        continue
                    fi

                    # Retrive EMR Additional Master node Security Groups rules
                    additional_master_node_sg_list=$("${AWSCLI}" emr describe-cluster --cluster-id "${cluster_id}" ${PROFILE_OPT} --region "${regx}" --query 'Cluster.Ec2InstanceAttributes.AdditionalMasterSecurityGroups' --output text 2>&1)
                    if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg}"; then
                        textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
                        continue
                    fi
                    local additional_master_node_sg_internet_open_list
                    if [[ "${additional_master_node_sg_list}" != "None" ]]; then
                        for additional_master_node_sg in ${additional_master_node_sg_list}; do
                            additional_master_node_sg_internet_open=$("${AWSCLI}" ec2 describe-security-groups --group-ids "${additional_master_node_sg}" --query 'SecurityGroups[?length(IpPermissions[?(contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
                            if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg_internet_open}"; then
                                textInfo "$regx: Access Denied trying to describe security groups" "$regx"
                                continue
                            fi
                            # Store additional master node security groups that allows access from the internet
                            additional_master_node_sg_internet_open_list+=( "${additional_master_node_sg_internet_open}" )
                        done
                    fi

                    # Retrive EMR Additional Slave node Security Groups rules
                    additional_slave_node_sg_list=$("${AWSCLI}" emr describe-cluster --cluster-id "${cluster_id}" ${PROFILE_OPT} --region "${regx}" --query 'Cluster.Ec2InstanceAttributes.AdditionalSlaveSecurityGroups' --output text 2>&1)
                    if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg}"; then
                        textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
                        continue
                    fi
                    local additional_slave_node_sg_internet_open_list
                    if [[ "${additional_slave_node_sg_list}" != "None" ]]; then
                        for additional_slave_node_sg in ${additional_master_node_sg_list}; do
                            additional_slave_node_sg_internet_open=$("${AWSCLI}" ec2 describe-security-groups --group-ids "${additional_slave_node_sg}" --query 'SecurityGroups[?length(IpPermissions[?(contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
                            if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${additional_slave_node_sg_internet_open}"; then
                                textInfo "$regx: Access Denied trying to describe security groups" "$regx"
                                continue
                            fi
                            # Store additional slave node security groups that allows access from the internet
                            additional_slave_node_sg_internet_open_list+=( "${additional_slave_node_sg_internet_open}" )
                        done
                    fi

                    # Check if EMR Cluster is publicly accessible through a Security Group
                    if [[ -n "${master_node_sg_internet_open}" || -n "${slave_node_sg_internet_open}" || "${#additional_master_node_sg_internet_open_list[@]}" -ne 0 || "${#additional_slave_node_sg_internet_open_list[@]}" -ne 0 ]]; then
                        textFail "${regx}: EMR Cluster ${cluster_id} is publicly accessible through the following Security Groups: Master Node ${master_node_sg_internet_open} ${additional_master_node_sg_internet_open_list[*]} -- Slaves Nodes ${slave_node_sg_internet_open} ${additional_slave_node_sg_internet_open_list[*]}" "${regx}" "${cluster_id}"
                    else
                        textPass "${regx}: EMR Cluster ${cluster_id} is not publicly accessible" "${regx}" "${cluster_id}"
                    fi
                else
                    textPass "${regx}: EMR Cluster ${cluster_id} is not publicly accessible" "${regx}" "${cluster_id}"
                fi
            done
        else
            textInfo "${regx}: No EMR Clusters found" "${regx}"
        fi
    done
}

providers/aws/services/emr/check_extra7176 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7176="7.176"
CHECK_TITLE_extra7176="[extra7176] EMR Cluster without Public IP"
CHECK_SCORED_extra7176="NOT_SCORED"
CHECK_CIS_LEVEL_extra7176="EXTRA"
CHECK_SEVERITY_extra7176="Medium"
CHECK_ASFF_TYPE_extra7176="AwsEMR"
CHECK_ALTERNATE_check7176="extra7176"
CHECK_SERVICENAME_extra7176="emr"
CHECK_RISK_extra7176='EMR Cluster should not have Public IP'
CHECK_REMEDIATION_extra7176='Only make acceptable EMR clusters public'
CHECK_DOC_extra7176='https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-block-public-access.html'
CHECK_CAF_EPIC_extra7176='Infrastructure Security'

extra7176(){
    # Public EMR cluster have their DNS ending with .amazonaws.com while private ones have format of ip-xxx-xx-xx.us-east-1.compute.internal.
    for regx in ${REGIONS}; do
        # List only EMR clusters with the following states: STARTING, BOOTSTRAPPING, RUNNING, WAITING, TERMINATING
        # [NOT TERMINATED AND TERMINATED_WITH_ERRORS]
        LIST_OF_CLUSTERS=$("${AWSCLI}" emr list-clusters ${PROFILE_OPT} --region "${regx}" --query 'Clusters[?(Status.State!=`TERMINATED` && Status.State!=`TERMINATED_WITH_ERRORS`)].Id' --output text 2>&1)
        if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_CLUSTERS}"; then
            textInfo "${regx}: Access Denied trying to list clusters of emr" "${regx}"
            continue
        fi
        if [[ "${LIST_OF_CLUSTERS}" ]]
        then
            for cluster_id in ${LIST_OF_CLUSTERS}; do
                master_public_dns=$("${AWSCLI}" emr describe-cluster ${PROFILE_OPT} --cluster-id "${cluster_id}" --query 'Cluster.MasterPublicDnsName' --region "${regx}" --output text 2>&1)
                if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${master_public_dns}"; then
                    textInfo "${regx}: Access Denied trying to describe emr cluster" "${regx}" "${cluster_id}"
                    continue
                fi
                if [[ $master_public_dns != None && $master_public_dns != *.internal ]];then
                    textFail "${regx}: EMR Cluster ${cluster_id} has a Public IP" "${regx}" "${cluster_id}"
                else
                    textPass "${regx}: EMR Cluster ${cluster_id} has not a Public IP" "${regx}" "${cluster_id}"
            fi
            done
        else
            textInfo "${regx}: No EMR Clusters found" "${regx}"
        fi
    done
}

providers/aws/services/elb/check_extra793 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra793="7.93"
CHECK_TITLE_extra793="[extra793] Check if Elastic Load Balancers have SSL listeners "
CHECK_SCORED_extra793="NOT_SCORED"
CHECK_CIS_LEVEL_extra793="EXTRA"
CHECK_SEVERITY_extra793="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra793="AwsElbLoadBalancer"
CHECK_ALTERNATE_check793="extra793"
CHECK_ASFF_COMPLIANCE_TYPE_extra793="ens-mp.com.2.aws.elb.1"
CHECK_SERVICENAME_extra793="elb"
CHECK_RISK_extra793='Clear text communication could affect privacy of information in transit.'
CHECK_REMEDIATION_extra793='Scan for Load Balancers with HTTP or TCP listeners and understand the reason for each of them. Check if the listener can be implemented as TLS instead.'
CHECK_DOC_extra793='https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html'
CHECK_CAF_EPIC_extra793='Data Protection'

extra793(){
  # "Check if Elastic Load Balancers have encrypted listeners "
  for regx in $REGIONS; do
    LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[*].LoadBalancerName' --output text 2>&1|xargs -n1)
    if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?(Type == `application`)].LoadBalancerArn' --output text 2>&1|xargs -n1)
    if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    if [[ $LIST_OF_ELBS || $LIST_OF_ELBSV2 ]]; then
      if [[ $LIST_OF_ELBS ]]; then
        ENCRYPTEDPROTOCOLS=("HTTPS" "SSL")
        for elb in $LIST_OF_ELBS; do
          ELB_PROTOCOLS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --load-balancer-name $elb --query "LoadBalancerDescriptions[0].ListenerDescriptions[*].Listener.Protocol" --output text)
          passed=true
          potential_redirect=false
          for protocol in $ELB_PROTOCOLS; do
            if array_contains ENCRYPTEDPROTOCOLS "$protocol"; then
              continue
            else
              # Check if both HTTP and HTTPS in use
              if [[ $(echo $ELB_PROTOCOLS | grep HTTPS) ]]; then
                 potential_redirect=true
              fi
              passed=false
            fi
          done

          if $passed; then
            textPass "$regx: $elb has encrypted listeners" "$regx"
          else
            if $potential_redirect; then
              textInfo "$regx: $elb has both encrypted and non-encrypted listeners" "$regx"
            else
              textFail "$regx: $elb has non-encrypted listeners" "$regx" "$elb"
            fi
          fi
        done
      fi
      if [[ $LIST_OF_ELBSV2 ]]; then
        for elbarn in $LIST_OF_ELBSV2; do
          https_only=true
          redirect_rule=false
          elbname=$(echo $elbarn | awk -F 'loadbalancer/app/' '{print $2}' | awk -F '/' '{print $1}')

          ELBV2_LISTENERS=$($AWSCLI elbv2 describe-listeners $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query "Listeners[*]")
          ELBV2_PROTOCOLS=$(echo $ELBV2_LISTENERS | jq -r '.[].Protocol')

          if [[ $(echo $ELBV2_PROTOCOLS | grep HTTPS) ]]; then
             for line in $(echo $ELBV2_LISTENERS | jq -r '.[] | .Protocol + "," + .ListenerArn'); do
                protocol=$(echo $line | awk -F ',' '{print $1}')
                listenerArn=$(echo $line | awk -F ',' '{print $2}')
                if [[ $protocol  == "HTTP" ]]; then
                  https_only=false
                  # Check for redirect rule
                  ELBV2_RULES=$($AWSCLI elbv2 describe-rules $PROFILE_OPT --region $regx --listener-arn $listenerArn --query 'Rules[]')
                  if [[ $(echo $ELBV2_RULES | jq -r '.[].Actions[].RedirectConfig.Protocol' | grep HTTPS) ]]; then
                     redirect_rule=true
                  fi
               fi
             done

             if $https_only; then
               textPass "$regx: $elbname has HTTPS listeners only" "$regx"
             else
               if $redirect_rule; then
                 textInfo "$regx: $elbname has HTTP listener that redirects to HTTPS" "$regx"
               else
                 textFail "$regx: $elbname has non-encrypted listeners" "$regx" "$elbname"
               fi
             fi
          else
            textFail "$regx: $elbname has non-encrypted listeners" "$regx" "$elbname"
          fi
        done
      fi
    else
      textInfo "$regx: No ELBs found" "$regx"
    fi
  done
}

array_contains () {
    local array="$1[@]"
    local seeking=$2
    local in=1
    for element in "${!array}"; do
        if [[ $element == "$seeking" ]]; then
            in=0
            break
        fi
    done
    return $in
}

providers/aws/services/elb/check_extra792 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra792="7.92"
CHECK_TITLE_extra792="[extra792] Check if Elastic Load Balancers have insecure SSL ciphers "
CHECK_SCORED_extra792="NOT_SCORED"
CHECK_CIS_LEVEL_extra792="EXTRA"
CHECK_SEVERITY_extra792="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra792="AwsElbLoadBalancer"
CHECK_ALTERNATE_check792="extra792"
CHECK_ASFF_COMPLIANCE_TYPE_extra792="ens-mp.com.2.aws.elb.2"
CHECK_SERVICENAME_extra792="elb"
CHECK_RISK_extra792='Using insecure ciphers could affect privacy of in transit information.'
CHECK_REMEDIATION_extra792='Use a Security policy with a ciphers that are stronger as possible. Drop legacy and unsecure ciphers.'
CHECK_DOC_extra792='https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-ssl-security-policy.html'
CHECK_CAF_EPIC_extra792='Data Protection'

extra792(){
  # "Check if Elastic Load Balancers have insecure SSL ciphers "
  for regx in $REGIONS; do
    LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[*].LoadBalancerName' --output text 2>&1|xargs -n1 )
    if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[*].LoadBalancerArn' --output text 2>&1|xargs -n1 )
    if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    if [[ $LIST_OF_ELBS || $LIST_OF_ELBSV2 ]]; then
      if [[ $LIST_OF_ELBS ]]; then
        # https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-ssl-security-policy.html#ssl-ciphers
        # https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html
        ELBSECUREPOLICIES=("ELBSecurityPolicy-TLS-1-2-2017-01")
        ELBSECURECIPHERS=("Protocol-TLSv1.2" "ECDHE-ECDSA-AES128-GCM-SHA256" "ECDHE-RSA-AES128-GCM-SHA256" "ECDHE-ECDSA-AES128-SHA256" "ECDHE-RSA-AES128-SHA256" "ECDHE-ECDSA-AES128-SHA" "ECDHE-RSA-AES128-SHA" "ECDHE-ECDSA-AES256-GCM-SHA384" "ECDHE-RSA-AES256-GCM-SHA384" "ECDHE-ECDSA-AES256-SHA384" "ECDHE-RSA-AES256-SHA384" "ECDHE-RSA-AES256-SHA" "ECDHE-ECDSA-AES256-SHA" "AES128-GCM-SHA256" "AES128-SHA256" "AES128-SHA" "AES256-GCM-SHA384" "AES256-SHA256" "AES256-SHA" "Server-Defined-Cipher-Order")

        for elb in $LIST_OF_ELBS; do
          ELB_LISTENERS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --load-balancer-name $elb --query "LoadBalancerDescriptions[0]" --output json)

          ELB_PROTOCOLS=$(echo $ELB_LISTENERS | jq -r '.ListenerDescriptions[].Listener.Protocol')
          if [[ $(echo $ELB_PROTOCOLS | grep HTTPS) || $(echo $ELB_PROTOCOLS | grep SSL) ]]; then
             ELB_POLICIES=$(echo $ELB_LISTENERS | jq -r '.ListenerDescriptions[].PolicyNames | .[]')
             passed=true
             for policy in $ELB_POLICIES; do
               # Check for secure default policy
               REFPOLICY=$($AWSCLI elb describe-load-balancer-policies $PROFILE_OPT --region $regx --load-balancer-name $elb --policy-name $policy --query "PolicyDescriptions[0].PolicyAttributeDescriptions[?(AttributeName == 'Reference-Security-Policy')].AttributeValue" --output text)
               if [[ -n "$REFPOLICY" ]]; then
                 if array_contains ELBSECUREPOLICIES "$REFPOLICY"; then
                    continue 	# Passed for this listener/policy
                 else
                    passed=false
                 fi
               else
                 # A custom policy is in use.  Check Ciphers
                 CIPHERS=$($AWSCLI  elb describe-load-balancer-policies $PROFILE_OPT --region $regx --load-balancer-name $elb --policy-name $policy --query "PolicyDescriptions[0].PolicyAttributeDescriptions[?(AttributeValue == 'true')].AttributeName" --output text)
                 for cipher in $CIPHERS; do
                   if array_contains ELBSECURECIPHERS "$cipher"; then
                     continue
                   else
                     passed=false
                   fi
                 done
               fi
             done

             if $passed; then
               textPass "$regx: $elb has no insecure SSL ciphers" "$regx" "$elb"
             else
               textFail "$regx: $elb has insecure SSL ciphers" "$regx" "$elb"
             fi
          else
             textInfo "$regx: $elb does not have an HTTPS or SSL listener" "$regx"
          fi
        done
      fi
      if [[ $LIST_OF_ELBSV2 ]]; then
        # NOTE - ALBs do NOT support custom security policies
        # https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html
        ELBV2SECUREPOLICIES=("ELBSecurityPolicy-TLS-1-2-2017-01" "ELBSecurityPolicy-TLS-1-2-Ext-2018-06" "ELBSecurityPolicy-FS-1-2-2019-08" "ELBSecurityPolicy-FS-1-2-Res-2019-08" "ELBSecurityPolicy-FS-1-2-Res-2020-10" "ELBSecurityPolicy-TLS13-1-2-2021-06" "ELBSecurityPolicy-TLS13-1-3-2021-06" "ELBSecurityPolicy-TLS13-1-2-Res-2021-06" "ELBSecurityPolicy-TLS13-1-2-Ext1-2021-06" "ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06")

        for elbarn in $LIST_OF_ELBSV2; do
          passed=true
          if [[ $(echo $elbarn | grep "loadbalancer/app/") ]]; then
            elbname=$(echo $elbarn | awk -F 'loadbalancer/app/' '{print $2}' | awk -F '/' '{print $1}')
          elif [[ $(echo $elbarn | grep "loadbalancer/net/") ]]; then
            elbname=$(echo $elbarn | awk -F 'loadbalancer/net/' '{print $2}' | awk -F '/' '{print $1}')
          else
            elbname=$elbarn
          fi

          ELBV2_LISTENERS=$($AWSCLI elbv2 describe-listeners $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query "Listeners[*]" --output json)

          ELBV2_PROTOCOLS=$(echo $ELBV2_LISTENERS | jq -r '.[].Protocol')

          if [[ $(echo $ELBV2_PROTOCOLS | grep HTTPS) || $(echo $ELBV2_PROTOCOLS | grep TLS) ]]; then
             ELBV2_SSL_POLICIES=$($AWSCLI elbv2 describe-listeners $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query 'Listeners[*].SslPolicy' --output text)

             for policy in $ELBV2_SSL_POLICIES; do
               if array_contains ELBV2SECUREPOLICIES "$policy"; then
                 continue        # Passed for this listener/policy
               else
                 passed=false
               fi
             done

             if $passed; then
               textPass "$regx: $elbname has no insecure SSL ciphers" "$regx" "$elbname"
             else
               textFail "$regx: $elbname has insecure SSL ciphers" "$regx" "$elbname"
             fi
          else
             textInfo "$regx: $elbname does not have an HTTPS or TLS listener" "$regx"
          fi
        done
      fi
    else
      textInfo "$regx: No ELBs found" "$regx"
    fi
  done
}

array_contains () {
    local array="$1[@]"
    local seeking=$2
    local in=1
    for element in "${!array}"; do
        if [[ $element == "$seeking" ]]; then
            in=0
            break
        fi
    done
    return $in
}

providers/aws/services/elb/check_extra79 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra79="7.9"
CHECK_TITLE_extra79="[extra79] Check for internet facing Elastic Load Balancers"
CHECK_SCORED_extra79="NOT_SCORED"
CHECK_CIS_LEVEL_extra79="EXTRA"
CHECK_SEVERITY_extra79="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra79="AwsElbLoadBalancer"
CHECK_ALTERNATE_extra709="extra79"
CHECK_ALTERNATE_check79="extra79"
CHECK_ALTERNATE_check709="extra79"
CHECK_SERVICENAME_extra79="elb"
CHECK_RISK_extra79='Publicly accessible load balancers could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra79='Ensure the load balancer should be publicly accessible. If publicly exposed ensure a WAF ACL is implemented.'
CHECK_DOC_extra79='https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html'
CHECK_CAF_EPIC_extra79='Data Protection'

extra79(){
  # "Check for internet facing Elastic Load Balancers "
  for regx in $REGIONS; do
    LIST_OF_PUBLIC_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].[LoadBalancerName,DNSName]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_PUBLIC_ELBS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied Trying to describe load balancers" "$regx"
      continue
    fi
    LIST_OF_PUBLIC_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Scheme == `internet-facing`].[LoadBalancerName,DNSName]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_PUBLIC_ELBSV2" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied Trying to describe load balancers" "$regx"
      continue
    fi
    LIST_OF_ALL_ELBS=$( echo $LIST_OF_PUBLIC_ELBS; echo $LIST_OF_PUBLIC_ELBSV2)
    LIST_OF_ALL_ELBS_PER_LINE=$( echo $LIST_OF_ALL_ELBS| xargs -n2 )
    if [[ $LIST_OF_ALL_ELBS ]];then
      while read -r elb;do
        ELB_NAME=$(echo $elb | awk '{ print $1; }')
        ELB_DNSNAME=$(echo $elb | awk '{ print $2; }')
        textFail "$regx: ELB: $ELB_NAME at DNS: $ELB_DNSNAME is internet-facing!" "$regx" "$ELB_NAME"
      done <<< "$LIST_OF_ALL_ELBS_PER_LINE"
      else
        textPass "$regx: no Internet Facing ELBs found" "$regx" "$ELB_NAME"
    fi
  done
}

providers/aws/services/elb/check_extra717 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra717="7.17"
CHECK_TITLE_extra717="[extra717] Check if Elastic Load Balancers have logging enabled"
CHECK_SCORED_extra717="NOT_SCORED"
CHECK_CIS_LEVEL_extra717="EXTRA"
CHECK_SEVERITY_extra717="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra717="AwsElbLoadBalancer"
CHECK_ALTERNATE_check717="extra717"
CHECK_SERVICENAME_extra717="elb"
CHECK_RISK_extra717='If logs are not enabled monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra717='Enable ELB logging; create la log lifecycle and define use cases.'
CHECK_DOC_extra717='https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html'
CHECK_CAF_EPIC_extra717='Logging and Monitoring'

extra717(){
  # "Check if Elastic Load Balancers have logging enabled "
  for regx in $REGIONS; do
    LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[*].LoadBalancerName' --output text 2>&1 |xargs -n1)
    if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to list load balancers v1" "$regx"
        continue
    fi
    LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[*].LoadBalancerArn' --output text 2>&1 |xargs -n1)
    if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to list load balancers v2" "$regx"
        continue
    fi
    if [[ $LIST_OF_ELBS || $LIST_OF_ELBSV2 ]]; then
      if [[ $LIST_OF_ELBS ]]; then
        for elb in $LIST_OF_ELBS; do
          CHECK_ELBS_LOG_ENABLED=$($AWSCLI elb describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-name $elb --query 'LoadBalancerAttributes.AccessLog.Enabled' |grep "^true")
          if [[ $CHECK_ELBS_LOG_ENABLED ]]; then
            textPass "$regx: $elb has access logs to S3 configured" "$regx" "$elb"
          else
            textFail "$regx: $elb has not configured access logs" "$regx" "$elb"
          fi
        done
      fi
      if [[ $LIST_OF_ELBSV2 ]]; then
        for elbarn in $LIST_OF_ELBSV2; do
          CHECK_ELBSV2_LOG_ENABLED=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query Attributes[*] --output text |grep "^access_logs.s3.enabled"|cut -f2|grep true)
          ELBV2_NAME=$(echo $elbarn|cut -d\/ -f3)
          if [[ $CHECK_ELBSV2_LOG_ENABLED ]]; then
            textPass "$regx: $ELBV2_NAME has access logs to S3 configured" "$regx" "$elb"
          else
            textFail "$regx: $ELBV2_NAME has not configured access logs" "$regx" "$elb"
          fi
        done
      fi
    else
      textInfo "$regx: No ELBs found" "$regx"
    fi
  done
}

providers/aws/services/elb/check_extra7159 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7159="7.159"
CHECK_TITLE_extra7159="[extra7159] Check if ELB has listeners underneath"
CHECK_SCORED_extra7159="NOT_SCORED"
CHECK_CIS_LEVEL_extra7159="EXTRA"
CHECK_SEVERITY_extra7159="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7159="AwsElbLoadBalancer"
CHECK_ALTERNATE_check7159="extra7159"
CHECK_SERVICENAME_extra7159="elb"
CHECK_RISK_extra7159='The rules that are defined for a listener determine how the load balancer routes requests to its registered targets.'
CHECK_REMEDIATION_extra7159='Add listeners to Elastic Load Balancers.'
CHECK_DOC_extra7159='https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html'
CHECK_CAF_EPIC_extra7159='Data Protection'

extra7159(){
  for regx in $REGIONS; do
    LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers --query 'LoadBalancerDescriptions[*].LoadBalancerName' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    if [[ $LIST_OF_ELBS ]]; then
      for elb in $LIST_OF_ELBS; do
        LIST_OF_LISTENERS=$($AWSCLI elb describe-load-balancers --load-balancer-name $elb --query 'LoadBalancerDescriptions[*].ListenerDescriptions' $PROFILE_OPT --region $regx --output text)
        if [[ $LIST_OF_LISTENERS ]]; then
            textPass "$regx: $elb has listeners underneath" "$regx" "$elb"
        else
            textFail "$regx: $elb has no listeners underneath" "$regx" "$elb"
        fi
      done
    else
      textInfo "$regx: No ELBs found" "$regx"
    fi
  done
}

providers/aws/services/elb/check_extra7158 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7158="7.158"
CHECK_TITLE_extra7158="[extra7158] Check if ELBV2 has listeners underneath"
CHECK_SCORED_extra7158="NOT_SCORED"
CHECK_CIS_LEVEL_extra7158="EXTRA"
CHECK_SEVERITY_extra7158="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7158="AwsElbv2LoadBalancer"
CHECK_ALTERNATE_check7158="extra7158"
CHECK_SERVICENAME_extra7158="elb"
CHECK_RISK_extra7158='The rules that are defined for a listener determine how the load balancer routes requests to its registered targets.'
CHECK_REMEDIATION_extra7158='Add listeners to Elastic Load Balancers V2.'
CHECK_DOC_extra7158='https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html'
CHECK_CAF_EPIC_extra7158='Data Protection'

extra7158(){
  for regx in $REGIONS; do
    LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers --query 'LoadBalancers[*].LoadBalancerArn' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    if [[ $LIST_OF_ELBSV2 ]]; then
      for elb in $LIST_OF_ELBSV2; do
        LIST_OF_LISTENERS=$($AWSCLI elbv2 describe-listeners $PROFILE_OPT --region $regx --load-balancer-arn $elb --query 'Listeners[*]' --output text)
        ELBV2_NAME=$(echo $elb|cut -d\/ -f3)
        if [[ $LIST_OF_LISTENERS ]]; then
            textPass "$regx: $ELBV2_NAME has listeners underneath" "$regx" "$elb"
        else
            textFail "$regx: $ELBV2_NAME has no listeners underneath" "$regx" "$elb"
        fi
      done
    else
      textInfo "$regx: No ELBs found" "$regx"
    fi
  done
}

providers/aws/services/elb/check_extra7155 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# Remediation:
#
#   https://docs.aws.amazon.com/cli/latest/reference/elbv2/modify-load-balancer-attributes.html
#
#   aws elbv2 modify-load-balancer-attributes
#   --load-balancer-arn <alb arn>\
#   --attributes Key=routing.http.desync_mitigation_mode,Value=<defensive/strictest>

CHECK_ID_extra7155="7.155"
CHECK_TITLE_extra7155="[extra7155] Check whether the Application Load Balancer is configured with defensive or strictest desync mitigation mode"
CHECK_SCORED_extra7155="NOT_SCORED"
CHECK_CIS_LEVEL_extra7155="EXTRA"
CHECK_SEVERITY_extra7155="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7155="AwsElasticLoadBalancingV2LoadBalancer"
CHECK_ALTERNATE_check7155="extra7155"
CHECK_SERVICENAME_extra7155="elb"
CHECK_RISK_extra7155='HTTP Desync issues can lead to request smuggling and make your applications vulnerable to request queue or cache poisoning; which could lead to credential hijacking or execution of unauthorized commands.'
CHECK_REMEDIATION_extra7155='Ensure Application Load Balancer is configured with defensive or strictest desync mitigation mode'
CHECK_DOC_extra7155='https://aws.amazon.com/about-aws/whats-new/2020/08/application-and-classic-load-balancers-adding-defense-in-depth-with-introduction-of-desync-mitigation-mode/'
CHECK_CAF_EPIC_extra7155='Data Protection'

extra7155() {
  for regx in $REGIONS; do
    LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Type == `application`].[LoadBalancerArn]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    if [[ $LIST_OF_ELBSV2 ]];then
      for alb in $LIST_OF_ELBSV2;do
        CHECK_DESYNC_MITIGATION_MODE=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $alb --query 'Attributes[8]' --output json | jq -r '.Value')
        if [[ $CHECK_DESYNC_MITIGATION_MODE == "monitor" ]]; then
          textFail "$regx: Application load balancer $alb does not have desync mitigation mode set as defensive or strictest." "$regx" "$alb"
        else
          textPass "$regx: Application load balancer $alb is configured with correct desync mitigation mode." "$regx" "$alb"
        fi
      done
    else
      textInfo "$regx: No Application Load Balancers found" "$regx"
    fi
  done
}

providers/aws/services/elb/check_extra7150 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7150="7.150"
CHECK_TITLE_extra7150="[extra7150] Check if Elastic Load Balancers have deletion protection enabled"
CHECK_SCORED_extra7150="NOT_SCORED"
CHECK_CIS_LEVEL_extra7150="EXTRA"
CHECK_SEVERITY_extra7150="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7150="AwsElbLoadBalancer"
CHECK_ALTERNATE_check7150="extra7150"
CHECK_SERVICENAME_extra7150="elb"
CHECK_RISK_extra7150='If deletion protection is not enabled; the resource is not protected against deletion.'
CHECK_REMEDIATION_extra7150='Enable deletion protection attribute; this is not enabled by default.'
CHECK_DOC_extra7150='https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#deletion-protection'
CHECK_CAF_EPIC_extra7150='Data Protection'

extra7150(){
  # "Check if Elastic Load Balancers have delete protection enabled."
  for regx in $REGIONS; do
    LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[*].LoadBalancerArn' --output text 2>&1|xargs -n1 )
    if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    if [[ $LIST_OF_ELBSV2 ]]; then
      for elb in $LIST_OF_ELBSV2; do
        CHECK_DELETION_PROTECTION_ENABLED=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $elb --query Attributes[*] --output text|grep "deletion_protection.enabled"|grep true )
        ELBV2_NAME=$(echo $elb|cut -d\/ -f3)
        if [[ $CHECK_DELETION_PROTECTION_ENABLED ]]; then
          textPass "$regx: $ELBV2_NAME has the attribute deletion protection enabled" "$regx" "$elb"
        else
          textFail "$regx: $ELBV2_NAME does not have deletion protection enabled." "$regx" "$elb"
        fi
      done
    else
      textInfo "$regx: No ELBs found" "$regx"
    fi
  done
}

providers/aws/services/elb/check_extra7142 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7142="7.142"
CHECK_TITLE_extra7142="[extra7142] Check if Application Load Balancer is dropping invalid packets to prevent header based HTTP request smuggling"
CHECK_SCORED_extra7142="NOT_SCORED"
CHECK_CIS_LEVEL_extra7142="EXTRA"
CHECK_SEVERITY_extra7142="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7142="AwsElasticLoadBalancingV2LoadBalancer"
CHECK_ALTERNATE_check7142="extra7142"
CHECK_ASFF_COMPLIANCE_TYPE_extra7142=""
CHECK_SERVICENAME_extra7142="elb"
CHECK_RISK_extra7142='ALB can be target of actors sending bad HTTP headers'
CHECK_REMEDIATION_extra7142='Ensure Application Load Balancer is configured for HTTP headers with header fields that are not valid are removed by the load balancer (true)'
CHECK_DOC_extra7142='https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#desync-mitigation-mode'
CHECK_CAF_EPIC_extra7142='Data Protection'


extra7142(){
  for regx in $REGIONS; do
    LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Type == `application`].[LoadBalancerArn]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
        continue
    fi
    if [[ $LIST_OF_ELBSV2 ]];then
      for alb in $LIST_OF_ELBSV2;do
        CHECK_IF_DROP_INVALID_HEADER_FIELDS=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $alb --query 'Attributes[6]' --output text|grep -i true)
        if [[ $CHECK_IF_DROP_INVALID_HEADER_FIELDS ]];then
          textPass "$regx: Application Load Balancer $alb is dropping invalid header fields." "$regx" "$alb"
        else
          textFail "$regx: Application Load Balancer $alb is not dropping invalid header fields" "$regx" "$alb"
        fi
      done
    else
      textInfo "$regx: no ALBs found"
    fi
  done
}

providers/aws/services/elb/check_extra7129 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7129="7.129"
CHECK_TITLE_extra7129="[extra7129] Check if Application Load Balancer has a WAF ACL attached"
CHECK_SCORED_extra7129="NOT_SCORED"
CHECK_CIS_LEVEL_extra7129="EXTRA"
CHECK_SEVERITY_extra7129="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7129="AwsElasticLoadBalancingV2LoadBalancer"
CHECK_ALTERNATE_check7129="extra7129"
CHECK_ASFF_COMPLIANCE_TYPE_extra7129="ens-mp.s.2.aws.waf.3"
CHECK_SERVICENAME_extra7129="elb"
CHECK_RISK_extra7129='If not WAF ACL is attached risk of web attacks increases.'
CHECK_REMEDIATION_extra7129='Using the AWS Management Console open the AWS WAF console to attach an ACL.'
CHECK_DOC_extra7129='https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html'
CHECK_CAF_EPIC_extra7129='Infrastructure Security'

PARALLEL_REGIONS="50"

extra7129(){
  for regx in $REGIONS; do
      # (
        LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Scheme == `internet-facing` && Type == `application`].[LoadBalancerName]' --output text 2>&1)
        if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
            continue
        fi
        LIST_OF_WAFV2_WEBACL_ARN=$($AWSCLI wafv2 list-web-acls $PROFILE_OPT --region=$regx --scope=REGIONAL --query WebACLs[*].ARN --output text)
        LIST_OF_WAFV1_WEBACL_WEBACLID=$($AWSCLI waf-regional list-web-acls $PROFILE_OPT --region $regx --query WebACLs[*].[WebACLId] --output text)

        if [[ $LIST_OF_ELBSV2 ]]; then
          for alb in $LIST_OF_ELBSV2; do
            if [[ ${#LIST_OF_WAFV2_WEBACL_ARN[@]} -gt 0 || ${#LIST_OF_WAFV1_WEBACL_WEBACLID[@]} -gt 0 ]]; then
              WAF_PROTECTED_ALBS=()
              for wafaclarn in $LIST_OF_WAFV2_WEBACL_ARN; do
                ALB_RESOURCES_IN_WEBACL=$($AWSCLI wafv2 list-resources-for-web-acl $PROFILE_OPT --web-acl-arn $wafaclarn --region=$regx --resource-type APPLICATION_LOAD_BALANCER --query ResourceArns --output text | xargs -n1 | awk -F'/' '{ print $3 }'| grep $alb)
                if [[ $ALB_RESOURCES_IN_WEBACL ]]; then
                  WAF_PROTECTED_ALBS+=($wafaclarn)
                fi
              done
              for wafv1aclid in $LIST_OF_WAFV1_WEBACL_WEBACLID; do
                ALB_RESOURCES_IN_WEBACL=$($AWSCLI waf-regional list-resources-for-web-acl $PROFILE_OPT --web-acl-id $wafv1aclid --region=$regx --resource-type APPLICATION_LOAD_BALANCER --output text --query "[ResourceArns]"| grep $alb)
                if [[ $ALB_RESOURCES_IN_WEBACL ]]; then
                  WAFv1_PROTECTED_ALBS+=($wafv1aclid)
                fi
              done
              if [[ ${#WAF_PROTECTED_ALBS[@]} -gt 0 || ${#WAFv1_PROTECTED_ALBS[@]} -gt 0 ]]; then
                if [[ ${#WAF_PROTECTED_ALBS[@]} -gt 0 ]]; then
                  for wafaclarn in "${WAF_PROTECTED_ALBS[@]}"; do
                    WAFV2_WEBACL_ARN_SHORT=$(echo $wafaclarn |  awk -F'/' '{ print $3 }')
                    textPass "$regx: Application Load Balancer $alb is protected by WAFv2 ACL $WAFV2_WEBACL_ARN_SHORT" "$regx" "$alb"
                  done
                fi
                if [[ ${#WAFv1_PROTECTED_ALBS[@]} -gt 0 ]]; then
                  for wafv1aclid in "${WAFv1_PROTECTED_ALBS[@]}"; do
                    textPass "$regx: Application Load Balancer $alb is protected by WAFv1 ACL $wafv1aclid" "$regx" "$alb"
                  done
                fi
              else
                textFail "$regx: Application Load Balancer $alb is not protected by WAF ACL" "$regx" "$alb"
              fi
            else
              textFail "$regx: Application Load Balancer $alb is not protected no WAF ACL found" "$regx" "$alb"
            fi
          done
        else
          textInfo "$regx: No Application Load Balancers found" "$regx"
        fi
    # ) &
  done
  # wait
}

providers/aws/services/eks/check_extra797 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra797="7.97"
CHECK_TITLE_extra797="[extra797] Ensure Kubernetes Secrets are encrypted using Customer Master Keys (CMKs)"
CHECK_SCORED_extra797="NOT_SCORED"
CHECK_CIS_LEVEL_extra797="EXTRA"
CHECK_SEVERITY_extra797="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra797="AwsEksCluster"
CHECK_ALTERNATE_check797="extra797"
CHECK_SERVICENAME_extra797="eks"
CHECK_RISK_extra797='Implementing envelope encryption is considered a security best practice for applications that store sensitive data and is part of a defense in depth security strategy.'
CHECK_REMEDIATION_extra797=' Setup your own Customer Master Key (CMK) in KMS and link this key by providing the CMK ARN when you create an EKS cluster.'
CHECK_DOC_extra797='https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html'
CHECK_CAF_EPIC_extra797='Data Protection'

extra797(){
  for regx in $REGIONS; do
    CLUSTERS=$($AWSCLI eks list-clusters $PROFILE_OPT --region $regx --query 'clusters[]' --output text 2>&1)
    if [[ $(echo "$CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list EKS clusters" "$regx"
        continue
    fi
    if [[ $CLUSTERS ]]; then
      for CLUSTER in $CLUSTERS;do
        ENC_CONFIG=$($AWSCLI eks describe-cluster $PROFILE_OPT --region $regx --name $CLUSTER --query 'cluster.encryptionConfig')

        if [[ $ENC_CONFIG == "null" ]]; then
           textFail "$regx: Encryption for Kubernetes secrets is not configured for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
        else
           textPass "$regx: Encryption for Kubernetes secrets is configured for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
        fi
      done
    else
      textInfo "$regx: No EKS clusters found" "$regx"
    fi
  done
}

providers/aws/services/eks/check_extra796 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra796="7.96"
CHECK_TITLE_extra796="[extra796] Restrict Access to the EKS Control Plane Endpoint"
CHECK_SCORED_extra796="NOT_SCORED"
CHECK_CIS_LEVEL_extra796="EXTRA"
CHECK_SEVERITY_extra796="High"
CHECK_ASFF_RESOURCE_TYPE_extra796="AwsEksCluster"
CHECK_ALTERNATE_check796="extra796"
CHECK_SERVICENAME_extra796="eks"
CHECK_RISK_extra796='By default; this API server endpoint is public to the internet; and access to the API server is secured using a combination of AWS Identity and Access Management (IAM) and native Kubernetes Role Based Access Control (RBAC).'
CHECK_REMEDIATION_extra796='You should enable private access to the Kubernetes API server so that all communication between your nodes and the API server stays within your VPC. You can limit the IP addresses that can access your API server from the internet; or completely disable internet access to the API server.'
CHECK_DOC_extra796='https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html'
CHECK_CAF_EPIC_extra796='Infrastructure Security'

extra796(){
  for regx in $REGIONS; do
    CLUSTERS=$($AWSCLI eks list-clusters $PROFILE_OPT --region $regx --query 'clusters[]' --output text 2>&1)
    if [[ $(echo "$CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list EKS clusters" "$regx"
        continue
    fi
    if [[ $CLUSTERS ]]; then
      for CLUSTER in $CLUSTERS;do
        CLUSTERDEF=$($AWSCLI eks describe-cluster $PROFILE_OPT --region $regx --name $CLUSTER --query 'cluster.resourcesVpcConfig')
        PUB_ENABLED=$(echo $CLUSTERDEF | jq -r '.endpointPublicAccess')
        PRIV_ENABLED=$(echo $CLUSTERDEF | jq -r '.endpointPrivateAccess')
        PUB_ACCESS_CIDRS=$(echo $CLUSTERDEF | jq -r '.publicAccessCidrs')

        if [[ $PUB_ENABLED == "false" ]] && [[ $PRIV_ENABLED == "true" ]] ; then
          textPass "$regx: Cluster endpoint access is private for EKS cluster $CLUSTER" "$regx"
        else
          if [[ $(echo $PUB_ACCESS_CIDRS | grep "0.0.0.0/0") ]] ; then
            textFail "$regx: Cluster control plane access is not restricted for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
          else
            textPass "$regx: Cluster control plane access is restricted for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
          fi
        fi
      done
    else
      textInfo "$regx: No EKS clusters found" "$regx"
    fi
  done
}

providers/aws/services/eks/check_extra795 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra795="7.95"
CHECK_TITLE_extra795="[extra795] Ensure EKS Clusters are created with Private Endpoint Enabled and Public Access Disabled"
CHECK_SCORED_extra795="NOT_SCORED"
CHECK_CIS_LEVEL_extra795="EXTRA"
CHECK_SEVERITY_extra795="High"
CHECK_ASFF_RESOURCE_TYPE_extra795="AwsEksCluster"
CHECK_ALTERNATE_check795="extra795"
CHECK_SERVICENAME_extra795="eks"
CHECK_RISK_extra795='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra795='Enable private access to the Kubernetes API server so that all communication between your nodes and the API server stays within your VPC. Disable internet access to the API server.'
CHECK_DOC_extra795='https://docs.aws.amazon.com/eks/latest/userguide/infrastructure-security.html'
CHECK_CAF_EPIC_extra795='Infrastructure Security'

extra795(){
  for regx in $REGIONS; do
    CLUSTERS=$($AWSCLI eks list-clusters $PROFILE_OPT --region $regx --query 'clusters[]' --output text 2>&1)
    if [[ $(echo "$CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list EKS clusters" "$regx"
        continue
    fi
    if [[ $CLUSTERS ]]; then
      for CLUSTER in $CLUSTERS;do
        CLUSTERDEF=$($AWSCLI eks describe-cluster $PROFILE_OPT --region $regx --name $CLUSTER --query 'cluster.resourcesVpcConfig')
        PUB_ENABLED=$(echo $CLUSTERDEF | jq -r '.endpointPublicAccess')
        PRIV_ENABLED=$(echo $CLUSTERDEF | jq -r '.endpointPrivateAccess')

        if [[ $PUB_ENABLED == "false" ]] && [[ $PRIV_ENABLED == "true" ]] ; then
          textPass "$regx: Cluster endpoint access is private for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
        else
              textFail "$regx: Cluster endpoint access is public for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
        fi
      done
    else
      textInfo "$regx: No EKS clusters found" "$regx"
    fi
  done
}

providers/aws/services/eks/check_extra794 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra794="7.94"
CHECK_TITLE_extra794="[extra794] Ensure EKS Control Plane Audit Logging is enabled for all log types"
CHECK_SCORED_extra794="NOT_SCORED"
CHECK_CIS_LEVEL_extra794="EXTRA"
CHECK_SEVERITY_extra794="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra794="AwsEksCluster"
CHECK_ALTERNATE_check794="extra794"
CHECK_SERVICENAME_extra794="eks"
CHECK_RISK_extra794='If logs are not enabled; monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra794='Make sure you logging for EKS control plane is enabled.'
CHECK_DOC_extra794='https://docs.aws.amazon.com/eks/latest/userguide/logging-monitoring.html'
CHECK_CAF_EPIC_extra794='Logging and Monitoring'

extra794(){
  for regx in $REGIONS; do
    CLUSTERS=$($AWSCLI eks list-clusters $PROFILE_OPT --region $regx --query 'clusters[]' --output text 2>&1)
    if [[ $(echo "$CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list EKS clusters" "$regx"
        continue
    fi
    if [[ $CLUSTERS ]]; then
      for CLUSTER in $CLUSTERS;do
        CLUSTERDEF=$($AWSCLI eks describe-cluster $PROFILE_OPT --region $regx --name $CLUSTER --query 'cluster.logging.clusterLogging[0]')
        LOGGING_ENABLED=$(echo $CLUSTERDEF | jq -r '.enabled')
        TYPES=$(echo $CLUSTERDEF | jq -r '.types[]')
        if [[ $LOGGING_ENABLED == "true" ]]; then
           if [[ $(echo $TYPES | egrep "api.*audit.*authenticator.*controllerManager.*scheduler") ]]; then
              textPass "$regx: Control plane logging enabled and correctly configured for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
           else
              textFail "$regx: Control plane logging enabled; not all log types collected for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
           fi
        else
              textFail "$regx: Control plane logging is not enabled for EKS cluster $CLUSTER" "$regx" "$CLUSTER"
        fi
      done
    else
      textInfo "$regx: No EKS clusters found" "$regx"
    fi
  done
}

providers/aws/services/efs/check_extra7161 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7161="7.161"
CHECK_TITLE_extra7161="[extra7161] Check if EFS protects sensitive data with encryption at rest"
CHECK_SCORED_extra7161="NOT_SCORED"
CHECK_CIS_LEVEL_extra7161="EXTRA"
CHECK_SEVERITY_extra7161="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7161="AwsEfsFileSystem"
CHECK_ALTERNATE_check7161="extra7161"
CHECK_SERVICENAME_extra7161="efs"
CHECK_RISK_extra7161='EFS should be encrypted at rest to prevent exposure of sensitive data to bad actors'
CHECK_REMEDIATION_extra7161='Ensure that encryption at rest is enabled for EFS file systems. Encryption at rest can only be enabled during the file system creation.'
CHECK_DOC_extra7161='https://docs.aws.amazon.com/efs/latest/ug/encryption-at-rest.html'
CHECK_CAF_EPIC_extra7161='Data Protection'

extra7161(){
  # "Check if EFS has encryption at rest enabled (Not Scored) (Proposed requirement for 1.5 CIS benchmark)"
  for regx in $REGIONS; do
  LIST_OF_EFS_IDS=$($AWSCLI efs describe-file-systems $PROFILE_OPT --region $regx --query 'FileSystems[*].FileSystemId' --output text 2>&1| xargs -n1 )
  if [[ $(echo "$LIST_OF_EFS_IDS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
      textInfo "$regx: Access Denied trying to describe file systems" "$regx"
      continue
  fi
  if [[ $LIST_OF_EFS_IDS ]]; then
    for efsId in $LIST_OF_EFS_IDS;do
      EFS_ENCRYPTION_CHECK=$($AWSCLI efs $PROFILE_OPT describe-file-systems --region $regx --file-system-id $efsId --output json --query 'FileSystems[*].Encrypted' --output text)
      if [[ $EFS_ENCRYPTION_CHECK == "True" ]]; then
        textPass "$regx: EFS $efsId has has encryption at rest enabled" "$regx" "$efsId"
      else
        textFail "$regx:  EFS: $efsId does not have encryption at rest enabled" "$regx" "$efsId"
      fi
    done
  else
    textInfo "$regx: No EFS found" "$regx"
  fi
 done
}

providers/aws/services/efs/check_extra7148 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7148="7.148"
CHECK_TITLE_extra7148="[extra7148] Check if EFS File systems have backup enabled"
CHECK_SCORED_extra7148="NOT_SCORED"
CHECK_CIS_LEVEL_extra7148="EXTRA"
CHECK_SEVERITY_extra7148="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7148="AwsEfsFileSystem"
CHECK_ALTERNATE_check7148="extra7148"
CHECK_SERVICENAME_extra7148="efs"
CHECK_RISK_extra7148='If backup is not enabled; data is vulnerable. Human error or bad actors could erase or modify data.'
CHECK_REMEDIATION_extra7148='Enable automated backup for production data. Define a retention period and periodically test backup restoration. A Disaster Recovery process should be in place to govern Data Protection approach.'
CHECK_DOC_extra7148='https://docs.aws.amazon.com/efs/latest/ug/whatisefs.html'
CHECK_CAF_EPIC_extra7148='Data Protection'

extra7148() {
  for regx in $REGIONS; do
    LIST_OF_EFS_SYSTEMS=$($AWSCLI efs describe-file-systems $PROFILE_OPT --region $regx --query 'FileSystems[*].FileSystemId' --output text 2>&1)
    if [[ $(echo "$LIST_OF_EFS_SYSTEMS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe file systems" "$regx"
        continue
    fi
    if [[ $LIST_OF_EFS_SYSTEMS ]]; then
      for filesystem in $LIST_OF_EFS_SYSTEMS; do
        # if retention is 0 then is disabled
        BACKUP_POLICY=$($AWSCLI efs describe-backup-policy $PROFILE_OPT --region $regx --file-system-id $filesystem --query BackupPolicy --output text 2>&1)
        if [[ $(echo "$BACKUP_POLICY" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to describe backup policy" "$regx"
          continue
        fi
        if [[ $BACKUP_POLICY == "DISABLED" ]]; then
          textFail "$regx: File system $filesystem does not have backup enabled!" "$regx" "$filesystem"
        else
          textPass "$regx: EFS File system $filesystem has backup enabled" "$regx" "$filesystem"
        fi
      done
    else
      textInfo "$regx: No EFS File systems found" "$regx" "$filesystem"
    fi
  done
}

providers/aws/services/efs/check_extra7143 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7143="7.143"
CHECK_TITLE_extra7143="[extra7143] Check if EFS have policies which allow access to everyone"
CHECK_SCORED_extra7143="NOT_SCORED"
CHECK_CIS_LEVEL_extra7143="EXTRA"
CHECK_SEVERITY_extra7143="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra7143="AwsEFS"
CHECK_ALTERNATE_check7143="extra7143"
CHECK_SERVICENAME_extra7143="efs"
CHECK_RISK_extra7143='EFS accessible to everyone could expose sensitive data to bad actors'
CHECK_REMEDIATION_extra7143='Ensure efs has some policy but it does not have principle as *'
CHECK_DOC_extra7143='https://docs.aws.amazon.com/efs/latest/ug/access-control-block-public-access.html'
CHECK_CAF_EPIC_extra7143='Data Protection'

extra7143(){
  # "Check if EFS have policies which allow access to everyone (Not Scored) (Not part of CIS benchmark)"
  for regx in $REGIONS; do
  LIST_OF_EFS_IDS=$($AWSCLI efs describe-file-systems $PROFILE_OPT --region $regx --query FileSystems[*].FileSystemId --output text 2>&1|xargs -n1 )
  if [[ $(echo "$LIST_OF_EFS_IDS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
      textInfo "$regx: Access Denied trying to describe file systems" "$regx"
      continue
  fi
  if [[ $LIST_OF_EFS_IDS ]]; then
    for efsId in $LIST_OF_EFS_IDS;do
      EFS_POLICY_STATEMENTS=$($AWSCLI efs $PROFILE_OPT describe-file-system-policy --region $regx --file-system-id $efsId --output json --query Policy 2>&1)
      if [[ $EFS_POLICY_STATEMENTS == *PolicyNotFound* ]]; then
        textFail "$regx:  EFS: $efsId doesn't have any policy which means it grants full access to any client" "$regx" "$efsId"
      else
        EFS_POLICY_BAD_STATEMENTS=$(echo $EFS_POLICY_STATEMENTS | jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
        if [[ $EFS_POLICY_BAD_STATEMENTS != "" ]]; then
          textFail "$regx: EFS $efsId has policy which allows access to everyone" "$regx" "$efsId"
        else
          textPass "$regx: EFS $efsId has policy which does not allow access to everyone" "$regx" "$efsId"
        fi
      fi
    done

  else
    textInfo "$regx: No EFS found" "$regx"
  fi
 done
}

providers/aws/services/ecs/check_extra768 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra768="7.68"
CHECK_TITLE_extra768="[extra768] Find secrets in ECS task definitions environment variables "
CHECK_SCORED_extra768="NOT_SCORED"
CHECK_CIS_LEVEL_extra768="EXTRA"
CHECK_SEVERITY_extra768="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra768="AwsEcsTaskDefinition"
CHECK_ALTERNATE_check768="extra768"
CHECK_SERVICENAME_extra768="ecs"
CHECK_RISK_extra768='The use of a hard-coded password increases the possibility of password guessing. If hard-coded passwords are used; it is possible that malicious users gain access through the account in question.'
CHECK_REMEDIATION_extra768='Use Secrets Manager or Parameter Store to securely provide credentials to containers without hardcoding the secrets in code or passing them through environment variables.'
CHECK_DOC_extra768='https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html'
CHECK_CAF_EPIC_extra768='Logging and Monitoring'

extra768(){
  SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM-$PROWLER_START_TIME"
  if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then
    mkdir $SECRETS_TEMP_FOLDER
  fi
  for regx in $REGIONS; do
    CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
    if [[ $? -eq 241 ]]; then
      textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
    else
      # Get a list of all task definition families first:
      FAMILIES=$($AWSCLI ecs list-task-definition-families $PROFILE_OPT --region $regx --status ACTIVE 2>&1 )
      if [[ $(echo "$FAMILIES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to list task definition families" "$regx"
          continue
      fi
      if [[ $(echo $FAMILIES | jq -r .families[]) ]]; then
        for FAMILY in $(echo $FAMILIES | jq -r .families[]);do
          # Get the full task definition arn:
          TASK_DEFINITION_TEMP=$($AWSCLI ecs list-task-definitions $PROFILE_OPT --region $regx --family-prefix $FAMILY --sort DESC --max-items 1 | jq -r .taskDefinitionArns[0])
          # We only care about the task definition name:
          IFS='/' read -r -a splitArn <<< "$TASK_DEFINITION_TEMP"
          TASK_DEFINITION=${splitArn[1]}
          TASK_DEFINITION_ENV_VARIABLES_FILE="$SECRETS_TEMP_FOLDER/extra768-$TASK_DEFINITION-$regx-variables.txt"
          TASK_DEFINITION_ENV_VARIABLES=$($AWSCLI ecs $PROFILE_OPT --region $regx describe-task-definition --task-definition $TASK_DEFINITION --query 'taskDefinition.containerDefinitions[*].environment' --output text > $TASK_DEFINITION_ENV_VARIABLES_FILE)
          if [ -s $TASK_DEFINITION_ENV_VARIABLES_FILE ];then
            # Implementation using https://github.com/Yelp/detect-secrets
            FINDINGS=$(secretsDetector file $TASK_DEFINITION_ENV_VARIABLES_FILE)
            if [[ $FINDINGS -eq 0 ]]; then
              textPass "$regx: No secrets found in ECS task definition $TASK_DEFINITION variables" "$regx" "$TASK_DEFINITION"
              # delete file if nothing interesting is there
              rm -f $TASK_DEFINITION_ENV_VARIABLES_FILE
            else
              textFail "$regx: Potential secret found in ECS task definition $TASK_DEFINITION variables" "$regx" "$TASK_DEFINITION"
            fi
          else
            textInfo "$regx: ECS task definition $TASK_DEFINITION has no variables" "$regx" "$TASK_DEFINITION"
            rm -f $TASK_DEFINITION_ENV_VARIABLES_FILE
          fi
        done
      else
        textInfo "$regx: No ECS task definitions found" "$regx"
      fi
    fi
  done
}

providers/aws/services/ecr/check_extra776 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation:
#
#   https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html
#
#   aws ecr put-image-scanning-configuration \
#     --region <value> \
#     --repository-name <value> \
#     --image-scanning-configuration scanOnPush=true
#
#   aws ecr describe-image-scan-findings \
#     --region <value> \
#     --repository-name <value>
#     --image-id imageTag=<value>

CHECK_ID_extra776="7.76"
CHECK_TITLE_extra776="[extra776] Check if ECR image scan found vulnerabilities in the newest image version"
CHECK_SCORED_extra776="NOT_SCORED"
CHECK_CIS_LEVEL_extra776="EXTRA"
CHECK_SEVERITY_extra776="Medium"
CHECK_ALTERNATE_check776="extra776"
CHECK_SERVICENAME_extra776="ecr"
CHECK_ASFF_RESOURCE_TYPE_extra776="AwsEcrRepository"
CHECK_RISK_extra776='Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings. '
CHECK_REMEDIATION_extra776='Open the Amazon ECR console. Then look for vulnerabilities and fix them.'
CHECK_DOC_extra776='https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html#describe-scan-findings'
CHECK_CAF_EPIC_extra776='Logging and Monitoring'

extra776(){
  for region in ${REGIONS}; do
    # List ECR repositories
    LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region "${region}" --query "repositories[*].[repositoryName]" --output text 2>&1)
    # Handle authorization errors
    if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "$LIST_ECR_REPOS"; then
      textInfo "$region: Access Denied trying to describe ECR repositories" "$region"
      continue
    fi
    if [[ -n "$LIST_ECR_REPOS" ]]; then
      for repo in $LIST_ECR_REPOS; do
        # Check if the repository has scanOnPush enabled
        SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region "${region}" --query "repositories[?repositoryName=='${repo}'].[imageScanningConfiguration.scanOnPush]" --output text 2>&1)
        if [[ "${SCAN_ENABLED}" == "True" ]]; then
          # Recover newest image digest
          NEWEST_IMAGE_DIGEST=$($AWSCLI ecr describe-images $PROFILE_OPT --region "${region}" --repository-name "${repo}" --query "sort_by(imageDetails,& imagePushedAt)[-1].imageDigest" --output text | head -n 1 2>&1)
          if [[ "${NEWEST_IMAGE_DIGEST}" != *"None"* ]]; then
            # Recover newest image tag
            NEWEST_IMAGE_TAG=$($AWSCLI ecr describe-images $PROFILE_OPT --region "${region}" --repository-name "${repo}" --query "sort_by(imageDetails,& imagePushedAt)[-1].imageTags[0]" --output text | head -n 1 2>&1)
            if [[ -n "${LIST_ECR_REPOS}" ]]; then
              # For this newest digest, recover the last scan status
              IMAGE_SCAN_STATUS=$($AWSCLI ecr describe-image-scan-findings $PROFILE_OPT --region "${region}" --repository-name "${repo}" --image-id imageDigest="${NEWEST_IMAGE_DIGEST}" --query "imageScanStatus.status" --output text 2>&1)
              if [[ "${IMAGE_SCAN_STATUS}" == *"ScanNotFoundException"* ]]; then
                textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} without a scan" "${region}" "${repo}"
              else
                if [[ "${IMAGE_SCAN_STATUS}" == *"FAILED"* ]]; then
                  textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with scan status ${IMAGE_SCAN_STATUS}" "${region}" "${repo}"
                else
                  # For this newest digest, recover the number of findings found
                  # This search needs a JSON response to match against severity
                  FINDINGS_COUNT=$($AWSCLI ecr describe-image-scan-findings $PROFILE_OPT --region "${region}" --repository-name "${repo}" --image-id imageDigest="${NEWEST_IMAGE_DIGEST}" --query "imageScanFindings.findingSeverityCounts" --output json 2>&1)
                  if [[ -n "${FINDINGS_COUNT}" ]]; then
                      SEVERITY_CRITICAL=$(jq -r '.CRITICAL' <<< "${FINDINGS_COUNT}")
                      if [[ "${SEVERITY_CRITICAL}" != "null" ]]; then
                        textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with CRITICAL ($SEVERITY_CRITICAL) findings" "${region}" "${repo}"
                      fi
                      SEVERITY_HIGH=$(jq -r '.HIGH' <<< "${FINDINGS_COUNT}")
                      if [[ "${SEVERITY_HIGH}" != "null" ]]; then
                        textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with HIGH ($SEVERITY_HIGH) findings" "${region}" "${repo}"
                      fi
                      SEVERITY_MEDIUM=$(jq -r '.MEDIUM' <<< "${FINDINGS_COUNT}")
                      if [[ "${SEVERITY_MEDIUM}" != "null" ]]; then
                        textFail "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with MEDIUM ($SEVERITY_MEDIUM) findings" "${region}" "${repo}"
                      fi
                      SEVERITY_LOW=$(jq -r '.LOW' <<< "${FINDINGS_COUNT}")
                      if [[ "${SEVERITY_LOW}" != "null" ]]; then
                        textInfo "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with LOW ($SEVERITY_LOW) findings" "${region}" "${repo}"
                      fi
                      SEVERITY_INFORMATIONAL=$(jq -r '.INFORMATIONAL' <<< "${FINDINGS_COUNT}")
                      if [[ "${SEVERITY_INFORMATIONAL}" != "null" ]]; then
                        textInfo "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with INFORMATIONAL ($SEVERITY_INFORMATIONAL) findings" "${region}" "${repo}"
                      fi
                      SEVERITY_UNDEFINED=$(jq -r '.UNDEFINED' <<< "${FINDINGS_COUNT}")
                      if [[ "${SEVERITY_UNDEFINED}" != "null" ]]; then
                        textInfo "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} with UNDEFINED ($SEVERITY_UNDEFINED) findings" "${region}" "${repo}"
                      fi
                  else
                    textPass "${region}: ECR repository ${repo} has imageTag ${NEWEST_IMAGE_TAG} without findings" "${region}" "${repo}"
                  fi
                fi
              fi
            fi
          else
            textInfo "${region}: ECR repository ${repo} has no images" "${region}"
          fi
        else
          textInfo "${region}: ECR repository ${repo} has scanOnPush disabled" "${region}" "${repo}"
        fi
      done
    else
      textInfo "${region}: No ECR repositories found" "${region}"
    fi
  done
}

providers/aws/services/ecr/check_extra77 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra77="7.7"
CHECK_TITLE_extra77="[extra77] Ensure there are no ECR repositories set as Public"
CHECK_SCORED_extra77="NOT_SCORED"
CHECK_CIS_LEVEL_extra77="EXTRA"
CHECK_SEVERITY_extra77="Critical"
CHECK_ALTERNATE_extra707="extra77"
CHECK_ALTERNATE_check77="extra77"
CHECK_ALTERNATE_check707="extra77"
CHECK_SERVICENAME_extra77="ecr"
CHECK_RISK_extra77='Policy may allow Anonymous users to perform actions.'
CHECK_REMEDIATION_extra77='Ensure this repository and its contents should be publicly accessible.'
CHECK_DOC_extra77='https://docs.aws.amazon.com/AmazonECR/latest/public/security_iam_service-with-iam.html'
CHECK_CAF_EPIC_extra77='Data Protection'

extra77(){
  # "Ensure there are no ECR repositories set as Public "
  for regx in $REGIONS; do
    LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[*].[repositoryName]" --output text 2>&1)
    if [[ $(echo "$LIST_ECR_REPOS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied Trying to describe ECR repositories" "$regx" "$repo"
      continue
    fi
    if [[ $(echo "$LIST_ECR_REPOS" | grep SubscriptionRequiredException) ]]; then
      textInfo "$regx: Subscription Required Exception trying to describe ECR repositories" "$regx" "$repo"
      continue
    fi
    if [[ ! -z "$LIST_ECR_REPOS" ]]; then
      for repo in $LIST_ECR_REPOS; do
        TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-ecr-repo.policy.XXXXXXXXXX)
        $AWSCLI ecr get-repository-policy $PROFILE_OPT --region $regx --repository-name $repo --query "policyText" --output text > $TEMP_POLICY_FILE 2>&1
        if [[ $(grep AccessDenied $TEMP_POLICY_FILE) ]]; then
          textInfo "$regx: Access Denied to get repository policy for repo $repo" "$regx" "$repo"
          rm -f $TEMP_POLICY_FILE
          continue
        fi
        # https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policies.html - "By default, only the repository owner has access to a repository."
        if [[ $(grep RepositoryPolicyNotFoundException $TEMP_POLICY_FILE) ]]; then
          textPass "$regx: $repo is not open" "$regx" "$repo"
          rm -f $TEMP_POLICY_FILE
          continue
        fi
        # check if the policy has Principal as *
        CHECK_ECR_REPO_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | jq '.Statement[]|select(.Effect=="Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")))')
        if [[ $CHECK_ECR_REPO_ALLUSERS_POLICY ]]; then
          textFail "$regx: $repo policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx"
        else
          textPass "$regx: $repo is not open" "$regx" "$repo"
        fi
        rm -f $TEMP_POLICY_FILE
      done
    else
      textInfo "$regx: No ECR repositories found" "$regx" "$repo"
    fi
  done
}

providers/aws/services/ecr/check_extra765 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation:
#
#   https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html
#
#   aws ecr put-image-scanning-configuration \
#     --region <value> \
#     --repository-name <value> \
#     --image-scanning-configuration scanOnPush=true

CHECK_ID_extra765="7.65"
CHECK_TITLE_extra765="[extra765] Check if ECR image scan on push is enabled "
CHECK_SCORED_extra765="NOT_SCORED"
CHECK_CIS_LEVEL_extra765="EXTRA"
CHECK_SEVERITY_extra765="Medium"
CHECK_ALTERNATE_check765="extra765"
CHECK_SERVICENAME_extra765="ecr"
CHECK_RISK_extra765='Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings. '
CHECK_REMEDIATION_extra765='Enable ECR image scanning and review the scan findings for information about the security of the container images that are being deployed.'
CHECK_DOC_extra765='https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html'
CHECK_CAF_EPIC_extra765='Infrastructure Security'

extra765(){
  for region in $REGIONS; do
    LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[*].[repositoryName]" --output text 2>&1)
    if [[ $(echo "$LIST_ECR_REPOS" | grep AccessDenied) ]]; then
      textInfo "$region: Access Denied Trying to describe ECR repositories" "$region"
      continue
    fi
    if [[ ! -z "$LIST_ECR_REPOS" ]]; then
      for repo in $LIST_ECR_REPOS; do
        SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text 2>&1)
        case "$SCAN_ENABLED" in
          "True")
            textPass "$region: ECR repository $repo has scan on push enabled" "$region" "$repo"
            ;;
          "False")
            textFail "$region: ECR repository $repo has scan on push disabled!" "$region" "$repo"
            ;;
          "None")
            textInfo "$region: ECR repository $repo has no scanOnPush status: newer awscli needed" "$region" "$repo"
            ;;
          "*")
            textInfo "$region: ECR repository $repo has unknown scanOnPush status \"$SCAN_ENABLED\"" "$region" "$repo"
            ;;
        esac
      done
    else
      textInfo "$region: No ECR repositories found" "$region"
    fi
  done
}

providers/aws/services/ec2/check_extra786 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra786="7.86"
CHECK_TITLE_extra786="[extra786] Check if EC2 Instance Metadata Service Version 2 (IMDSv2) is Enabled and Required "
CHECK_SCORED_extra786="NOT_SCORED"
CHECK_CIS_LEVEL_extra786="EXTRA"
CHECK_SEVERITY_extra786="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra786="AwsEc2Instance"
CHECK_ALTERNATE_check786="extra786"
CHECK_SERVICENAME_extra786="ec2"
CHECK_RISK_extra786='Using IMDSv2 will protect from misconfiguration and SSRF vulnerabilities. IMDSv1 will not.'
CHECK_REMEDIATION_extra786='If you don’t need IMDS you can turn it off. Using aws-cli you can force the instance to use only IMDSv2.'
CHECK_DOC_extra786='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html#configuring-instance-metadata-options'
CHECK_CAF_EPIC_extra786='Infrastructure Security'

extra786(){
  for regx in $REGIONS; do
    TEMP_EXTRA786_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.EXTRA786.XXXXXXXXXX)
    $AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx \
      --query 'Reservations[*].Instances[*].{HttpTokens:MetadataOptions.HttpTokens,HttpEndpoint:MetadataOptions.HttpEndpoint,InstanceId:InstanceId}' \
      --output text --max-items $MAXITEMS > $TEMP_EXTRA786_FILE 2>&1
    if [[ $(cat $TEMP_EXTRA786_FILE | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe instances" "$regx"
        continue
    fi
    # if the file contains data, there are instances in that region
    if [[ -s "$TEMP_EXTRA786_FILE" ]];then
      # here we read content from the file fields instanceid httptokens_status httpendpoint
      while read httpendpoint httptokens_status instanceid  ; do
        #echo i:$instanceid tok:$httptokens_status end:$httpendpoint
        if [[ "$httpendpoint" == "enabled" &&  "$httptokens_status" == "required" ]];then
          textPass "$regx: EC2 Instance $instanceid has IMDSv2 enabled and required" "$regx" "$instanceid"
        elif [[ "$httpendpoint" == "disabled" ]];then
          textInfo "$regx: EC2 Instance $instanceid has HTTP endpoint access to metadata service disabled" "$regx"
        else
          textFail "$regx: EC2 Instance $instanceid has IMDSv2 disabled or not required" "$regx" "$instanceid"
        fi
      done < <(cat $TEMP_EXTRA786_FILE)
    else
     textInfo "$regx: no EC2 Instances found" "$regx"
    fi
    rm -fr $TEMP_EXTRA786_FILE
  done
}

# Remediation:

# aws ec2 modify-instance-metadata-options \
#     --instance-id i-1234567898abcdef0 \
#     --http-tokens required \
#     --http-endpoint enabled

# More information here https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html

providers/aws/services/ec2/check_extra779 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra779="7.79"
CHECK_TITLE_extra779="[extra779] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Elasticsearch/Kibana ports"
CHECK_SCORED_extra779="NOT_SCORED"
CHECK_CIS_LEVEL_extra779="EXTRA"
CHECK_SEVERITY_extra779="High"
CHECK_ASFF_RESOURCE_TYPE_extra779="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check779="extra779"
CHECK_SERVICENAME_extra779="ec2"
CHECK_RISK_extra779='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra779='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra779='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra779='Infrastructure Security'

extra779(){
  ES_API_PORT="9200"
  ES_DATA_PORT="9300"
  ES_KIBANA_PORT="5601"
  # Test connectivity and authentication is performed by check extra787
  for regx in $REGIONS; do
    # crate a list of SG open to the world with port $ES_API_PORT or $ES_DATA_PORT or $ES_KIBANA_PORT
    SG_LIST=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --output text \
      --query "SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=\`$ES_API_PORT\` && ToPort>=\`$ES_API_PORT\`) || (FromPort<=\`$ES_DATA_PORT\` && ToPort>=\`$ES_DATA_PORT\`) || (FromPort<=\`$ES_KIBANA_PORT\` && ToPort>=\`$ES_KIBANA_PORT\`)) && (contains(IpRanges[].CidrIp, \`0.0.0.0/0\`) || contains(Ipv6Ranges[].CidrIpv6, \`::/0\`))]) > \`0\`].{GroupId:GroupId}" 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    # in case of open security groups goes through each one
    if [[ $SG_LIST ]];then
      for sg in $SG_LIST;do
        # temp file store the list of instances IDs and public IP address if found
        TEMP_EXTRA779_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.EXTRA779.XXXXXXXXXX)
        # finds instances with that open security group attached and get its public ip address (if it has one)
        $AWSCLI $PROFILE_OPT --region $regx ec2 describe-instances --filters Name=instance.group-id,Values=$sg --query 'Reservations[*].Instances[*].[InstanceId,PublicIpAddress]' --output text > $TEMP_EXTRA779_FILE
        # in case of exposed instances it does access checks
        if [[ -s "$TEMP_EXTRA779_FILE" ]];then
          while read instance eip ; do
            if [[ "$eip" == "None" ]];then
              textInfo "$regx: Found instance $instance with private IP on Security Group: $sg" "$regx"
            else
              textFail "$regx: Found instance $instance with public IP $eip on Security Group: $sg open to 0.0.0.0/0 on for Elasticsearch/Kibana ports - use extra787 to test AUTH" "$regx" "$sg"
            fi
          done < <(cat $TEMP_EXTRA779_FILE)
        fi
        rm -rf $TEMP_EXTRA779_FILE
      done
    else
      textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Elasticsearch/Kibana ports" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra778 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra778="7.78"
CHECK_TITLE_extra778="[extra778] Find VPC security groups with wide-open public IPv4 CIDR ranges (non-RFC1918) "
CHECK_SCORED_extra778="NOT_SCORED"
CHECK_CIS_LEVEL_extra778="EXTRA"
CHECK_SEVERITY_extra778="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra778="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check778="extra778"
CHECK_SERVICENAME_extra778="ec2"
CHECK_RISK_extra778='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra778='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra778='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra778='Infrastructure Security'

extra778(){
    CIDR_THRESHOLD=24
    RFC1918_REGEX="(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)"

    check_cidr() {
        local SECURITY_GROUP=$1
        local DIRECTION=$2
        local DIRECTION_FILTER=""
        local REGION=$3

        case ${DIRECTION} in
            "inbound")
                DIRECTION_FILTER="IpPermissions"
                ;;
            "outbound")
                DIRECTION_FILTER="IpPermissionsEgress"
                ;;
        esac

        CIDR_IP_LIST=$(${AWSCLI} ec2 describe-security-groups \
                        ${PROFILE_OPT} \
                        --filter "Name=group-id,Values=${SECURITY_GROUP}" \
                        --query "SecurityGroups[*].${DIRECTION_FILTER}[*].IpRanges[*].CidrIp" \
                        --region ${REGION} \
                        --output text 2>&1| xargs )
        if [[ $(echo "$CIDR_IP_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to describe security groups" "$regx"
            continue
        fi

        for CIDR_IP in ${CIDR_IP_LIST}; do
            if [[ ! ${CIDR_IP} =~ ${RFC1918_REGEX} ]]; then
                CIDR=$(echo ${CIDR_IP} | cut -d"/" -f2 | xargs)

                # Edge case "0.0.0.0/0" for RDP and SSH are checked already by check41 and check42
                if [[ ${CIDR} < ${CIDR_THRESHOLD} && 0 < ${CIDR} ]]; then
                    textFail "${REGION}: ${SECURITY_GROUP} has potential wide-open non-RFC1918 address ${CIDR_IP} in ${DIRECTION} rule" "${REGION}" "${SECURITY_GROUP}"
                else
                    textPass "${REGION}: ${SECURITY_GROUP} has no potential wide-open non-RFC1918 address" "${REGION}" "${SECURITY_GROUP}"
                fi
            fi
        done
    }

    for regx in ${REGIONS}; do
        SECURITY_GROUP_IDS=$(${AWSCLI} ec2 describe-security-groups \
                              ${PROFILE_OPT} \
                              --region ${regx} \
                              --query 'SecurityGroups[*].GroupId' \
                              --output text 2>&1| xargs )
        if [[ $(echo "$SECURITY_GROUP_IDS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to describe security groups" "$regx"
            continue
        fi
        for SECURITY_GROUP in ${SECURITY_GROUP_IDS}; do
            check_cidr "${SECURITY_GROUP}" "inbound" "${regx}"
            check_cidr "${SECURITY_GROUP}" "outbound" "${regx}"
        done
    done
}

providers/aws/services/ec2/check_extra777 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Current VPC Limit is 120 rules (60 inbound and 60 outbound)
# Reference: https://docs.aws.amazon.com/vpc/latest/userguide/amazon-vpc-limits.html

CHECK_ID_extra777="7.77"
CHECK_TITLE_extra777="[extra777] Find VPC security groups with more than 50 ingress or egress rules "
CHECK_SCORED_extra777="NOT_SCORED"
CHECK_CIS_LEVEL_extra777="EXTRA"
CHECK_SEVERITY_extra777="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra777="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check777="extra777"
CHECK_SERVICENAME_extra777="ec2"
CHECK_RISK_extra777='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra777='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra777='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra777='Infrastructure Security'

extra777(){
    THRESHOLD=50

    for regx in ${REGIONS}; do
        SECURITY_GROUP_IDS=$(${AWSCLI} ec2 describe-security-groups \
                             ${PROFILE_OPT} \
                             --region ${regx} \
                             --query 'SecurityGroups[*].GroupId' \
                             --output text 2>&1| xargs )
        if [[ $(echo "$SECURITY_GROUP_IDS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to describe security groups" "$regx"
            continue
        fi

        for SECURITY_GROUP in ${SECURITY_GROUP_IDS}; do

            INGRESS_TOTAL=$(${AWSCLI} ec2 describe-security-groups \
                            ${PROFILE_OPT} \
                            --filter "Name=group-id,Values=${SECURITY_GROUP}" \
                            --query "SecurityGroups[*].IpPermissions[*].IpRanges" \
                            --region ${regx} \
                            --output text | wc -l | xargs
                            )

            EGRESS_TOTAL=$(${AWSCLI} ec2 describe-security-groups \
                            ${PROFILE_OPT} \
                            --filter "Name=group-id,Values=${SECURITY_GROUP}" \
                            --query "SecurityGroups[*].IpPermissionsEgress[*].IpRanges" \
                            --region ${regx} \
                            --output text | wc -l | xargs
                            )

            if [[ (${INGRESS_TOTAL} -ge ${THRESHOLD}) || (${EGRESS_TOTAL} -ge ${THRESHOLD}) ]]; then
                textFail "${regx}: ${SECURITY_GROUP} has ${INGRESS_TOTAL} inbound rules and ${EGRESS_TOTAL} outbound rules" "${regx}" "${SECURITY_GROUP}"
            else
                textPass "${regx}: ${SECURITY_GROUP} has ${INGRESS_TOTAL} inbound rules and ${EGRESS_TOTAL} outbound rules" "${regx}" "${SECURITY_GROUP}"
            fi
        done
    done
}

providers/aws/services/ec2/check_extra772 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra772="7.72"
CHECK_TITLE_extra772="[extra772] Check if elastic IPs are unused "
CHECK_SCORED_extra772="NOT_SCORED"
CHECK_CIS_LEVEL_extra772="EXTRA"
CHECK_SEVERITY_extra772="Low"
CHECK_ASFF_RESOURCE_TYPE_extra772="AwsEc2Eip"
CHECK_ALTERNATE_check772="extra772"
CHECK_SERVICENAME_extra772="ec2"
CHECK_RISK_extra772='You are charged by the hour for each Elastic IP address that are not attached to an EC2 instance .'
CHECK_REMEDIATION_extra772='If you don’t need an Elastic IP address; you can stop the charges by releasing the IP address.'
CHECK_DOC_extra772='https://aws.amazon.com/premiumsupport/knowledge-center/elastic-ip-charges/'
CHECK_CAF_EPIC_extra772='Infrastructure Security'

extra772(){
  for region in $REGIONS; do
    EIP_DUMP=$($AWSCLI ec2 describe-addresses ${PROFILE_OPT} --region $region --output json 2>&1)
    if [[ $(echo "$EIP_DUMP" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe addresses" "$regx"
        continue
    fi
    EIP_LIST=$(echo $EIP_DUMP | jq -r '.Addresses[].AllocationId')
    if [[ $EIP_LIST ]]; then
      for eip in $EIP_LIST; do
        ASSOCIATION_ID=$(echo $EIP_DUMP | jq -r --arg i "$eip" '.Addresses[]|select(.AllocationId==$i)|.AssociationId')
        if [[ "$ASSOCIATION_ID" == "null" ]]; then
          textFail "$region: EIP $eip is unused" "$region" "$eip"
        else
          textPass "$region: EIP $eip is used" "$region" "$eip"
        fi
      done
    else
      textInfo "$region: No Elastic IPs found" $region
    fi
  done
}

providers/aws/services/ec2/check_extra770 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra770="7.70"
CHECK_TITLE_extra770="[extra770] Check for internet facing EC2 instances with Instance Profiles attached "
CHECK_SCORED_extra770="NOT_SCORED"
CHECK_CIS_LEVEL_extra770="EXTRA"
CHECK_SEVERITY_extra770="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra770="AwsEc2Instance"
CHECK_ALTERNATE_check770="extra770"
CHECK_SERVICENAME_extra770="ec2"
CHECK_RISK_extra770='Exposing an EC2 directly to internet increases the attack surface and therefore the risk of compromise.'
CHECK_REMEDIATION_extra770='Use an ALB and apply WAF ACL.'
CHECK_DOC_extra770='https://aws.amazon.com/blogs/aws/aws-web-application-firewall-waf-for-application-load-balancers/'
CHECK_CAF_EPIC_extra770='Infrastructure Security'

extra770(){
  # "Check for internet facing EC2 Instances "
  for regx in $REGIONS; do
    LIST_OF_PUBLIC_INSTANCES_WITH_INSTANCE_PROFILES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query 'Reservations[*].Instances[?((IamInstanceProfile!=`null` && PublicIpAddress!=`null`))].[InstanceId,PublicIpAddress,IamInstanceProfile.Arn]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_PUBLIC_INSTANCES_WITH_INSTANCE_PROFILES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe instances" "$regx"
        continue
    fi
    if [[ $LIST_OF_PUBLIC_INSTANCES_WITH_INSTANCE_PROFILES ]];then
      while read -r instance;do
        INSTANCE_ID=$(echo $instance | awk '{ print $1; }')
        PUBLIC_IP=$(echo $instance | awk '{ print $2; }')
        INSTANCE_PROFILE=$(echo $instance | awk '{ print $3; }')
        textFail "$regx: Instance: $INSTANCE_ID at IP: $PUBLIC_IP is internet-facing with Instance Profile $INSTANCE_PROFILE" "$regx" "$INSTANCE_ID"
      done <<< "$LIST_OF_PUBLIC_INSTANCES_WITH_INSTANCE_PROFILES"
      else
        textPass "$regx: no Internet Facing EC2 Instances with Instance Profiles found" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra761 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra761="7.61"
CHECK_TITLE_extra761="[extra761] Check if EBS Default Encryption is activated "
CHECK_SCORED_extra761="NOT_SCORED"
CHECK_CIS_LEVEL_extra761="EXTRA"
CHECK_SEVERITY_extra761="Medium"
CHECK_ALTERNATE_check761="extra761"
CHECK_ASFF_COMPLIANCE_TYPE_extra761="ens-mp.info.3.aws.ebs.2"
CHECK_SERVICENAME_extra761="ec2"
CHECK_RISK_extra761='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra761='Enable Encryption. Use a CMK where possible. It will provide additional management and privacy benefits.'
CHECK_DOC_extra761='https://aws.amazon.com/premiumsupport/knowledge-center/ebs-automatic-encryption/'
CHECK_CAF_EPIC_extra761='Data Protection'

extra761(){
  for regx in $REGIONS; do
    EBS_DEFAULT_ENCRYPTION=$($AWSCLI ec2 get-ebs-encryption-by-default $PROFILE_OPT --region $regx --query 'EbsEncryptionByDefault' --output text 2>&1)
    if [[ $(echo "$EBS_DEFAULT_ENCRYPTION" | grep "argument operation: Invalid choice") ]]; then
      textInfo "$regx: Newer aws cli needed for get-ebs-encryption-by-default" "$regx"
      continue
    fi
    if [[ $(echo "$EBS_DEFAULT_ENCRYPTION" | grep UnauthorizedOperation) ]]; then
      textInfo "$regx: Prowler needs ec2:GetEbsEncryptionByDefault permission for this check" "$regx"
      continue
    fi
    if [[ $EBS_DEFAULT_ENCRYPTION == "True" ]];then
      textPass "$regx: EBS Default Encryption is activated" "$regx"
    else
      textFail "$regx: EBS Default Encryption is not activated" "$regx"
    fi
    done
}

providers/aws/services/ec2/check_extra76 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra76="7.6"
CHECK_TITLE_extra76="[extra76] Ensure there are no EC2 AMIs set as Public"
CHECK_SCORED_extra76="NOT_SCORED"
CHECK_CIS_LEVEL_extra76="EXTRA"
CHECK_SEVERITY_extra76="Critical"
CHECK_ALTERNATE_extra706="extra76"
CHECK_ALTERNATE_check76="extra76"
CHECK_ALTERNATE_check706="extra76"
CHECK_SERVICENAME_extra76="ec2"
CHECK_RISK_extra76='A shared AMI is an AMI that a developer created and made available for other developers to use. If AMIs have embebed information about the environment could pose a security risk. You use a shared AMI at your own risk. Amazon can not vouch for the integrity or security of AMIs shared by Amazon EC2 users. '
CHECK_REMEDIATION_extra76='List all shared AMIs and make sure there is a business reason for them.'
CHECK_DOC_extra76='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/usingsharedamis-finding.html'
CHECK_CAF_EPIC_extra76='Infrastructure Security'

extra76(){
  # "Ensure there are no EC2 AMIs set as Public "
  for regx in $REGIONS; do
    LIST_OF_PUBLIC_AMIS=$($AWSCLI ec2 describe-images --owners self $PROFILE_OPT --region $regx --filters "Name=is-public,Values=true" --query 'Images[*].{ID:ImageId}' --output text 2>&1)
    if [[ $(echo "$LIST_OF_PUBLIC_AMIS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe images" "$regx"
        continue
    fi
    if [[ $LIST_OF_PUBLIC_AMIS ]];then
      for ami in $LIST_OF_PUBLIC_AMIS; do
        textFail "$regx: $ami is currently Public!" "$regx" "$ami"
      done
    else
      textPass "$regx: No Public AMIs found" "$regx" "$ami"
    fi
  done
}

providers/aws/services/ec2/check_extra758 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra758="7.58"
CHECK_TITLE_extra758="[extra758] Check EC2 Instances older than 12 months "
CHECK_SCORED_extra758="NOT_SCORED"
CHECK_CIS_LEVEL_extra758="EXTRA"
CHECK_SEVERITY_extra758="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra758="AwsEc2Instance"
CHECK_ALTERNATE_check758="extra758"
CHECK_SERVICENAME_extra758="ec2"
CHECK_RISK_extra758='Having old instances within your AWS account could increase the risk of having vulnerable software.'
CHECK_REMEDIATION_extra758='Check if software running in the instance is up to date and patched accordingly. Use AWS Systems Manager to patch instances and view patching compliance information.'
CHECK_DOC_extra758='https://docs.aws.amazon.com/systems-manager/latest/userguide/viewing-patch-compliance-results.html'
CHECK_CAF_EPIC_extra758='Infrastructure Security'

extra758(){
  # OLDAGE has the following format: YYYY-MM-DD
  OLDAGE="$(get_date_previous_than_months 12)"
  for regx in ${REGIONS}; do
    INSTACES_OLD_THAN_AGE=$("${AWSCLI}" ec2 describe-instances --query "Reservations[].Instances[?LaunchTime<='${OLDAGE}'][].[InstanceId, LaunchTime, State.Name]" ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
    if [[ $(echo "${INSTACES_OLD_THAN_AGE}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "${regx}: Access Denied trying to describe instances" "${regx}"
        continue
    fi
    if [[ "${INSTACES_OLD_THAN_AGE}" ]]; then
      while read -r EC2_ID LAUNCH_DATE STATE
      do
        textFail "${regx}: EC2 Instance ${EC2_ID} launched before than ${OLDAGE}. Launch date: ${LAUNCH_DATE} - State: ${STATE}" "${regx}" "${EC2_ID}"
      done <<< "${INSTACES_OLD_THAN_AGE}"
    else
      textPass "${regx}: No EC2 Instances found older than 12 months" "${regx}"
    fi
  done
}

providers/aws/services/ec2/check_extra757 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra757="7.57"
CHECK_TITLE_extra757="[extra757] Check EC2 Instances older than 6 months"
CHECK_SCORED_extra757="NOT_SCORED"
CHECK_CIS_LEVEL_extra757="EXTRA"
CHECK_SEVERITY_extra757="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra757="AwsEc2Instance"
CHECK_ALTERNATE_check757="extra757"
CHECK_SERVICENAME_extra757="ec2"
CHECK_RISK_extra757='Having old instances within your AWS account could increase the risk of having vulnerable software.'
CHECK_REMEDIATION_extra757='Check if software running in the instance is up to date and patched accordingly. Use AWS Systems Manager to patch instances and view patching compliance information.'
CHECK_DOC_extra757='https://docs.aws.amazon.com/systems-manager/latest/userguide/viewing-patch-compliance-results.html'
CHECK_CAF_EPIC_extra757='Infrastructure Security'

extra757(){
  OLDAGE="$(get_date_previous_than_months 6)"
  for regx in $REGIONS; do
    EC2_RUNNING=$($AWSCLI ec2 describe-instances --query "Reservations[*].Instances[*].[InstanceId]" $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$EC2_RUNNING" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe instances" "$regx"
        continue
    fi
    if [[ $EC2_RUNNING ]]; then
      INSTACES_OLD_THAN_AGE=$($AWSCLI ec2 describe-instances --query "Reservations[].Instances[?LaunchTime<='$OLDAGE'][].{id: InstanceId, launched: LaunchTime}" $PROFILE_OPT --region $regx --output text)
      if [[ $INSTACES_OLD_THAN_AGE ]]; then
        while IFS= read -r ec2_instace
        do
          EC2_ID=$(echo "$ec2_instace" | awk '{print $1}')
          LAUNCH_DATE=$(echo "$ec2_instace" | awk '{print $2}')
          textFail "$regx: EC2 Instance $EC2_ID running before than $OLDAGE" "$regx" "$EC2_ID"
        done <<< "$INSTACES_OLD_THAN_AGE"
      else
        textPass "$regx: All Instances newer than 6 months" "$regx"
      fi
    else
      textInfo "$regx: No EC2 Instances Found" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra755 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra755="7.55"
CHECK_TITLE_extra755="[extra755] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Memcached port 11211"
CHECK_SCORED_extra755="NOT_SCORED"
CHECK_CIS_LEVEL_extra755="EXTRA"
CHECK_SEVERITY_extra755="High"
CHECK_ASFF_RESOURCE_TYPE_extra755="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check755="extra755"
CHECK_ASFF_COMPLIANCE_TYPE_extra755="ens-mp.com.4.aws.sg.12"
CHECK_SERVICENAME_extra755="ec2"
CHECK_RISK_extra755='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra755='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra755='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra755='Infrastructure Security'

extra755(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`11211` && ToPort>=`11211`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Memcached port" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Memcached port" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra754 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra754="7.54"
CHECK_TITLE_extra754="[extra754] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Cassandra ports 7199 or 9160 or 8888"
CHECK_SCORED_extra754="NOT_SCORED"
CHECK_CIS_LEVEL_extra754="EXTRA"
CHECK_SEVERITY_extra754="High"
CHECK_ASFF_RESOURCE_TYPE_extra754="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check754="extra754"
CHECK_ASFF_COMPLIANCE_TYPE_extra754="ens-mp.com.4.aws.sg.11"
CHECK_SERVICENAME_extra754="ec2"
CHECK_RISK_extra754='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra754='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra754='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra754='Infrastructure Security'

extra754(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || ((FromPort<=`7199` && ToPort>=`7199`) || (FromPort<=`9160` && ToPort>=`9160`)|| (FromPort<=`8888` && ToPort>=`8888`))) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Cassandra ports" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Cassandra ports" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra753 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra753="7.53"
CHECK_TITLE_extra753="[extra753] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MongoDB ports 27017 and 27018"
CHECK_SCORED_extra753="NOT_SCORED"
CHECK_CIS_LEVEL_extra753="EXTRA"
CHECK_SEVERITY_extra753="High"
CHECK_ASFF_RESOURCE_TYPE_extra753="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check753="extra753"
CHECK_ASFF_COMPLIANCE_TYPE_extra753="ens-mp.com.4.aws.sg.10"
CHECK_SERVICENAME_extra753="ec2"
CHECK_RISK_extra753='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra753='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra753='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra753='Infrastructure Security'

extra753(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || ((FromPort<=`27017` && ToPort>=`27017`) || (FromPort<=`27018` && ToPort>=`27018`))) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for MongoDB ports" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found open to 0.0.0.0/0 for MongoDB ports" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra752 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra752="7.52"
CHECK_TITLE_extra752="[extra752] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Redis port 6379"
CHECK_SCORED_extra752="NOT_SCORED"
CHECK_CIS_LEVEL_extra752="EXTRA"
CHECK_SEVERITY_extra752="High"
CHECK_ASFF_RESOURCE_TYPE_extra752="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check752="extra752"
CHECK_ASFF_COMPLIANCE_TYPE_extra752="ens-mp.com.4.aws.sg.9"
CHECK_SERVICENAME_extra752="ec2"
CHECK_RISK_extra752='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra752='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra752='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra752='Infrastructure Security'

extra752(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`6379` && ToPort>=`6379`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Redis port" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Redis port" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra751 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra751="7.51"
CHECK_TITLE_extra751="[extra751] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Postgres port 5432"
CHECK_SCORED_extra751="NOT_SCORED"
CHECK_CIS_LEVEL_extra751="EXTRA"
CHECK_SEVERITY_extra751="High"
CHECK_ASFF_RESOURCE_TYPE_extra751="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check751="extra751"
CHECK_ASFF_COMPLIANCE_TYPE_extra751="ens-mp.com.4.aws.sg.8"
CHECK_SERVICENAME_extra751="ec2"
CHECK_RISK_extra751='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra751='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra751='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra751='Infrastructure Security'

extra751(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`5432` && ToPort>=`5432`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Postgres port" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Postgres port" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra750 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra750="7.50"
CHECK_TITLE_extra750="[extra750] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MySQL port 3306"
CHECK_SCORED_extra750="NOT_SCORED"
CHECK_CIS_LEVEL_extra750="EXTRA"
CHECK_SEVERITY_extra750="High"
CHECK_ASFF_RESOURCE_TYPE_extra750="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check750="extra750"
CHECK_ASFF_COMPLIANCE_TYPE_extra750="ens-mp.com.4.aws.sg.7"
CHECK_SERVICENAME_extra750="ec2"
CHECK_RISK_extra750='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra750='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra750='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra750='Infrastructure Security'

extra750(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`3306` && ToPort>=`3306`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for MySQL port" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found open to 0.0.0.0/0 for MySQL port" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra75 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra75="7.5"
CHECK_TITLE_extra75="[extra75] Ensure there are no Security Groups not being used"
CHECK_SCORED_extra75="NOT_SCORED"
CHECK_CIS_LEVEL_extra75="EXTRA"
CHECK_SEVERITY_extra75="Informational"
CHECK_ASFF_RESOURCE_TYPE_extra75="AwsEc2SecurityGroup"
CHECK_ALTERNATE_extra705="extra75"
CHECK_ALTERNATE_check75="extra75"
CHECK_ALTERNATE_check705="extra75"
CHECK_ASFF_COMPLIANCE_TYPE_extra75="ens-mp.com.4.aws.sg.3"
CHECK_SERVICENAME_extra75="ec2"
CHECK_RISK_extra75='Having clear definition and scope for Security Groups creates a better administration environment.'
CHECK_REMEDIATION_extra75='List all the security groups and then use the cli to check if they are attached to an instance.'
CHECK_DOC_extra75='https://aws.amazon.com/premiumsupport/knowledge-center/ec2-find-security-group-resources/'
CHECK_CAF_EPIC_extra75='Infrastructure Security'

extra75(){
  # "Ensure there are no Security Groups not being used "
  for regx in $REGIONS; do
    SECURITYGROUPS=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --max-items $MAXITEMS --output json 2>&1 )
    if [[ $(echo "$SECURITYGROUPS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $(echo "$SECURITYGROUPS" | jq '."SecurityGroups" | length') -eq 0 ]]; then
      textInfo "$regx: No Security Groups found in $regx" "$regx"
      continue
    fi
    SECURITYGROUP_NAMES=$(echo $SECURITYGROUPS | jq '.SecurityGroups|map({(.GroupId): (.GroupName)})|add')
    LIST_OF_SECURITYGROUPS=$(echo $SECURITYGROUP_NAMES | jq -r 'to_entries|sort_by(.key)|.[]|.key')
    for SG_ID in $LIST_OF_SECURITYGROUPS; do
      SG_NOT_USED=$($AWSCLI ec2 describe-network-interfaces $PROFILE_OPT --region $regx --filters "Name=group-id,Values=$SG_ID" --query "length(NetworkInterfaces)" --output text)
      # Default security groups can not be deleted, so draw attention to them
      if [[ $SG_NOT_USED -eq 0 ]];then
        GROUP_NAME=$(echo $SECURITYGROUP_NAMES | jq -r --arg id $SG_ID '.[$id]')
        if [[ $GROUP_NAME != "default" ]];
        then
          textFail "$regx: $SG_ID is not being used!" "$regx" "$SG_ID"
        else
          textInfo "$regx: $SG_ID is not being used - default security group" "$regx" "$SG_ID"
        fi
      else
        textPass "$regx: $SG_ID is being used" "$regx" "$SG_ID"
      fi
    done
  done
}

providers/aws/services/ec2/check_extra749 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra749="7.49"
CHECK_TITLE_extra749="[extra749] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Oracle ports 1521 or 2483"
CHECK_SCORED_extra749="NOT_SCORED"
CHECK_CIS_LEVEL_extra749="EXTRA"
CHECK_SEVERITY_extra749="High"
CHECK_ASFF_RESOURCE_TYPE_extra749="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check749="extra749"
CHECK_ASFF_COMPLIANCE_TYPE_extra749="ens-mp.com.4.aws.sg.6"
CHECK_SERVICENAME_extra749="ec2"
CHECK_RISK_extra749='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra749='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra749='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra749='Infrastructure Security'

extra749(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || ((FromPort<=`1521` && ToPort>=`1521`)||(FromPort<=`2483` && ToPort>=`2483`))) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Oracle ports" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Oracle ports" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra748 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra748="7.48"
CHECK_TITLE_extra748="[extra748] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to any port"
CHECK_SCORED_extra748="NOT_SCORED"
CHECK_CIS_LEVEL_extra748="EXTRA"
CHECK_SEVERITY_extra748="High"
CHECK_ASFF_RESOURCE_TYPE_extra748="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check748="extra748"
CHECK_SERVICENAME_extra748="ec2"
CHECK_RISK_extra748='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra748='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra748='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra748='Infrastructure Security'

extra748(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`0` && ToPort==`65535`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra741 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra741="7.41"
CHECK_TITLE_extra741="[extra741] Find secrets in EC2 User Data"
CHECK_SCORED_extra741="NOT_SCORED"
CHECK_CIS_LEVEL_extra741="EXTRA"
CHECK_SEVERITY_extra741="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra741="AwsEc2Instance"
CHECK_ALTERNATE_check741="extra741"
CHECK_SERVICENAME_extra741="ec2"
CHECK_RISK_extra741='Secrets hardcoded into instance user data can be used by malware and bad actors to gain lateral access to other services.'
CHECK_REMEDIATION_extra741='Implement automated detective control (e.g. using tools like Prowler ) to scan accounts for passwords and secrets. Use secrets manager service to store and retrieve passwords and secrets. '
CHECK_DOC_extra741='https://docs.aws.amazon.com/secretsmanager/latest/userguide/tutorials_basic.html'
CHECK_CAF_EPIC_extra741='IAM'

extra741(){
  SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM"
  if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then
    # this folder is deleted once this check is finished
    mkdir $SECRETS_TEMP_FOLDER
  fi

  for regx in $REGIONS; do
    CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
    if [[ $? -eq 241 ]]; then
      textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
    else
      LIST_OF_EC2_INSTANCES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query Reservations[*].Instances[*].InstanceId --output text --max-items $MAXITEMS 2>&1| grep -v None)
      if [[ $(echo "$LIST_OF_EC2_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to describe instances" "$regx"
          continue
      fi
      if [[ $LIST_OF_EC2_INSTANCES ]];then
        for instance in $LIST_OF_EC2_INSTANCES; do
          EC2_USERDATA_FILE="$SECRETS_TEMP_FOLDER/extra741-$instance-userData.decoded"
          EC2_USERDATA=$($AWSCLI ec2 describe-instance-attribute --attribute userData $PROFILE_OPT --region $regx --instance-id $instance --query UserData.Value --output text| grep -v ^None | decode_report > $EC2_USERDATA_FILE)
          if [ -s "$EC2_USERDATA_FILE" ];then
            # This finds ftp or http URLs with credentials and common keywords
            # FINDINGS=$(egrep -i '[[:alpha:]]*://[[:alnum:]]*:[[:alnum:]]*@.*/|key|secret|token|pass' $EC2_USERDATA_FILE |wc -l|tr -d '\ ')
            # New implementation using https://github.com/Yelp/detect-secrets
            # Test if user data is a valid GZIP file, if so gunzip first
            if gunzip -t "$EC2_USERDATA_FILE" > /dev/null 2>&1; then
              mv "$EC2_USERDATA_FILE" "$EC2_USERDATA_FILE.gz" ; gunzip "$EC2_USERDATA_FILE.gz"
            fi
            FINDINGS=$(secretsDetector file "$EC2_USERDATA_FILE")
            if [[ $FINDINGS -eq 0 ]]; then
              textPass "$regx: No secrets found in $instance User Data" "$regx" "$instance"
              # delete file if nothing interesting is there
              rm -f "$EC2_USERDATA_FILE"
            else
              textFail "$regx: Potential secret found in $instance User Data" "$regx" "$instance"
              # delete file to not leave trace, user must look at the instance User Data
              rm -f "$EC2_USERDATA_FILE"
            fi
          else
            textPass "$regx: No secrets found in $instance User Data or it is empty" "$regx" "$instance"
          fi
        done
      else
        textInfo "$regx: No EC2 instances found" "$regx"
      fi
    fi
  done
  rm -rf $SECRETS_TEMP_FOLDER
}

providers/aws/services/ec2/check_extra740 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra740="7.40"
CHECK_TITLE_extra740="[extra740] Check if EBS snapshots are encrypted"
CHECK_SCORED_extra740="NOT_SCORED"
CHECK_CIS_LEVEL_extra740="EXTRA"
CHECK_SEVERITY_extra740="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra740="AwsEc2Snapshot"
CHECK_ALTERNATE_check740="extra740"
CHECK_ASFF_COMPLIANCE_TYPE_extra740="ens-mp.info.3.aws.ebs.3"
CHECK_SERVICENAME_extra740="ec2"
CHECK_RISK_extra740='Data encryption at rest prevents data visibility in the event of its unauthorized access or theft.'
CHECK_REMEDIATION_extra740='Encrypt all EBS Snapshot and Enable Encryption by default. You can configure your AWS account to enforce the encryption of the new EBS volumes and snapshot copies that you create. For example; Amazon EBS encrypts the EBS volumes created when you launch an instance and the snapshots that you copy from an unencrypted snapshot.'
CHECK_DOC_extra740='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html#encryption-by-default'
CHECK_CAF_EPIC_extra740='Data Protection'

extra740(){
  # This does NOT use max-items, which would limit the number of items
  # considered. It considers all snapshots, but only reports at most
  # max-items passing and max-items failing.
  for regx in ${REGIONS}; do
    UNENCRYPTED_SNAPSHOTS=$(${AWSCLI} ec2 describe-snapshots ${PROFILE_OPT} \
    --region ${regx} --owner-ids ${ACCOUNT_NUM} --output text \
    --query 'Snapshots[?Encrypted==`false`]|[*].{Id:SnapshotId}' 2>&1 \
     | grep -v None )
    if [[ $(echo "$UNENCRYPTED_SNAPSHOTS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe snapshots" "$regx"
        continue
    fi

    ENCRYPTED_SNAPSHOTS=$(${AWSCLI} ec2 describe-snapshots ${PROFILE_OPT} \
    --region ${regx} --owner-ids ${ACCOUNT_NUM} --output text \
    --query 'Snapshots[?Encrypted==`true`]|[*].{Id:SnapshotId}' 2>&1 \
     | grep -v None )
    if [[ $(echo "$ENCRYPTED_SNAPSHOTS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe snapshots" "$regx"
        continue
    fi
    typeset -i unencrypted
    typeset -i encrypted
    unencrypted=0
    encrypted=0

    if [[ ${UNENCRYPTED_SNAPSHOTS} ]]; then
      for snapshot in ${UNENCRYPTED_SNAPSHOTS}; do
          unencrypted=${unencrypted}+1
          if [ "${unencrypted}" -le "${MAXITEMS}" ]; then
            textFail "${regx}: ${snapshot} is not encrypted!" "${regx}" "${snapshot}"
          fi
      done
    fi
    if [[ ${ENCRYPTED_SNAPSHOTS} ]]; then
      for snapshot in ${ENCRYPTED_SNAPSHOTS}; do
          encrypted=${encrypted}+1
          if [ "${encrypted}" -le "${MAXITEMS}" ]; then
            textPass "${regx}: ${snapshot} is encrypted." "${regx}" "${snapshot}"
          fi
      done
    fi
    if [[ "${encrypted}" = "0" ]] && [[ "${unencrypted}" = "0" ]] ; then
      textInfo "${regx}: No EBS volume snapshots" "${regx}"
    else
      typeset -i total
      total=${encrypted}+${unencrypted}
      if [[ "${unencrypted}" -ge "${MAXITEMS}" ]]; then
        textFail "${unencrypted} unencrypted snapshots out of ${total} snapshots found. Only the first ${MAXITEMS} unencrypted snapshots are reported!"
      fi
      if [[ "${encrypted}" -ge "${MAXITEMS}" ]]; then
        textPass "${encrypted} encrypted snapshots out of ${total} snapshots found. Only the first ${MAXITEMS} encrypted snapshots are reported."
      fi
      # Bit of 'bc' magic to print something like 10.42% or 0.85% or similar. 'bc' has a
      # bug where it will never print leading zeros. So 0.5 is output as ".5". This has a
      # little extra clause to print a 0 if 0 < x < 1.
      ratio=$(echo "scale=2; p=(100*${encrypted}/(${encrypted}+${unencrypted})); if(p<1 && p>0) print 0;print p, \"%\";" | bc 2>/dev/null)
      exit=$?

      # maybe 'bc' doesn't exist, or it exits with an error
      if [[ "${exit}" = "0" ]]
      then
        textInfo "${regx}: ${ratio} encrypted EBS volumes (${encrypted} out of ${total})" "${regx}"
      else
        textInfo "${regx}: ${unencrypted} unencrypted EBS volume snapshots out of ${total} total snapshots" "${regx}"
      fi
    fi
  done
}

providers/aws/services/ec2/check_extra74 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra74="7.4"
CHECK_TITLE_extra74="[extra74] Ensure there are no Security Groups without ingress filtering being used"
CHECK_SCORED_extra74="NOT_SCORED"
CHECK_CIS_LEVEL_extra74="EXTRA"
CHECK_SEVERITY_extra74="High"
CHECK_ASFF_RESOURCE_TYPE_extra74="AwsEc2SecurityGroup"
CHECK_ALTERNATE_extra704="extra74"
CHECK_ALTERNATE_check74="extra74"
CHECK_ALTERNATE_check704="extra74"
CHECK_ASFF_COMPLIANCE_TYPE_extra74="ens-mp.com.4.aws.sg.2"
CHECK_SERVICENAME_extra74="ec2"
CHECK_RISK_extra74='If Security groups are not filtering traffic appropriately the attack surface is increased.'
CHECK_REMEDIATION_extra74=' You can grant access to a specific CIDR range; or to another security group in your VPC or in a peer VPC.'
CHECK_DOC_extra74='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra74='Infrastructure Security'

extra74(){
  # "Ensure there are no Security Groups without ingress filtering being used "
  for regx in $REGIONS; do
    LIST_OF_SECURITYGROUPS=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --filters "Name=ip-permission.cidr,Values=0.0.0.0/0" --query "SecurityGroups[].[GroupId]" --output text --max-items $MAXITEMS 2>&1)
    if [[ $(echo "$LIST_OF_SECURITYGROUPS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    for SG_ID in $LIST_OF_SECURITYGROUPS; do
      SG_NO_INGRESS_FILTER=$($AWSCLI ec2 describe-network-interfaces $PROFILE_OPT --region $regx --filters "Name=group-id,Values=$SG_ID" --query "length(NetworkInterfaces)" --output text)
      if [[ $SG_NO_INGRESS_FILTER -ne 0 ]];then
        textFail "$regx: $SG_ID has no ingress filtering and it is being used!" "$regx" "$SG_ID"
      else
        textInfo "$regx: $SG_ID has no ingress filtering but it is not being used" "$regx" "$SG_ID"
      fi
    done
  done
}

providers/aws/services/ec2/check_extra729 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra729="7.29"
CHECK_TITLE_extra729="[extra729] Ensure there are no EBS Volumes unencrypted"
CHECK_SCORED_extra729="NOT_SCORED"
CHECK_CIS_LEVEL_extra729="EXTRA"
CHECK_SEVERITY_extra729="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra729="AwsEc2Volume"
CHECK_ALTERNATE_check729="extra729"
CHECK_ASFF_COMPLIANCE_TYPE_extra729="ens-mp.info.3.aws.ebs.1"
CHECK_SERVICENAME_extra729="ec2"
CHECK_RISK_extra729='Data encryption at rest prevents data visibility in the event of its unauthorized access or theft.'
CHECK_REMEDIATION_extra729='Encrypt all EBS volumes and Enable Encryption by default You can configure your AWS account to enforce the encryption of the new EBS volumes and snapshot copies that you create. For example; Amazon EBS encrypts the EBS volumes created when you launch an instance and the snapshots that you copy from an unencrypted snapshot.'
CHECK_DOC_extra729='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html'
CHECK_CAF_EPIC_extra729='Data Protection'

extra729(){
  # "Ensure there are no EBS Volumes unencrypted "
  for regx in $REGIONS; do
    LIST_OF_EBS_NON_ENC_VOLUMES=$($AWSCLI ec2 describe-volumes $PROFILE_OPT --region $regx --query 'Volumes[?Encrypted==`false`].VolumeId' --output text 2>&1)
    if [[ $(echo "$LIST_OF_EBS_NON_ENC_VOLUMES" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe volumes" "$regx"
        continue
    fi
    if [[ $LIST_OF_EBS_NON_ENC_VOLUMES ]];then
      for volume in $LIST_OF_EBS_NON_ENC_VOLUMES; do
        textFail "$regx: $volume is not encrypted!" "$regx" "$volume"
      done
    fi
    LIST_OF_EBS_ENC_VOLUMES=$($AWSCLI ec2 describe-volumes $PROFILE_OPT --region $regx --query 'Volumes[?Encrypted==`true`].VolumeId' --output text)
    if [[ $LIST_OF_EBS_ENC_VOLUMES ]];then
      for volume in $LIST_OF_EBS_ENC_VOLUMES; do
        textPass "$regx: $volume is encrypted" "$regx" "$volume"
      done
    fi
  done
}

providers/aws/services/ec2/check_extra72 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra72="7.2"
CHECK_TITLE_extra72="[extra72] Ensure there are no EBS Snapshots set as Public"
CHECK_SCORED_extra72="NOT_SCORED"
CHECK_CIS_LEVEL_extra72="EXTRA"
CHECK_SEVERITY_extra72="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra72="AwsEc2Snapshot"
CHECK_ALTERNATE_extra702="extra72"
CHECK_ALTERNATE_check72="extra72"
CHECK_ALTERNATE_check702="extra72"
CHECK_SERVICENAME_extra72="ec2"
CHECK_RISK_extra72='When you share a snapshot; you are giving others access to all of the data on the snapshot. Share snapshots only with people with whom you want to share all of your snapshot data.'
CHECK_REMEDIATION_extra72='Ensure the snapshot should be shared.'
CHECK_DOC_extra72='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-modifying-snapshot-permissions.html'
CHECK_CAF_EPIC_extra72='Data Protection'

extra72(){
  # "Ensure there are no EBS Snapshots set as Public "
  for regx in $REGIONS; do
    LIST_OF_EBS_SNAPSHOTS=$($AWSCLI ec2 describe-snapshots $PROFILE_OPT --region $regx --owner-ids $ACCOUNT_NUM --output text --query 'Snapshots[*].{ID:SnapshotId}' --max-items $MAXITEMS 2>&1 | grep -v None )
    if [[ $(echo "$LIST_OF_EBS_SNAPSHOTS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe snapshot" "$regx"
        continue
    fi
    for snapshot in $LIST_OF_EBS_SNAPSHOTS; do
      SNAPSHOT_IS_PUBLIC=$($AWSCLI ec2 describe-snapshot-attribute $PROFILE_OPT --region $regx --output text --snapshot-id $snapshot --attribute createVolumePermission --query "CreateVolumePermissions[?Group=='all']")
      if [[ $SNAPSHOT_IS_PUBLIC ]];then
        textFail "$regx: $snapshot is currently Public!" "$regx" "$snapshot"
      else
        textPass "$regx: $snapshot is not Public" "$regx" "$snapshot"
      fi
    done
  done
}

providers/aws/services/ec2/check_extra7173 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7173="7.173"
CHECK_TITLE_extra7173="[check7173] Security Groups created by EC2 Launch Wizard"
CHECK_SCORED_extra7173="NOT_SCORED"
CHECK_CIS_LEVEL_extra7173="EXTRA"
CHECK_SEVERITY_extra7173="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7173="AwsEc2SecurityGroup"
CHECK_ALTERNATE_extra7173="extra7173"
CHECK_SERVICENAME_extra7173="ec2"
CHECK_RISK_cextra7173="Security Groups Created on the AWS Console using the EC2 wizard may allow port 22 from 0.0.0.0/0"
CHECK_REMEDIATION_extra7173="Apply Zero Trust approach. Implement a process to scan and remediate security groups created by the EC2 Wizard. Recommended best practices is to use an authorized security group."
CHECK_DOC_extra7173="CHECK_DOC_extra7173='https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html'"
CHECK_CAF_EPIC_extra7173="Infrastructure Security"

extra7173(){
  # Ensure no security groups are created using Console EC2 Wizard
  for regx in $REGIONS; do
    CHECK_SGDEFAULT_IDS=$("${AWSCLI}" ec2 describe-security-groups ${PROFILE_OPT} --region "${regx}" --filters Name=group-name,Values='launch-wizard-*' --query 'SecurityGroups[*].GroupId[]' --output text 2>&1)
    if grep -q -E 'AccessDenied|UnauthorizedOperation' <<< "${CHECK_SGDEFAULT_IDS}"; then
        textInfo "${regx}: Access Denied trying to describe security groups" "${regx}"
        continue
    fi
    if [[ ${CHECK_SGDEFAULT_IDS} ]]; then
      for CHECK_SGDEFAULT_ID in ${CHECK_SGDEFAULT_IDS}; do
          SECURITY_GROUP_NAME=$(${AWSCLI} ec2 describe-security-groups ${PROFILE_OPT} --region "${regx}" --group-ids "${CHECK_SGDEFAULT_ID}" --query 'SecurityGroups[*].GroupName[]' --output text 2>&1)
          textFail "${regx}: Security Group ${SECURITY_GROUP_NAME} (ID: ${CHECK_SGDEFAULT_ID}) was created using the EC2 Launch Wizard" "${regx}" "${CHECK_SGDEFAULT_ID}"
      done
    else
      textPass "${regx}: No Security Groups found that were created using the Wizard" "${regx}" "${CHECK_SGDEFAULT_ID}"
    fi
  done
}

providers/aws/services/ec2/check_extra7146 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7146="7.146"
CHECK_TITLE_extra7146="[extra7146] Check if there is any unassigned Elastic IP"
CHECK_SCORED_extra7146="NOT_SCORED"
CHECK_CIS_LEVEL_extra7146="EXTRA"
CHECK_SEVERITY_extra7146="Low"
CHECK_ASFF_RESOURCE_TYPE_extra7146="AwsElasticIPs"
CHECK_ALTERNATE_check7146="extra7146"
CHECK_SERVICENAME_extra7146="ec2"
CHECK_RISK_extra7146='Unassigned Elastic IPs may result in extra cost'
CHECK_REMEDIATION_extra7146='Ensure Elastic IPs are not unassigned'
CHECK_DOC_extra7146='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html'
CHECK_CAF_EPIC_extra7146='Infrastructure Security'

extra7146(){
  # "Check if there is any unassigned elastic ip (Not Scored) (Not part of CIS benchmark)"
  for regx in $REGIONS; do
    ELASTIC_IP_ADDRESSES=$($AWSCLI ec2 describe-addresses $PROFILE_OPT --region $regx --query Addresses --output json 2>&1)
    if [[ $(echo "$ELASTIC_IP_ADDRESSES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe addresses" "$regx"
        continue
    fi
    if [[ $ELASTIC_IP_ADDRESSES != [] ]]; then
      LIST_OF_ASSOCIATED_IPS=$(echo $ELASTIC_IP_ADDRESSES | jq -r '.[] | select(.AssociationId!=null) | .PublicIp')
      LIST_OF_UNASSOCIATED_IPS=$(echo $ELASTIC_IP_ADDRESSES | jq -r '.[] | select(.AssociationId==null) | .PublicIp')
      for ass_ip in $LIST_OF_ASSOCIATED_IPS; do
      textPass "$regx: Elastic IP $ass_ip is associated with an instance or network interface" "$regx" "$ass_ip"
      done
      for unass_ip in $LIST_OF_UNASSOCIATED_IPS; do
      textInfo "$regx: Elastic IP $unass_ip is not associated with any instance or network interface and it may incurr extra cost" "$regx" "$unass_ip"
      done
    else
      textInfo "$regx: No Elastic IPs found" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra7138 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7138="7.138"
CHECK_TITLE_extra7138="[extra7138] Ensure no Network ACLs allow ingress from 0.0.0.0/0 to any port"
CHECK_SCORED_extra7138="NOT SCORED"
CHECK_CIS_LEVEL_extra7138="LEVEL2"
CHECK_SEVERITY_extra7138="High"
CHECK_ASFF_TYPE_extra7138="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_extra7138="AwsEc2NetworkAcl"
CHECK_ALTERNATE_check7138="extra7138"
CHECK_SERVICENAME_extra7138="ec2"
CHECK_RISK_extra7138='Even having a perimeter firewall; having network acls open allows any user or malware with vpc access to scan for well known and sensitive ports and gain access to instance.'
CHECK_REMEDIATION_extra7138='Apply Zero Trust approach. Implement a process to scan and remediate unrestricted or overly permissive network acls. Recommended best practices is to narrow the definition for the minimum ports required.'
CHECK_DOC_extra7138='https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html'
CHECK_CAF_EPIC_extra7138='Infrastructure Security'

extra7138(){
  for regx in $REGIONS; do
    NACL_LIST=$($AWSCLI ec2 describe-network-acls --query 'NetworkAcls[?Entries[?((!PortRange) && (CidrBlock == `0.0.0.0/0`) && (Egress == `false`) && (RuleAction == `allow`))]].{NetworkAclId:NetworkAclId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$NACL_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe network acls" "$regx"
        continue
    fi
    if [[ $NACL_LIST ]];then
      for NACL in $NACL_LIST;do
        textInfo "$regx: Found Network ACL: $NACL open to 0.0.0.0/0 for any port" "$regx" "$NACL"
      done
    else
      textPass "$regx: No Network ACL found with any port open to 0.0.0.0/0" "$regx" "$NACL"
    fi
  done
}

providers/aws/services/ec2/check_extra7137 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7137="7.137"
CHECK_TITLE_extra7137="[extra7137] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Windows SQL Server ports 1433 or 1434 "
CHECK_SCORED_extra7137="NOT_SCORED"
CHECK_CIS_LEVEL_extra7137="EXTRA"
CHECK_SEVERITY_extra7137="High"
CHECK_ASFF_RESOURCE_TYPE_extra7137="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check7137="extra7137"
CHECK_SERVICENAME_extra7137="ec2"
CHECK_RISK_extra7137='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra7137='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra7137='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra7137='Infrastructure Security'

extra7137(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`1433` && ToPort==`1434`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Microsoft SQL Server ports" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Microsoft SQL Server ports" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra7136 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7136="7.136"
CHECK_TITLE_extra7136="[extra7136] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Telnet port 23 "
CHECK_SCORED_extra7136="NOT_SCORED"
CHECK_CIS_LEVEL_extra7136="EXTRA"
CHECK_SEVERITY_extra7136="High"
CHECK_ASFF_RESOURCE_TYPE_extra7136="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check7136="extra7136"
CHECK_SERVICENAME_extra7136="ec2"
CHECK_RISK_extra7136='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra7136='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra7136='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra7136='Infrastructure Security'

extra7136(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`23` && ToPort==`23`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Telnet ports" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Telnet ports" "$regx" "$SG"
    fi
  done
}

providers/aws/services/ec2/check_extra7135 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7135="7.135"
CHECK_TITLE_extra7135="[extra7135] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Kafka port 9092 "
CHECK_SCORED_extra7135="NOT_SCORED"
CHECK_CIS_LEVEL_extra7135="EXTRA"
CHECK_SEVERITY_extra7135="High"
CHECK_ASFF_RESOURCE_TYPE_extra7135="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check7135="extra7135"
CHECK_SERVICENAME_extra7135="ec2"
CHECK_RISK_extra7135='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra7135='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra7135='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra7135='Infrastructure Security'

extra7135(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`9092` && ToPort==`9092`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Kafka ports" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Kafka ports" "$regx"
    fi
  done
}

providers/aws/services/ec2/check_extra7134 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7134="7.134"
CHECK_TITLE_extra7134="[extra7134] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to FTP ports 20 or 21 "
CHECK_SCORED_extra7134="NOT_SCORED"
CHECK_CIS_LEVEL_extra7134="EXTRA"
CHECK_SEVERITY_extra7134="High"
CHECK_ASFF_RESOURCE_TYPE_extra7134="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check7134="extra7134"
CHECK_SERVICENAME_extra7134="ec2"
CHECK_RISK_extra7134='If Security groups are not properly configured the attack surface is increased. '
CHECK_REMEDIATION_extra7134='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
CHECK_DOC_extra7134='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
CHECK_CAF_EPIC_extra7134='Infrastructure Security'

extra7134(){
  for regx in $REGIONS; do
    SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`20` && ToPort==`21`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for FTP ports" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for FTP ports" "$regx" "$SG"
    fi
  done
}

providers/aws/services/ec2/check_extra7102 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7102="7.102"
CHECK_TITLE_extra7102="[extra7102] Check if any of the Elastic or Public IP are in Shodan (requires Shodan API KEY)"
CHECK_SCORED_extra7102="NOT_SCORED"
CHECK_CIS_LEVEL_extra7102="EXTRA"
CHECK_SEVERITY_extra7102="High"
CHECK_ASFF_RESOURCE_TYPE_extra7102="AwsEc2Eip"
CHECK_ALTERNATE_check7102="extra7102"
CHECK_SERVICENAME_extra7102="ec2"
CHECK_RISK_extra7102='Sites like Shodan index exposed systems and further expose them to wider audiences as a quick way to find exploitable systems.'
CHECK_REMEDIATION_extra7102='Check Identified IPs; consider changing them to private ones and delete them from Shodan.'
CHECK_DOC_extra7102='https://www.shodan.io/'
CHECK_CAF_EPIC_extra7102='Infrastructure Security'

# Watch out, always use Shodan API key, if you use `curl https://www.shodan.io/host/{ip}` massively
# your IP will be banned by Shodan

# This is the right way to do so
# curl -ks https://api.shodan.io/shodan/host/{ip}?key={YOUR_API_KEY}

# Each finding will be saved in prowler/output folder for further review.

extra7102(){
  if [[ ! $SHODAN_API_KEY ]]; then
    textInfo "[extra7102] Requires a Shodan API key to work. Use -N <shodan_api_key>"
  else
    for regx in $REGIONS; do
      LIST_OF_EIP=$($AWSCLI $PROFILE_OPT --region $regx ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Association.PublicIp' --output text 2>&1)
      if [[ $(echo "$LIST_OF_EIP" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to describe network interfaces" "$regx"
          continue
      fi
      if [[ $LIST_OF_EIP ]]; then
        for ip in $LIST_OF_EIP;do
          SHODAN_QUERY=$(curl -ks https://api.shodan.io/shodan/host/$ip?key=$SHODAN_API_KEY)
	  # Shodan has a request rate limit of 1 request/second.
          sleep 1
          if [[ $SHODAN_QUERY == *"No information available for that IP"* ]]; then
            textPass "$regx: IP $ip is not listed in Shodan" "$regx"
          else
            echo $SHODAN_QUERY > $OUTPUT_DIR/shodan-output-$ip.json
            IP_SHODAN_INFO=$(cat $OUTPUT_DIR/shodan-output-$ip.json | jq -r '. | { ports: .ports, org: .org, country: .country_name }| @text' | tr -d \"\{\}\}\]\[ | tr , '\ ' )
            textFail "$regx: IP $ip is listed in Shodan with data $IP_SHODAN_INFO. More info https://www.shodan.io/host/$ip and $OUTPUT_DIR/shodan-output-$ip.json" "$regx" "$ip"
          fi
        done
      else
        textInfo "$regx: No Public or Elastic IPs found" "$regx"
      fi
    done
  fi
}

providers/aws/services/ec2/check_extra710 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra710="7.10"
CHECK_TITLE_extra710="[extra710] Check for internet facing EC2 Instances"
CHECK_SCORED_extra710="NOT_SCORED"
CHECK_CIS_LEVEL_extra710="EXTRA"
CHECK_SEVERITY_extra710="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra710="AwsEc2Instance"
CHECK_ALTERNATE_check710="extra710"
CHECK_ASFF_COMPLIANCE_TYPE_extra710="ens-mp.com.4.aws.vpc.1"
CHECK_SERVICENAME_extra710="ec2"
CHECK_RISK_extra710='Exposing an EC2 directly to internet increases the attack surface and therefore the risk of compromise.'
CHECK_REMEDIATION_extra710='Use an ALB and apply WAF ACL.'
CHECK_DOC_extra710='https://aws.amazon.com/blogs/aws/aws-web-application-firewall-waf-for-application-load-balancers/'
CHECK_CAF_EPIC_extra710='Infrastructure Security'

extra710(){
  # "Check for internet facing EC2 Instances "
  for regx in $REGIONS; do
    LIST_OF_PUBLIC_INSTANCES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query 'Reservations[*].Instances[?PublicIpAddress].[InstanceId,PublicIpAddress]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_PUBLIC_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe instances" "$regx"
        continue
    fi
    if [[ $LIST_OF_PUBLIC_INSTANCES ]];then
      while read -r instance;do
        INSTANCE_ID=$(echo $instance | awk '{ print $1; }')
        PUBLIC_IP=$(echo $instance | awk '{ print $2; }')
        textFail "$regx: Instance: $INSTANCE_ID at IP: $PUBLIC_IP is internet-facing!" "$regx" "$INSTANCE_ID"
      done <<< "$LIST_OF_PUBLIC_INSTANCES"
      else
        textPass "$regx: no Internet Facing EC2 Instances found" "$regx" "$INSTANCE_ID"
    fi
  done
}

providers/aws/services/ec2/check46 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check46="4.6"
CHECK_TITLE_check46="[check46] Ensure no Network ACLs allow ingress from 0.0.0.0/0 to Microsoft RDP port 3389"
CHECK_SCORED_check46="SCORED"
CHECK_CIS_LEVEL_check46="LEVEL2"
CHECK_SEVERITY_check46="High"
CHECK_ASFF_TYPE_check46="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check46="AwsEc2NetworkAcl"
CHECK_ALTERNATE_check401="check46"
CHECK_SERVICENAME_check46="ec2"
CHECK_RISK_check46='Even having a perimeter firewall; having network acls open allows any user or malware with vpc access to scan for well known and sensitive ports and gain access to instance.'
CHECK_REMEDIATION_check46='Apply Zero Trust approach. Implement a process to scan and remediate unrestricted or overly permissive network acls. Recommended best practices is to narrow the definition for the minimum ports required.'
CHECK_DOC_check46='https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html'
CHECK_CAF_EPIC_check46='Infrastructure Security'

check46(){
  for regx in $REGIONS; do
    NACL_LIST=$($AWSCLI ec2 describe-network-acls --query 'NetworkAcls[?Entries[?(((!PortRange) || (PortRange.From<=`3389` && PortRange.To>=`3389`)) && ((CidrBlock == `0.0.0.0/0`) && (Egress == `false`) && (RuleAction == `allow`)))]].{NetworkAclId:NetworkAclId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$NACL_LIST" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe vpc network acls" "$regx"
        continue
    fi
    if [[ $NACL_LIST ]];then
      for NACL in $NACL_LIST;do
        textInfo "$regx: Found Network ACL: $NACL open to 0.0.0.0/0 for Microsoft RDP port 3389" "$regx" "$NACL"
      done
    else
      textPass "$regx: No Network ACL found with Microsoft RDP port 3389 open to 0.0.0.0/0" "$regx" "$NACL"
    fi
  done
}

providers/aws/services/ec2/check45 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check45="4.5"
CHECK_TITLE_check45="[check45] Ensure no Network ACLs allow ingress from 0.0.0.0/0 to SSH port 22"
CHECK_SCORED_check45="SCORED"
CHECK_CIS_LEVEL_check45="LEVEL2"
CHECK_SEVERITY_check45="High"
CHECK_ASFF_TYPE_check45="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check45="AwsEc2NetworkAcl"
CHECK_ALTERNATE_check401="check45"
CHECK_SERVICENAME_check45="ec2"
CHECK_RISK_check45='Even having a perimeter firewall; having network acls open allows any user or malware with vpc access to scan for well known and sensitive ports and gain access to instance.'
CHECK_REMEDIATION_check45='Apply Zero Trust approach. Implement a process to scan and remediate unrestricted or overly permissive network acls. Recommended best practices is to narrow the definition for the minimum ports required.'
CHECK_DOC_check45='https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html'
CHECK_CAF_EPIC_check45='Infrastructure Security'

check45(){
  for regx in $REGIONS; do
    NACL_LIST=$($AWSCLI ec2 describe-network-acls --query 'NetworkAcls[?Entries[?(((!PortRange) || (PortRange.From<=`22` && PortRange.To>=`22`)) && ((CidrBlock == `0.0.0.0/0`) && (Egress == `false`) && (RuleAction == `allow`)))]].{NetworkAclId:NetworkAclId}' $PROFILE_OPT --region $regx --output text 2>&1)
    if [[ $(echo "$NACL_LIST" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe vpc network acls" "$regx"
        continue
    fi
    if [[ $NACL_LIST ]];then
      for NACL in $NACL_LIST;do
        textInfo "$regx: Found Network ACL: $NACL open to 0.0.0.0/0 for SSH port 22" "$regx" "$NACL"
      done
    else
      textPass "$regx: No Network ACL found with SSH port 22 open to 0.0.0.0/0" "$regx" "$NACL"
    fi
  done
}

providers/aws/services/ec2/check43 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check43="4.3"
CHECK_TITLE_check43="[check43] Ensure the default security group of every VPC restricts all traffic"
CHECK_SCORED_check43="SCORED"
CHECK_CIS_LEVEL_check43="LEVEL2"
CHECK_SEVERITY_check43="High"
CHECK_ASFF_TYPE_check43="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check43="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check403="check43"
CHECK_ASFF_COMPLIANCE_TYPE_check43="ens-mp.com.4.aws.sg.1"
CHECK_SERVICENAME_check43="ec2"
CHECK_RISK_check43='Even having a perimeter firewall; having security groups open allows any user or malware with vpc access to scan for well known and sensitive ports and gain access to instance.'
CHECK_REMEDIATION_check43='Apply Zero Trust approach. Implement a process to scan and remediate unrestricted or overly permissive security groups. Recommended best practices is to narrow the definition for the minimum ports required.'
CHECK_DOC_check43='https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html'
CHECK_CAF_EPIC_check43='Infrastructure Security'

check43(){
  # "Ensure the default security group of every VPC restricts all traffic (Scored)"
  for regx in $REGIONS; do
    CHECK_SGDEFAULT_IDS=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --filters Name=group-name,Values='default' --query 'SecurityGroups[*].GroupId[]' --output text 2>&1)
    if [[ $(echo "$CHECK_SGDEFAULT_IDS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    for CHECK_SGDEFAULT_ID in $CHECK_SGDEFAULT_IDS; do
      CHECK_SGDEFAULT_ID_OPEN=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --group-ids $CHECK_SGDEFAULT_ID --query 'SecurityGroups[*].{IpPermissions:IpPermissions,IpPermissionsEgress:IpPermissionsEgress,GroupId:GroupId}' --output text |egrep '\s0.0.0.0|\:\:\/0')
      if [[ $CHECK_SGDEFAULT_ID_OPEN ]];then
        textFail "$regx: Default Security Groups ($CHECK_SGDEFAULT_ID) found that allow 0.0.0.0 IN or OUT traffic" "$regx" "$CHECK_SGDEFAULT_ID"
      else
        textPass "$regx: No Default Security Groups ($CHECK_SGDEFAULT_ID) open to 0.0.0.0 found" "$regx" "$CHECK_SGDEFAULT_ID"
      fi
    done
  done
}

providers/aws/services/ec2/check42 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check42="4.2"
CHECK_TITLE_check42="[check42] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 3389"
CHECK_SCORED_check42="SCORED"
CHECK_CIS_LEVEL_check42="LEVEL2"
CHECK_SEVERITY_check42="High"
CHECK_ASFF_TYPE_check42="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check42="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check402="check42"
CHECK_ASFF_COMPLIANCE_TYPE_check42="ens-mp.com.4.aws.sg.5"
CHECK_SERVICENAME_check42="ec2"
CHECK_RISK_check42='Even having a perimeter firewall; having security groups open allows any user or malware with vpc access to scan for well known and sensitive ports and gain access to instance.'
CHECK_REMEDIATION_check42='Apply Zero Trust approach. Implement a process to scan and remediate unrestricted or overly permissive security groups. Recommended best practices is to narrow the definition for the minimum ports required.'
CHECK_DOC_check42='https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html'
CHECK_CAF_EPIC_check42='Infrastructure Security'

check42(){
  # "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 3389 (Scored)"
  for regx in $REGIONS; do
    SG_LIST=$("${AWSCLI}" ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`3389` && ToPort>=`3389`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`)) && (IpProtocol==`tcp`) ]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region "${regx}" --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found with port 3389 TCP open to 0.0.0.0/0" "$regx" "$SG"
    fi
  done
}

providers/aws/services/ec2/check41 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check41="4.1"
CHECK_TITLE_check41="[check41] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 22"
CHECK_SCORED_check41="SCORED"
CHECK_CIS_LEVEL_check41="LEVEL2"
CHECK_SEVERITY_check41="High"
CHECK_ASFF_TYPE_check41="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check41="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check401="check41"
CHECK_ASFF_COMPLIANCE_TYPE_check41="ens-mp.com.4.aws.sg.4"
CHECK_SERVICENAME_check41="ec2"
CHECK_RISK_check41='Even having a perimeter firewall; having security groups open allows any user or malware with vpc access to scan for well known and sensitive ports and gain access to instance.'
CHECK_REMEDIATION_check41='Apply Zero Trust approach. Implement a process to scan and remediate unrestricted or overly permissive security groups. Recommended best practices is to narrow the definition for the minimum ports required.'
CHECK_DOC_check41='https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html'
CHECK_CAF_EPIC_check41='Infrastructure Security'

check41(){
  # "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 22 (Scored)"
  for regx in $REGIONS; do
    SG_LIST=$("${AWSCLI}" ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`22` && ToPort>=`22`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`)) && (IpProtocol==`tcp`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region "${regx}" --output text 2>&1)
    if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to describe security groups" "$regx"
        continue
    fi
    if [[ $SG_LIST ]];then
      for SG in $SG_LIST;do
        textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0" "$regx" "$SG"
      done
    else
      textPass "$regx: No Security Groups found with port 22 TCP open to 0.0.0.0/0" "$regx" "$SG"
    fi
  done
}

providers/aws/services/ec2/check310 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name SecurityGroupConfigChanges \
#     --filter-pattern '{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup) }' \
#     --metric-transformations metricName=SecurityGroupEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name SecurityGroupConfigChangesAlarm \
#     --alarm-description "Triggered by AWS security group(s) config changes." \
#     --metric-name SecurityGroupEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check310="3.10"
CHECK_TITLE_check310="[check310] Ensure a log metric filter and alarm exist for security group changes"
CHECK_SCORED_check310="SCORED"
CHECK_CIS_LEVEL_check310="LEVEL2"
CHECK_SEVERITY_check310="Medium"
CHECK_ASFF_TYPE_check310="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check310="AwsCloudTrailTrail"
CHECK_ALTERNATE_check310="check310"
CHECK_SERVICENAME_check310="ec2"
CHECK_RISK_check310='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check310='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check310='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check310='Logging and Monitoring'

check310(){
  check3x '\$\.eventName\s*=\s*AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*RevokeSecurityGroupIngress.+\$\.eventName\s*=\s*RevokeSecurityGroupEgress.+\$\.eventName\s*=\s*CreateSecurityGroup.+\$\.eventName\s*=\s*DeleteSecurityGroup'
}

providers/aws/services/ec2/check119 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check119="1.19"
CHECK_TITLE_check119="[check119] Ensure IAM instance roles are used for AWS resource access from instances"
CHECK_SCORED_check119="NOT_SCORED"
CHECK_CIS_LEVEL_check119="LEVEL2"
CHECK_SEVERITY_check119="Medium"
CHECK_ASFF_TYPE_check119="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check119="AwsEc2Instance"
CHECK_ALTERNATE_check119="check119"
CHECK_SERVICENAME_check119="ec2"
CHECK_RISK_check119='AWS access from within AWS instances can be done by either encoding AWS keys into AWS API calls or by assigning the instance to a role which has an appropriate permissions policy for the required access. AWS IAM roles reduce the risks associated with sharing and rotating credentials that can be used outside of AWS itself. If credentials are compromised; they can be used from outside of the AWS account.'
CHECK_REMEDIATION_check119='IAM roles can only be associated at the launch of an instance. To remediate an instance to add it to a role you must create or re-launch a new instance. (Check for external dependencies on its current private ip or public addresses).'
CHECK_DOC_check119='http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html'
CHECK_CAF_EPIC_check119='IAM'

check119(){
  for regx in $REGIONS; do
    EC2_DATA=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query 'Reservations[].Instances[].[InstanceId, IamInstanceProfile.Arn, State.Name]' --output json 2>&1)
    if [[ $(echo "$EC2_DATA" | grep UnauthorizedOperation) ]]; then
      textInfo "$regx: Unauthorized Operation error trying to describe instances" "$regx"
      continue
    else
      EC2_DATA=$(echo $EC2_DATA | jq '.[]|{InstanceId: .[0], ProfileArn: .[1], StateName: .[2]}')
      INSTANCE_LIST=$(echo $EC2_DATA | jq -r '.InstanceId')
    fi
    if [[ $INSTANCE_LIST ]]; then
      for instance in $INSTANCE_LIST; do
        STATE_NAME=$(echo $EC2_DATA | jq -r --arg i "$instance" 'select(.InstanceId==$i)|.StateName')
        if [[ $STATE_NAME != "terminated" && $STATE_NAME != "shutting-down" ]]; then
          PROFILEARN=$(echo $EC2_DATA | jq -r --arg i "$instance" 'select(.InstanceId==$i)|.ProfileArn')
          if [[ $PROFILEARN == "null" ]]; then
            textFail "$regx: Instance $instance not associated with an instance role" "$regx" "$instance"
          else
            textPass "$regx: Instance $instance associated with role ${PROFILEARN##*/}" "$regx" "$instance"
          fi
        fi
      done
    else
      textInfo "$regx: No EC2 instances found" "$regx" "$instance"
    fi
  done
}

providers/aws/services/dynamodb/check_extra7165 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation
#
#   https://docs.aws.amazon.com/cli/latest/reference/dax/create-cluster.html
#
#   create-cluster
#       --cluster-name <value>
#       --node-type <value>
#       --replication-factor <value>
#       --iam-role-arn <value>
#       --sse-specification Enabled=true


CHECK_ID_extra7165="7.165"
CHECK_TITLE_extra7165="[extra7165] Check if DynamoDB: DAX Clusters are encrypted at rest"
CHECK_SCORED_extra7165="NOT_SCORED"
CHECK_CIS_LEVEL_extra7165="EXTRA"
CHECK_SEVERITY_extra7165="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7165="AwsDaxCluster"
CHECK_ALTERNATE_check7165="extra7165"
CHECK_SERVICENAME_extra7165="dynamodb"
CHECK_RISK_extra7165="Encryption at rest provides an additional layer of data protection by securing your data from unauthorized access to the underlying storage."
CHECK_REMEDIATION_extra7165="Re-create the cluster to enable encryption at rest if it was not enabled at creation."
CHECK_DOC_extra7165="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DAXEncryptionAtRest.html"
CHECK_CAF_EPIC_extra7165="Data Protection"

extra7165(){
  # "Check if DynamoDB: DAX Clusters are encrypted at rest"
  for regx in $REGIONS; do
    LIST_OF_DAX_CLUSTERS=$($AWSCLI dax describe-clusters $PROFILE_OPT --region $regx --query 'Clusters[*]' --output json 2>&1)
    if [[ $(echo "$LIST_OF_DAX_CLUSTERS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
      textInfo "$regx: Access Denied trying to describe clusters" "$regx"
      continue
    fi
    if [[ $(echo "$LIST_OF_DAX_CLUSTERS" | grep -E 'Could not connect') ]]; then
      textInfo "$regx: Service not available in this region" "$regx"
      continue
    fi
    if [[ $LIST_OF_DAX_CLUSTERS && $LIST_OF_DAX_CLUSTERS != '[]' ]]; then
      LIST_OF_CLUSTERS_WITH_ENCRYPTION=$(echo "${LIST_OF_DAX_CLUSTERS}" | jq '.[] | select(.SSEDescription.Status=="ENABLED") | {ClusterName}' | jq -r '.ClusterName')
      LIST_OF_CLUSTERS_WITHOUT_ENCRYPTION=$(echo "${LIST_OF_DAX_CLUSTERS}" | jq '.[] | select(.SSEDescription.Status=="DISABLED") | {ClusterName}' | jq -r '.ClusterName')
      if [[ $LIST_OF_CLUSTERS_WITHOUT_ENCRYPTION ]]; then
        for cluster in $LIST_OF_CLUSTERS_WITHOUT_ENCRYPTION; do
          textFail "$regx: ${cluster} does not have encryption at rest enabled." "$regx" "${cluster}"
        done
      fi
      if [[ $LIST_OF_CLUSTERS_WITH_ENCRYPTION ]]; then
        for cluster in $LIST_OF_CLUSTERS_WITH_ENCRYPTION; do
          textPass "$regx: ${cluster} has encryption at rest enabled." "$regx" "${cluster}"
        done
      fi
    else
      textInfo "$regx: No DynamoDB: DAX Clusters found." "$regx"
    fi
  done
}

providers/aws/services/dynamodb/check_extra7151 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7151="7.151"
CHECK_TITLE_extra7151="[extra7151] Check if DynamoDB tables point-in-time recovery (PITR) is enabled"
CHECK_SCORED_extra7151="NOT_SCORED"
CHECK_CIS_LEVEL_extra7151="EXTRA"
CHECK_SEVERITY_extra7151="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7151="AwsDynamoDbTable"
CHECK_ALTERNATE_check7151="extra7151"
CHECK_SERVICENAME_extra7151="dynamodb"
CHECK_RISK_extra7151='If the DynamoDB Table does not have point-in-time recovery enabled; it is vulnerable to accidental write or delete operations.'
CHECK_REMEDIATION_extra7151='Enable point-in-time recovery; this is not enabled by default.'
CHECK_DOC_extra7151='https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/PointInTimeRecovery_Howitworks.html'
CHECK_CAF_EPIC_extra7151='Data Protection'

extra7151(){
  # "Check if DynamoDB tables point-in-time recovery (PITR) is enabled"
  for regx in $REGIONS; do
    LIST_OF_DYNAMODB_TABLES=$($AWSCLI dynamodb list-tables $PROFILE_OPT --region $regx --query 'TableNames[*]' --output text 2>&1)
    if [[ $(echo "$LIST_OF_DYNAMODB_TABLES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list tables" "$regx"
        continue
    fi
    if [[ $LIST_OF_DYNAMODB_TABLES ]]; then
      for dynamodb_table in $LIST_OF_DYNAMODB_TABLES; do
        POINT_IN_TIME_RECOVERY_ENABLED=$($AWSCLI dynamodb describe-continuous-backups $PROFILE_OPT --region $regx --table-name $dynamodb_table | jq '.[].PointInTimeRecoveryDescription | select(.PointInTimeRecoveryStatus=="ENABLED") | .PointInTimeRecoveryStatus')
        if [[ $POINT_IN_TIME_RECOVERY_ENABLED ]]; then
          textPass "$regx: $dynamodb_table has point-in-time recovery enabled." "$regx" "$dynamodb_table"
        else
          textFail "$regx: $dynamodb_table does not have point-in-time recovery enabled." "$regx" "$dynamodb_table"
        fi
      done
    else
      textInfo "$regx: No DynamoDB tables found" "$regx"
    fi
  done
}

providers/aws/services/dynamodb/check_extra7128 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7128="7.128"
CHECK_TITLE_extra7128="[extra7128] Check if DynamoDB table has encryption at rest enabled using CMK KMS"
CHECK_SCORED_extra7128="NOT_SCORED"
CHECK_CIS_LEVEL_extra7128="EXTRA"
CHECK_SEVERITY_extra7128="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7128="AwsDynamoDBTable"
CHECK_ALTERNATE_check7128="extra7128"
CHECK_ASFF_COMPLIANCE_TYPE_extra7128="ens-mp.info.3.aws.dyndb.1"
CHECK_SERVICENAME_extra7128="dynamodb"
CHECK_RISK_extra7128='All user data stored in Amazon DynamoDB is fully encrypted at rest. This functionality helps reduce the operational burden and complexity involved in protecting sensitive data.'
CHECK_REMEDIATION_extra7128='Specify an encryption key when you create a new table or switch the encryption keys on an existing table by using the AWS Management Console.'
CHECK_DOC_extra7128='https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/EncryptionAtRest.html'
CHECK_CAF_EPIC_extra7128='Data Protection'

extra7128(){
  for regx in $REGIONS; do
    DDB_TABLES_LIST=$($AWSCLI dynamodb list-tables $PROFILE_OPT --region $regx --output text --query TableNames 2>&1)
    if [[ $(echo "$DDB_TABLES_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to list tables" "$regx"
        continue
    fi
    if [[ $DDB_TABLES_LIST ]]; then
      for table in $DDB_TABLES_LIST; do
          DDB_TABLE_WITH_KMS=$($AWSCLI dynamodb describe-table --table-name $table $PROFILE_OPT --region $regx --query Table.SSEDescription.SSEType --output text)
          if [[ $DDB_TABLE_WITH_KMS == "KMS" ]]; then
            textPass "$regx: DynamoDB table $table does have KMS encryption enabled" "$regx" "$table"
          else
            textInfo "$regx: DynamoDB table $table does have DEFAULT encryption enabled" "$regx" "$table"
          fi
      done
    else
      textInfo "$regx: There are no DynamoDB tables" "$regx"
    fi
  done
}

providers/aws/services/config/check39 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/CloudWatchLogGroup \
#     --filter-name AWSConfigChanges \
#     --filter-pattern '{ ($.eventSource = config.amazonaws.com) && (($.eventName = StopConfigurationRecorder)||($.eventName = DeleteDeliveryChannel)||($.eventName = PutDeliveryChannel)||($.eventName = PutConfigurationRecorder)) }' \
#     --metric-transformations metricName=ConfigEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name AWSConfigChangesAlarm \
#     --alarm-description "Triggered by AWS Config changes." \
#     --metric-name ConfigEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check39="3.9"
CHECK_TITLE_check39="[check39] Ensure a log metric filter and alarm exist for AWS Config configuration changes"
CHECK_SCORED_check39="SCORED"
CHECK_CIS_LEVEL_check39="LEVEL2"
CHECK_SEVERITY_check39="Medium"
CHECK_ASFF_TYPE_check39="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check39="AwsCloudTrailTrail"
CHECK_ALTERNATE_check309="check39"
CHECK_SERVICENAME_check39="config"
CHECK_RISK_check39='If not enabled important changes to accounts could go unnoticed or difficult to find.'
CHECK_REMEDIATION_check39='Use this service as a complement to implement detective controls that cannot be prevented. (e.g. a Security Group is modified to open to internet without restrictions or route changed to avoid going thru the network firewall). Ensure AWS Config is enabled in all regions in order to detect any not intended action. On the other hand if sufficient preventive controls to make changes in critical services are in place; the rating on this finding can be lowered or discarded depending on residual risk.'
CHECK_DOC_check39='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check39='Logging and Monitoring'

check39(){
  check3x '\$\.eventSource\s*=\s*config.amazonaws.com.+\$\.eventName\s*=\s*StopConfigurationRecorder.+\$\.eventName\s*=\s*DeleteDeliveryChannel.+\$\.eventName\s*=\s*PutDeliveryChannel.+\$\.eventName\s*=\s*PutConfigurationRecorder'
}

providers/aws/services/config/check25 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check25="2.5"
CHECK_TITLE_check25="[check25] Ensure AWS Config is enabled in all regions"
CHECK_SCORED_check25="SCORED"
CHECK_CIS_LEVEL_check25="LEVEL1"
CHECK_SEVERITY_check25="Medium"
CHECK_ASFF_TYPE_check25="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check205="check25"
CHECK_ASFF_COMPLIANCE_TYPE_check25="ens-op.exp.1.aws.cfg.1"
CHECK_SERVICENAME_check25="config"
CHECK_RISK_check25='The AWS configuration item history captured by AWS Config enables security analysis; resource change tracking; and compliance auditing.'
CHECK_REMEDIATION_check25='It is recommended to enable AWS Config be enabled in all regions.'
CHECK_DOC_check25='https://aws.amazon.com/blogs/mt/aws-config-best-practices/'
CHECK_CAF_EPIC_check25='Logging and Monitoring'

check25(){
  # "Ensure AWS Config is enabled in all regions (Scored)"
  for regx in $REGIONS; do
    CHECK_AWSCONFIG_RECORDING=$($AWSCLI configservice describe-configuration-recorder-status $PROFILE_OPT --region $regx --query 'ConfigurationRecordersStatus[*].recording' --output text 2>&1)
    CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice describe-configuration-recorder-status $PROFILE_OPT --region $regx --query 'ConfigurationRecordersStatus[*].lastStatus' --output text 2>&1)
    if [[ $(echo "$CHECK_AWSCONFIG_STATUS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to describe configuration recorder status" "$regx" "recorder"
      continue
    fi
    if [[ $CHECK_AWSCONFIG_RECORDING == "True" ]]; then
      if [[ $CHECK_AWSCONFIG_STATUS == "SUCCESS" ]]; then
        textPass "$regx: AWS Config recorder enabled" "$regx" "recorder"
      else
        textFail "$regx: AWS Config recorder in failure state" "$regx" "recorder"
      fi
    else
      textFail "$regx: AWS Config recorder disabled" "$regx" "recorder"
    fi
  done
}

providers/aws/services/codebuild/check_extra7175 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7175="7.175"
CHECK_TITLE_extra7175="[extra7175] CodeBuild Project with an user controlled buildspec"
CHECK_SCORED_extra7175="NOT_SCORED"
CHECK_CIS_LEVEL_extra7175="EXTRA"
CHECK_SEVERITY_extra7175="High"
CHECK_ASFF_TYPE_extra7175="AwsCodebuildProject"
CHECK_ALTERNATE_check7175="extra7175"
CHECK_SERVICENAME_extra7175="codebuild"
CHECK_RISK_extra7175='The CodeBuild projects with user controlled buildspec'
CHECK_REMEDIATION_extra7175='Use buildspec.yml from a trusted source which user cant interfere with'
CHECK_DOC_extra7175='https://docs.aws.amazon.com/codebuild/latest/userguide/security.html'
CHECK_CAF_EPIC_extra7175='Infrastructure Security'

extra7175(){
    # "Looking for all build projects with user controlled buildspec files"
    for regx in ${REGIONS}; do
        LIST_OF_PROJECTS=$("${AWSCLI}" codebuild list-projects ${PROFILE_OPT} --region "${regx}" --query 'projects[*]' --output text 2>&1)
        if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_PROJECTS}"; then
            textInfo "${regx}: Access Denied trying to list Codebuild projects" "${regx}"
            continue
        fi
        if [[ "${LIST_OF_PROJECTS}" ]]; then
            for project in ${LIST_OF_PROJECTS}; do
                buildspec_file=$("${AWSCLI}" codebuild batch-get-projects ${PROFILE_OPT} --name "${project}" --query 'projects[0].source.buildspec' --region "${regx}" --output text 2>&1)
                if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${buildspec_file}"; then
                    textInfo "${regx}: Access Denied trying to fetch Id for Codebuild project" "${regx}" "${project}"
                    continue
                fi
                if [[ $buildspec_file == *.yml ]];then
                    textFail "${regx}: Codebuild project ${project} uses a user controlled buildspec" "${regx}" "${project}"
                else
                    textPass "${regx}: Codebuild project ${project} not uses a user controlled buildspec" "${regx}" "${project}"
                fi
            done
        else
            textInfo "${regx}: No CodeBuild Projects found" "${regx}"
        fi
    done
}

providers/aws/services/codebuild/check_extra7174 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7174="7.174"
CHECK_TITLE_extra7174="[extra7174] CodeBuild Project last invoked greater than 90 days"
CHECK_SCORED_extra7174="NOT_SCORED"
CHECK_CIS_LEVEL_extra7174="EXTRA"
CHECK_SEVERITY_extra7174="High"
CHECK_ASFF_TYPE_extra7174="AwsCodebuildProject"
CHECK_ALTERNATE_check7174="extra7174"
CHECK_SERVICENAME_extra7174="codebuild"
CHECK_RISK_extra7174='Older CodeBuild projects can be checked to see if they are currently in use'
CHECK_REMEDIATION_extra7174='Check if CodeBuild project are really in use and remove the stale ones'
CHECK_DOC_extra7174='https://docs.aws.amazon.com/codebuild/latest/userguide/delete-project.html'
CHECK_CAF_EPIC_extra7174='Infrastructure Security'

extra7174(){
    # "Looking for all build projects with last build invocation greater then 90 days in all regions"
    for regx in ${REGIONS}; do
        LIST_OF_PROJECTS=$("${AWSCLI}" codebuild list-projects ${PROFILE_OPT} --region "${regx}" --query 'projects[*]' --output text 2>&1)
        if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_PROJECTS}"; then
            textInfo "${regx}: Access Denied trying to list Codebuild projects" "${regx}"
            continue
        fi
        if [[ "${LIST_OF_PROJECTS}" ]]; then
            for project in ${LIST_OF_PROJECTS}; do
                project_id=$("${AWSCLI}" codebuild list-builds-for-project ${PROFILE_OPT} --project-name "${project}" --query 'ids[0]' --region "${regx}" --output text | head -n 1 2>&1)
                if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${project_id}"; then
                    textInfo "${regx}: Access Denied trying to fetch Id for Codebuild project" "${regx}" "${project}"
                    continue
                elif grep -q -E 'None' <<< "${project_id}"; then
                    textInfo "${regx}: Codebuild project ${project} has been never build" "${regx}" "${project}"
                    continue
                fi
                last_invoked_time=$("${AWSCLI}" codebuild batch-get-builds ${PROFILE_OPT} --ids "${project_id}" --region "${regx}" --query 'builds[0].endTime' --output text 2>&1)
                if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${last_invoked_time}"; then
                    textInfo "${regx}: Access Denied trying to get build details for Codebuild project ID" "${regx}" "${project}"
                elif grep -q -E 'None' <<< "${last_invoked_time}"; then
                    textInfo "${regx}: Codebuild project ${project} has been never build" "${regx}" "${project}"
                else
                    HOWOLDER=$(how_older_from_today "${last_invoked_time}")
                    if [ "${HOWOLDER}" -ge 90 ]; then
                        textFail "${regx}: CodeBuild project ${project} was last invoked greater then 90 days" "${regx}" "${project}"
                    else
                        textPass "${regx}: Codebuild project ${project} was last invoked in the past 90 days" "${regx}" "${project}"
                    fi
                fi
            done
        else
            textInfo "${regx}: No CodeBuild Projects found" "${regx}"
        fi
    done
}

providers/aws/services/cloudwatch/check_extra7164 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

# Remediation:
#
#   https://docs.aws.amazon.com/cli/latest/reference/logs/associate-kms-key.html
#       associate-kms-key
#       --log-group-name <value>
#       --kms-key-id <value>
#       [--cli-input-json <value>]
#       [--generate-cli-skeleton <value>]

CHECK_ID_extra7164="7.164"
CHECK_TITLE_extra7164="[extra7164] Check if CloudWatch log groups are protected by AWS KMS "
CHECK_SCORED_extra7164="NOT_SCORED"
CHECK_CIS_LEVEL_extra7164="EXTRA"
CHECK_SEVERITY_extra7164="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7164="Logs"
CHECK_ALTERNATE_extra7164="extra7164"
CHECK_SERVICENAME_extra7164="cloudwatch"
CHECK_RISK_extra7164="Using customer managed KMS to encrypt CloudWatch log group provide additional confidentiality and control over the log data"
CHECK_REMEDIATION_extra7164="Associate KMS Key with Cloudwatch log group."
CHECK_DOC_extra7164="https://docs.aws.amazon.com/cli/latest/reference/logs/associate-kms-key.html"
CHECK_CAF_EPIC_extra7164="Data Protection"

extra7164(){
    # "Check if Cloudwatch log groups are associated with AWS KMS"
    for regx in $REGIONS; do
      LIST_OF_LOGGROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --output json 2>&1 )
      if [[ $(echo "$LIST_OF_LOGGROUPS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to describe log groups" "$regx"
          continue
      fi
      if [[ $LIST_OF_LOGGROUPS ]]; then
        LIST_OF_LOGGROUPS_WITHOUT_KMS=$(echo "${LIST_OF_LOGGROUPS}" | jq '.logGroups[]' |  jq '. | select( has("kmsKeyId") == false )' | jq -r '.logGroupName')
        LIST_OF_LOGGROUPS_WITH_KMS=$(echo "${LIST_OF_LOGGROUPS}" | jq '.logGroups[]' |  jq '. | select( has("kmsKeyId") == true )' | jq -r '.logGroupName')
      if [[ $LIST_OF_LOGGROUPS_WITHOUT_KMS ]]; then
        for loggroup in $LIST_OF_LOGGROUPS_WITHOUT_KMS; do
          textFail "$regx: ${loggroup} does not have AWS KMS keys associated." "$regx" "${loggroup}"
        done
      fi
      if [[ $LIST_OF_LOGGROUPS_WITH_KMS ]]; then
        for loggroup in $LIST_OF_LOGGROUPS_WITH_KMS; do
          textPass "$regx: ${loggroup} does have AWS KMS keys associated." "$regx" "${loggroup}"
        done
      fi
    else
      textPass "$regx: No Cloudwatch log groups found." "$regx"
      fi
    done
}

providers/aws/services/cloudwatch/check_extra7162 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7162="7.162"
CHECK_TITLE_extra7162="[extra7162] Check if CloudWatch Log Groups have a retention policy of 365 days"
CHECK_SCORED_extra7162="NOT_SCORED"
CHECK_CIS_LEVEL_extra7162="EXTRA"
CHECK_SEVERITY_extra7162="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7162="AwsLogsLogGroup"
CHECK_ALTERNATE_check7162="extra7162"
CHECK_SERVICENAME_extra7162="cloudwatch"
CHECK_RISK_extra7162='If log groups have a low retention policy of less than 365 days; crucial logs and data can be lost'
CHECK_REMEDIATION_extra7162='Add Log Retention policy of 365 days to log groups. This will persist logs and traces for a long time.'
CHECK_DOC_extra7162='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Logs.html'
CHECK_CAF_EPIC_extra7162='Data Retention'

extra7162() {
    # "Check if CloudWatch Log Groups have a retention policy of 365 days"
    declare -i LOG_GROUP_RETENTION_PERIOD_DAYS=365
    for regx in $REGIONS; do
        LIST_OF_365_RETENTION_LOG_GROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --query 'logGroups[?retentionInDays=="${LOG_GROUP_RETENTION_PERIOD_DAYS}"].[logGroupName]' --output text 2>&1)
        if [[ $(echo "$LIST_OF_365_RETENTION_LOG_GROUPS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to describe log groups" "$regx"
            continue
        fi
        if [[ $LIST_OF_365_RETENTION_LOG_GROUPS ]]; then
            for log in $LIST_OF_365_RETENTION_LOG_GROUPS; do
                textPass "$regx: $log Log Group has 365 days retention period!" "$regx" "$log"
            done
        fi
        LIST_OF_NON_365_RETENTION_LOG_GROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --query 'logGroups[?retentionInDays!="${LOG_GROUP_RETENTION_PERIOD_DAYS}"].[logGroupName]' --output text)
        if [[ $LIST_OF_NON_365_RETENTION_LOG_GROUPS ]]; then
            for log in $LIST_OF_NON_365_RETENTION_LOG_GROUPS; do
                textFail "$regx: $log Log Group does not have 365 days retention period!" "$regx" "$log"
            done
        fi
        REGION_NO_LOG_GROUP=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --output text)
        if [[ $REGION_NO_LOG_GROUP ]]; then
            :
        else
            textInfo "$regx does not have a Log Group!" "$regx"

        fi
    done
}

providers/aws/services/cloudwatch/check_extra7144 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7144="7.144"
CHECK_TITLE_extra7144="[extra7144] Check if CloudWatch has allowed cross-account sharing"
CHECK_SCORED_extra7144="NOT_SCORED"
CHECK_CIS_LEVEL_extra7144="EXTRA"
CHECK_SEVERITY_extra7144="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7144="AwsCloudWatch"
CHECK_ALTERNATE_check7144="extra7144"
CHECK_SERVICENAME_extra7144="cloudwatch"
CHECK_RISK_extra7144='Cross-Account access to CloudWatch could increase the risk of compromising information between accounts'
CHECK_REMEDIATION_extra7144='Grant usage permission on a per-resource basis to enforce least privilege and Zero Trust principles'
CHECK_DOC_extra7144='https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Cross-Account-Cross-Region.html'
CHECK_CAF_EPIC_extra7144='Logging and Monitoring'

extra7144(){
  # "Check if aws cloudwatch has allowed sharing with other accounts (Not Scored) (Not part of CIS benchmark)"
  CLOUDWATCH_CROSS_ACCOUNT_ROLE=$($AWSCLI iam get-role $PROFILE_OPT --role-name=CloudWatch-CrossAccountSharingRole --output json --query Role 2>&1)
  if [[ $CLOUDWATCH_CROSS_ACCOUNT_ROLE != *NoSuchEntity* ]]; then
      CLOUDWATCH_POLICY_BAD_STATEMENTS=$(echo $CLOUDWATCH_CROSS_ACCOUNT_ROLE | jq '.AssumeRolePolicyDocument')
      textInfo "$REGION: CloudWatch has allowed cross-account sharing" "$REGION"
  else
      textPass "$REGION: CloudWatch doesn't allows cross-account sharing" "$REGION"
  fi
}

providers/aws/services/cloudtrail/check35 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
#   aws logs put-metric-filter \
#     --region us-east-1 \
#     --log-group-name CloudTrail/MyCloudTrailLG \
#     --filter-name AWSCloudTrailChanges \
#     --filter-pattern '{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }' \
#     --metric-transformations metricName=CloudTrailEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
#   aws cloudwatch put-metric-alarm \
#     --region us-east-1 \
#     --alarm-name "CloudTrail Changes" \
#     --alarm-description "Triggered by AWS CloudTrail configuration changes." \
#     --metric-name CloudTrailEventCount \
#     --namespace CloudTrailMetrics \
#     --statistic Sum \
#     --comparison-operator GreaterThanOrEqualToThreshold \
#     --evaluation-periods 1 \
#     --period 300 \
#     --threshold 1 \
#     --actions-enabled \
#     --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic

CHECK_ID_check35="3.5"
CHECK_TITLE_check35="[check35] Ensure a log metric filter and alarm exist for CloudTrail configuration changes"
CHECK_SCORED_check35="SCORED"
CHECK_CIS_LEVEL_check35="LEVEL1"
CHECK_SEVERITY_check35="Medium"
CHECK_ASFF_TYPE_check35="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check35="AwsCloudTrailTrail"
CHECK_ALTERNATE_check305="check35"
CHECK_ASFF_COMPLIANCE_TYPE_check35="ens-op.exp.8.aws.trail.1"
CHECK_SERVICENAME_check35="cloudtrail"
CHECK_RISK_check35='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
CHECK_REMEDIATION_check35='It is recommended that a metric filter and alarm be established for unauthorized requests.'
CHECK_DOC_check35='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check35='Logging and Monitoring'

check35(){
  check3x '\$\.eventName\s*=\s*CreateTrail.+\$\.eventName\s*=\s*UpdateTrail.+\$\.eventName\s*=\s*DeleteTrail.+\$\.eventName\s*=\s*StartLogging.+\$\.eventName\s*=\s*StopLogging'
}

providers/aws/services/cloudtrail/check27 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check27="2.7"
CHECK_TITLE_check27="[check27] Ensure CloudTrail logs are encrypted at rest using KMS CMKs"
CHECK_SCORED_check27="SCORED"
CHECK_CIS_LEVEL_check27="LEVEL2"
CHECK_SEVERITY_check27="Medium"
CHECK_ASFF_TYPE_check27="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check27="AwsCloudTrailTrail"
CHECK_ALTERNATE_check207="check27"
CHECK_ASFF_COMPLIANCE_TYPE_check27="ens-op.exp.10.aws.trail.5"
CHECK_SERVICENAME_check27="cloudtrail"
CHECK_RISK_check27='By default; the log files delivered by CloudTrail to your bucket are encrypted by Amazon server-side encryption with Amazon S3-managed encryption keys (SSE-S3). To provide a security layer that is directly manageable; you can instead use server-side encryption with AWS KMS–managed keys (SSE-KMS) for your CloudTrail log files.'
CHECK_REMEDIATION_check27='This approach has the following advantages: You can create and manage the CMK encryption keys yourself. You can use a single CMK to encrypt and decrypt log files for multiple accounts across all regions. You have control over who can use your key for encrypting and decrypting CloudTrail log files. You can assign permissions for the key to the users. You have enhanced security.'
CHECK_DOC_check27='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/encrypting-cloudtrail-log-files-with-aws-kms.html'
CHECK_CAF_EPIC_check27='Logging and Monitoring'

check27(){
  trail_count=0
  # "Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)"
  for regx in $REGIONS; do
    TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr "	" ',')
    if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
      continue
    fi
    if [[ $TRAILS_AND_REGIONS ]]; then
      for reg_trail in $TRAILS_AND_REGIONS; do
        TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
        if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
          continue
        fi
        trail=$(echo $reg_trail | cut -d',' -f2)
        trail_count=$((trail_count + 1))

        KMSKEYID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].KmsKeyId' --output text --trail-name-list $trail)
        if [[ "$KMSKEYID" ]];then
          textPass "$regx: Trail $trail has encryption enabled" "$regx" "$trail"
        else
          textFail "$regx: Trail $trail has encryption disabled" "$regx" "$trail"
        fi
      done
    fi
  done
  if [[ $trail_count == 0 ]]; then
    textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
  fi
}

providers/aws/services/cloudtrail/check24 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check24="2.4"
CHECK_TITLE_check24="[check24] Ensure CloudTrail trails are integrated with CloudWatch Logs"
CHECK_SCORED_check24="SCORED"
CHECK_CIS_LEVEL_check24="LEVEL1"
CHECK_SEVERITY_check24="Low"
CHECK_ASFF_TYPE_check24="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check24="AwsCloudTrailTrail"
CHECK_ALTERNATE_check204="check24"
CHECK_ASFF_COMPLIANCE_TYPE_check24="ens-op.exp.8.aws.cw.1"
CHECK_SERVICENAME_check24="cloudtrail"
CHECK_RISK_check24='Sending CloudTrail logs to CloudWatch Logs will facilitate real-time and historic activity logging based on user; API; resource; and IP address; and provides opportunity to establish alarms and notifications for anomalous or sensitivity account activity.'
CHECK_REMEDIATION_check24='Validate that the trails in CloudTrail has an arn set in the CloudWatchLogsLogGroupArn property.'
CHECK_DOC_check24='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/send-cloudtrail-events-to-cloudwatch-logs.html'
CHECK_CAF_EPIC_check24='Logging and Monitoring'

check24(){
  trail_count=0
  # "Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)"
  for regx in $REGIONS; do
    TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr "	" ',')
    if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
      continue
    fi
    if [[ $TRAILS_AND_REGIONS ]]; then
      for reg_trail in $TRAILS_AND_REGIONS; do
        TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
        if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
          continue
        fi
        trail=$(echo $reg_trail | cut -d',' -f2)
        trail_count=$((trail_count + 1))

        LATESTDELIVERY_TIMESTAMP=$($AWSCLI cloudtrail get-trail-status --name $trail $PROFILE_OPT --region $TRAIL_REGION --query 'LatestCloudWatchLogsDeliveryTime' --output text|grep -v None)
        if [[ ! $LATESTDELIVERY_TIMESTAMP ]];then
          textFail "$TRAIL_REGION: $trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" "$TRAIL_REGION" "$trail"
        else
          LATESTDELIVERY_DATE=$(timestamp_to_date $LATESTDELIVERY_TIMESTAMP)
          HOWOLDER=$(how_older_from_today $LATESTDELIVERY_DATE)
          if [ $HOWOLDER -gt "1" ];then
            textFail "$TRAIL_REGION: $trail trail is not logging in the last 24h or not configured" "$TRAIL_REGION" "$trail"
          else
            textPass "$TRAIL_REGION: $trail trail has been logging during the last 24h" "$TRAIL_REGION" "$trail"
          fi
        fi

      done
    fi
  done
  if [[ $trail_count == 0 ]]; then
    textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
  fi
}

providers/aws/services/cloudtrail/check23 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check23="2.3"
CHECK_TITLE_check23="[check23] Ensure the S3 bucket CloudTrail logs to is not publicly accessible"
CHECK_SCORED_check23="SCORED"
CHECK_CIS_LEVEL_check23="LEVEL1"
CHECK_SEVERITY_check23="Critical"
CHECK_ASFF_TYPE_check23="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check23="AwsS3Bucket"
CHECK_ALTERNATE_check203="check23"
CHECK_ASFF_COMPLIANCE_TYPE_check23="ens-op.exp.10.aws.trail.3 ens-op.exp.10.aws.trail.4"
CHECK_SERVICENAME_check23="cloudtrail"
CHECK_RISK_check23='Allowing public access to CloudTrail log content may aid an adversary in identifying weaknesses in the affected accounts use or configuration.'
CHECK_REMEDIATION_check23='Analyze Bucket policy to validate appropriate permissions. Ensure the AllUsers principal is not granted privileges. Ensure the AuthenticatedUsers principal is not granted privileges.'
CHECK_DOC_check23='https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html'
CHECK_CAF_EPIC_check23='Logging and Monitoring'

check23(){
  trail_count=0
  # "Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)"
  for regx in $REGIONS; do
    TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr "	" ',')
    if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
      continue
    fi
    if [[ $TRAILS_AND_REGIONS ]]; then
      for reg_trail in $TRAILS_AND_REGIONS; do
        TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
        if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
          continue
        fi
        trail=$(echo $reg_trail | cut -d',' -f2)
        trail_count=$((trail_count + 1))

        CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].[S3BucketName]' --output text --trail-name-list $trail)
        if [[ -z $CLOUDTRAILBUCKET ]]; then
          textFail "Trail $trail in $TRAIL_REGION does not publish to S3"
          continue
        fi

        CLOUDTRAIL_ACCOUNT_ID=$(echo $trail | awk -F: '{ print $5 }')
        if [ "$CLOUDTRAIL_ACCOUNT_ID" != "$ACCOUNT_NUM" ]; then
          textInfo "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not in current account"
          continue
        fi

        #
        # LOCATION - requests referencing buckets created after March 20, 2019
        # must be made to S3 endpoints in the same region as the bucket was
        # created.
        #
        BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $CLOUDTRAILBUCKET --output text 2>&1)
        if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
          textInfo "Trail $trail in $TRAIL_REGION Access Denied getting bucket location for $CLOUDTRAILBUCKET"
          continue
        fi
        if [[ $BUCKET_LOCATION == "None" ]]; then
          BUCKET_LOCATION="us-east-1"
        fi
        if [[ $BUCKET_LOCATION == "EU" ]]; then
          BUCKET_LOCATION="eu-west-1"
        fi

        CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $CLOUDTRAILBUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --output text 2>&1)
        if [[ $(echo "$CLOUDTRAILBUCKET_HASALLPERMISIONS" | grep AccessDenied) ]]; then
          textInfo "Trail $trail in $TRAIL_REGION Access Denied getting bucket acl for $CLOUDTRAILBUCKET"
          continue
        fi

        if [[ -z $CLOUDTRAILBUCKET_HASALLPERMISIONS ]]; then
          textPass "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not publicly accessible"
        else
          textFail "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is publicly accessible"
        fi

      done
    fi
  done
  if [[ $trail_count == 0 ]]; then
    textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
  fi
}

providers/aws/services/cloudtrail/check22 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check22="2.2"
CHECK_TITLE_check22="[check22] Ensure CloudTrail log file validation is enabled"
CHECK_SCORED_check22="SCORED"
CHECK_CIS_LEVEL_check22="LEVEL2"
CHECK_SEVERITY_check22="Medium"
CHECK_ASFF_TYPE_check22="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check22="AwsCloudTrailTrail"
CHECK_ALTERNATE_check202="check22"
CHECK_ASFF_COMPLIANCE_TYPE_check22="ens-op.exp.10.aws.trail.1"
CHECK_SERVICENAME_check22="cloudtrail"
CHECK_RISK_check22='Enabling log file validation will provide additional integrity checking of CloudTrail logs. '
CHECK_REMEDIATION_check22='Ensure LogFileValidationEnabled is set to true for each trail.'
CHECK_DOC_check22='http://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-log-filevalidation-enabling.html'
CHECK_CAF_EPIC_check22='Logging and Monitoring'

check22(){
  trail_count=0
  # "Ensure CloudTrail log file validation is enabled (Scored)"
  for regx in $REGIONS; do
    TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr "	" ',')
    if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
      continue
    fi
    if [[ $TRAILS_AND_REGIONS ]]; then
      for reg_trail in $TRAILS_AND_REGIONS; do
        TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
        if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
          continue
        fi
        trail=$(echo $reg_trail | cut -d',' -f2)
        trail_count=$((trail_count + 1))

        LOGFILEVALIDATION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].LogFileValidationEnabled' --output text --trail-name-list $trail)
        if [[ "$LOGFILEVALIDATION_TRAIL_STATUS" == 'False' ]];then
          textFail "$regx: Trail $trail log file validation disabled" "$regx" "$trail"
        else
          textPass "$regx: Trail $trail log file validation enabled" "$regx" "$trail"
        fi

      done
    fi
  done
  if [[ $trail_count == 0 ]]; then
    textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
  fi
}

providers/aws/services/cloudtrail/check21 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_check21="2.1"
CHECK_TITLE_check21="[check21] Ensure CloudTrail is enabled in all regions"
CHECK_SCORED_check21="SCORED"
CHECK_CIS_LEVEL_check21="LEVEL1"
CHECK_SEVERITY_check21="High"
CHECK_ASFF_TYPE_check21="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check21="AwsCloudTrailTrail"
CHECK_ALTERNATE_check201="check21"
CHECK_ASFF_COMPLIANCE_TYPE_check21="ens-op.acc.7.aws.iam.1 ens-op.mon.1.aws.trail.1"
CHECK_SERVICENAME_check21="cloudtrail"
CHECK_RISK_check21='AWS CloudTrail is a web service that records AWS API calls for your account and delivers log files to you. The recorded information includes the identity of the API caller; the time of the API call; the source IP address of the API caller; the request parameters; and the response elements returned by the AWS service.'
CHECK_REMEDIATION_check21='Ensure Logging is set to ON on all regions (even if they are not being used at the moment.'
CHECK_DOC_check21='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrailconcepts.html#cloudtrail-concepts-management-events'
CHECK_CAF_EPIC_check21='Logging and Monitoring'

check21(){
  trail_count=0
  # "Ensure CloudTrail is enabled in all regions (Scored)"
  for regx in $REGIONS; do
    TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr "	" ',')
    if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
      textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
      continue
    fi
    if [[ $TRAILS_AND_REGIONS ]]; then
      for reg_trail in $TRAILS_AND_REGIONS; do
        TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
        if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
          continue
        fi
        trail=$(echo $reg_trail | cut -d',' -f2)
        trail_count=$((trail_count + 1))

        MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail)
        if [[ "$MULTIREGION_TRAIL_STATUS" == 'False' ]];then
          textFail "$regx: Trail $trail is not enabled for all regions" "$regx" "$trail"
        else
          TRAIL_ON_OFF_STATUS=$($AWSCLI cloudtrail get-trail-status $PROFILE_OPT --region $TRAIL_REGION --name $trail --query IsLogging --output text)
          if [[ "$TRAIL_ON_OFF_STATUS" == 'False'  ]];then
            textFail "$regx: Trail $trail is configured for all regions but it is OFF" "$regx" "$trail"
          else
            textPass "$regx: Trail $trail is enabled for all regions" "$regx" "$trail"
          fi
        fi
      done
    fi
  done
  if [[ $trail_count == 0 ]]; then
    if [[ $FILTERREGION ]]; then
      textFail "$regx: No CloudTrail trails were found in the filtered region" "$regx" "$trail"
    else
      textFail "$regx: No CloudTrail trails were found in the account" "$regx" "$trail"
    fi
  fi
}

providers/aws/services/cloudfront/check_extra791 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra791="7.91"
CHECK_TITLE_extra791="[extra791] Check if CloudFront distributions are using deprecated SSL protocols"
CHECK_SCORED_extra791="NOT_SCORED"
CHECK_CIS_LEVEL_extra791="EXTRA"
CHECK_SEVERITY_extra791="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra791="AwsCloudFrontDistribution"
CHECK_ALTERNATE_check791="extra791"
CHECK_SERVICENAME_extra791="cloudfront"
CHECK_RISK_extra791='Using insecure ciphers could affect privacy of in transit information.'
CHECK_REMEDIATION_extra791='Use a Security policy with a ciphers that are stronger as possible. Drop legacy and unsecure ciphers.'
CHECK_DOC_extra791='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/secure-connections-supported-viewer-protocols-ciphers.html'
CHECK_CAF_EPIC_extra791='Data Protection'

extra791(){
    LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions --query 'DistributionList.Items[*].Id' $PROFILE_OPT --output text|grep -v ^None)
    if [[ $LIST_OF_DISTRIBUTIONS ]];then
      for dist in $LIST_OF_DISTRIBUTIONS; do
        CHECK_ORIGINSSLPROTOCOL_STATUS=$($AWSCLI cloudfront get-distribution --id $dist --query Distribution.DistributionConfig.Origins.Items[].CustomOriginConfig.OriginSslProtocols.Items $PROFILE_OPT --output text)
        if [[ $CHECK_ORIGINSSLPROTOCOL_STATUS == *"SSLv2"* ]] || [[ $CHECK_ORIGINSSLPROTOCOL_STATUS == *"SSLv3"* ]]; then
          textFail "CloudFront distribution $dist is using a deprecated SSL protocol!" "$regx" "$dist"
        else
          textPass "CloudFront distribution $dist is not using a deprecated SSL protocol" "$regx" "$dist"
        fi
      done
    else
      textInfo "No CloudFront distributions found" "$regx"
    fi
}

providers/aws/services/cloudfront/check_extra773 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra773="7.73"
CHECK_TITLE_extra773="[extra773] Check if CloudFront distributions are using WAF "
CHECK_SCORED_extra773="NOT_SCORED"
CHECK_CIS_LEVEL_extra773="EXTRA"
CHECK_SEVERITY_extra773="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra773="AwsCloudFrontDistribution"
CHECK_ALTERNATE_check773="extra773"
CHECK_ASFF_COMPLIANCE_TYPE_extra773="ens-mp.s.2.aws.waf.1"
CHECK_SERVICENAME_extra773="cloudfront"
CHECK_RISK_extra773='Potential attacks and / or abuse of service; more even for even for internet reachable services.'
CHECK_REMEDIATION_extra773='Use AWS WAF to protect your service from common web exploits. These could affect availability and performance; compromise security; or consume excessive resources.'
CHECK_DOC_extra773='https://docs.aws.amazon.com/waf/latest/developerguide/cloudfront-features.html'
CHECK_CAF_EPIC_extra773='Infrastructure Security'

extra773(){
  # "Check if CloudFront distributions have logging enabled "
  LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[].Id' --output text | grep -v "^None")
  if [[ $LIST_OF_DISTRIBUTIONS ]]; then
    for dist in $LIST_OF_DISTRIBUTIONS; do
      WEB_ACL_ID=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.WebACLId' --output text)
      if [[ $WEB_ACL_ID ]]; then
        textPass "CloudFront distribution $dist is using AWS WAF web ACL $WEB_ACL_ID" "us-east-1" "$dist"
      else
        textFail "CloudFront distribution $dist is not using AWS WAF web ACL" "us-east-1" "$dist"
      fi
    done
  else
    textInfo "No CloudFront distributions found"
  fi
}

providers/aws/services/cloudfront/check_extra767 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra767="7.67"
CHECK_TITLE_extra767="[extra767] Check if CloudFront distributions have Field Level Encryption enabled "
CHECK_SCORED_extra767="NOT_SCORED"
CHECK_CIS_LEVEL_extra767="EXTRA"
CHECK_SEVERITY_extra767="Low"
CHECK_ASFF_RESOURCE_TYPE_extra767="AwsCloudFrontDistribution"
CHECK_ALTERNATE_check767="extra767"
CHECK_SERVICENAME_extra767="cloudfront"
CHECK_RISK_extra767='Allows you protect specific data throughout system processing so that only certain applications can see it.'
CHECK_REMEDIATION_extra767='Check if applicable to any sensitive data. This encryption ensures that only applications that need the data—and have the credentials to decrypt it - are able to do so.'
CHECK_DOC_extra767='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/field-level-encryption.html'
CHECK_CAF_EPIC_extra767='Data Protection'

extra767(){
    LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions --query 'DistributionList.Items[*].Id' $PROFILE_OPT --output text|grep -v ^None)
    if [[ $LIST_OF_DISTRIBUTIONS ]];then
      for dist in $LIST_OF_DISTRIBUTIONS; do
        CHECK_FLE=$($AWSCLI cloudfront get-distribution --id $dist --query Distribution.DistributionConfig.DefaultCacheBehavior.FieldLevelEncryptionId $PROFILE_OPT --output text)
        if [[ $CHECK_FLE ]]; then
          textPass "CloudFront distribution $dist has Field Level Encryption enabled" "$regx" "$dist"
        else
          textFail "CloudFront distribution $dist has Field Level Encryption disabled!" "$regx" "$dist"
        fi
      done
    else
      textInfo "No CloudFront distributions found" "$regx"
    fi
}

providers/aws/services/cloudfront/check_extra738 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra738="7.38"
CHECK_TITLE_extra738="[extra738] Check if CloudFront distributions are set to HTTPS"
CHECK_SCORED_extra738="NOT_SCORED"
CHECK_CIS_LEVEL_extra738="EXTRA"
CHECK_SEVERITY_extra738="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra738="AwsCloudFrontDistribution"
CHECK_ALTERNATE_check738="extra738"
CHECK_ASFF_COMPLIANCE_TYPE_extra738="ens-mp.com.2.aws.front.1"
CHECK_SERVICENAME_extra738="cloudfront"
CHECK_RISK_extra738='If not enabled sensitive information in transit is not protected. Surveillance and other threats are risks may exists.'
CHECK_REMEDIATION_extra738='Use HTTPS everywhere possible. It will enforce privacy and protect against account hijacking and other threats.'
CHECK_DOC_extra738='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-https.html'
CHECK_CAF_EPIC_extra738='Data Protection'

extra738(){
    LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions --query 'DistributionList.Items[*].Id' $PROFILE_OPT --output text|grep -v ^None)
    if [[ $LIST_OF_DISTRIBUTIONS ]];then
      for dist in $LIST_OF_DISTRIBUTIONS; do
        CHECK_HTTPS_STATUS=$($AWSCLI cloudfront get-distribution --id $dist --query Distribution.DistributionConfig.DefaultCacheBehavior.ViewerProtocolPolicy $PROFILE_OPT --output text)
        if [[ $CHECK_HTTPS_STATUS == "allow-all" ]]; then
          textFail "$REGION: CloudFront distribution $dist viewers can use HTTP or HTTPS!" "$REGION" "$dist"
        elif [[ $CHECK_HTTPS_STATUS == "redirect-to-https" ]]; then
          textPass "$REGION: CloudFront distribution $dist has redirect to HTTPS" "$REGION" "$dist"
        else
          textPass "$REGION: CloudFront distribution $dist has HTTPS only" "$REGION" "$dist"
        fi
      done
    else
      textInfo "$REGION: No CloudFront distributions found" "$REGION"
    fi
}

providers/aws/services/cloudfront/check_extra732 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra732="7.32"
CHECK_TITLE_extra732="[extra732] Check if Geo restrictions are enabled in CloudFront distributions"
CHECK_SCORED_extra732="NOT_SCORED"
CHECK_CIS_LEVEL_extra732="EXTRA"
CHECK_SEVERITY_extra732="Low"
CHECK_ASFF_RESOURCE_TYPE_extra732="AwsCloudFrontDistribution"
CHECK_ALTERNATE_check732="extra732"
CHECK_SERVICENAME_extra732="cloudfront"
CHECK_RISK_extra732='Consider countries where service should not be accessed; by legal or compliance requirements. Additionally if not restricted the attack vector is increased.'
CHECK_REMEDIATION_extra732='If possible; define and enable Geo restrictions for this service.'
CHECK_DOC_extra732='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html'
CHECK_CAF_EPIC_extra732='Infrastructure Security'

extra732(){
  LIST_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[*].Id' --output text |grep -v ^None)
  if [[ $LIST_DISTRIBUTIONS ]]; then
    for dist in $LIST_DISTRIBUTIONS; do
      GEO_ENABLED=$($AWSCLI cloudfront get-distribution-config $PROFILE_OPT --id $dist --query DistributionConfig.Restrictions.GeoRestriction.RestrictionType --output text)
      if [[ $GEO_ENABLED == "none" ]]; then
        textFail "$REGION: CloudFront distribution $dist has not Geo restrictions" "$REGION" "$dist"
      else
        textPass "$REGION: CloudFront distribution $dist has Geo restrictions enabled" "$REGION" "$dist"
      fi
    done
  else
    textInfo "$REGION: No CloudFront distributions found"
  fi
}

providers/aws/services/cloudfront/check_extra714 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra714="7.14"
CHECK_TITLE_extra714="[extra714] Check if CloudFront distributions have logging enabled"
CHECK_SCORED_extra714="NOT_SCORED"
CHECK_CIS_LEVEL_extra714="EXTRA"
CHECK_SEVERITY_extra714="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra714="AwsCloudFrontDistribution"
CHECK_ALTERNATE_check714="extra714"
CHECK_SERVICENAME_extra714="cloudfront"
CHECK_RISK_extra714='If not enabled monitoring of service use is not possible.'
CHECK_REMEDIATION_extra714='Real-time monitoring can be achieved by directing CloudTrail Logs to CloudWatch Logs and establishing corresponding metric filters and alarms. Enable logging for services with defined log rotation. This logs are useful for Incident Response and forensics investigation among other use cases.'
CHECK_DOC_extra714='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html'
CHECK_CAF_EPIC_extra714='Logging and Monitoring'

extra714(){
  # "Check if CloudFront distributions have logging enabled "
  LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[].Id' --output text | grep -v "^None")
  if [[ $LIST_OF_DISTRIBUTIONS ]]; then
    for dist in $LIST_OF_DISTRIBUTIONS; do
      LOG_ENABLED=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.Logging.Enabled' --output text | grep True)
      LOG_ENABLED_REALTIME=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.DefaultCacheBehavior.RealtimeLogConfigArn' --output text | grep -i arn)
      if [[ $LOG_ENABLED || $LOG_ENABLED_REALTIME ]]; then
        textPass "$REGION: CloudFront distribution $dist has logging enabled" "$REGION" "$dist"
      else
        textFail "$REGION: CloudFront distribution $dist has logging disabled" "$REGION" "$dist"
      fi
    done
  else
    textInfo "$REGION: No CloudFront distributions found" "$REGION" "$dist"
  fi
}

providers/aws/services/cloudformation/check_extra742 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra742="7.42"
CHECK_TITLE_extra742="[extra742] Find secrets in CloudFormation outputs"
CHECK_SCORED_extra742="NOT_SCORED"
CHECK_CIS_LEVEL_extra742="EXTRA"
CHECK_SEVERITY_extra742="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra742="AwsCloudFormationStack"
CHECK_ALTERNATE_check742="extra742"
CHECK_SERVICENAME_extra742="cloudformation"
CHECK_RISK_extra742='Secrets hardcoded into CloudFormation outputs can be used by malware and bad actors to gain lateral access to other services.'
CHECK_REMEDIATION_extra742='Implement automated detective control (e.g. using tools like Prowler ) to scan accounts for passwords and secrets. Use secrets manager service to store and retrieve passwords and secrets. '
CHECK_DOC_extra742='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-secret-generatesecretstring.html'
CHECK_CAF_EPIC_extra742='IAM'

extra742(){
  SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM"
  if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then
    # this folder is deleted once this check is finished
    mkdir "${SECRETS_TEMP_FOLDER}"
  fi

  for regx in $REGIONS; do
    CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
    if [[ $? -eq 241 ]]; then
      textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
    else
      CFN_STACKS=$("${AWSCLI}" cloudformation describe-stacks $PROFILE_OPT --region "${regx}" --output json 2>&1)
      if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "$CFN_STACKS" ; then
          textInfo "$regx: Access Denied trying to describe stacks" "$regx"
          continue
      fi
      LIST_OF_CFN_STACKS=$(jq -r '.Stacks[].StackName' <<< "${CFN_STACKS}")
      if [[ $LIST_OF_CFN_STACKS ]];then
        for stackName in $LIST_OF_CFN_STACKS; do
          CFN_OUTPUTS_FILE="$SECRETS_TEMP_FOLDER/extra742-${stackName}-${regx}-outputs.txt"
          # OutputKey and OutputValue are separated by a colon because secrets-detector needs a way to link both values
          jq --arg stackName "$stackName" -r '.Stacks[] | select( .StackName == $stackName ) | .Outputs[]? | "\(.OutputKey):\(.OutputValue)"' <<< "${CFN_STACKS}" > "${CFN_OUTPUTS_FILE}"
          if [ -s "${CFN_OUTPUTS_FILE}" ];then
            FINDINGS=$(secretsDetector file "${CFN_OUTPUTS_FILE}")
              if [[ $FINDINGS -eq 0 ]]; then
                textPass "$regx: No secrets found in stack ${stackName} Outputs" "$regx" "${stackName}"
                # Delete file if nothing interesting is there
                rm -f "${CFN_OUTPUTS_FILE}"
              else
                textFail "$regx: Potential secret found in stack ${stackName} Outputs" "$regx" "${stackName}"
                # Delete file to not leave trace, user must look at the CFN Stack
                rm -f "${CFN_OUTPUTS_FILE}"
              fi
          else
            textInfo "$regx: CloudFormation stack ${stackName} has no Outputs" "$regx"
          fi
        done
      else
        textInfo "$regx: No CloudFormation stacks found" "$regx"
      fi
    fi
  done

  # Cleanup temporary folder
  if [[ -d $SECRETS_TEMP_FOLDER ]]
  then
    rm -rf "${SECRETS_TEMP_FOLDER}"
  fi
}

providers/aws/services/cloudformation/check_extra7154 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
#   https://docs.aws.amazon.com/cli/latest/reference/cloudformation/update-termination-protection.html
#
#   aws cloudformation update-termination-protection \
#   --stack-name my-stack \
#   --enable-termination-protection

CHECK_ID_extra7154="7.154"
CHECK_TITLE_extra7154="[extra7154] Enable termination protection for Cloudformation Stacks"
CHECK_SCORED_extra7154="NOT_SCORED"
CHECK_CIS_LEVEL_extra7154="EXTRA"
CHECK_SEVERITY_extra7154="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7154="AwsCloudFormationStack"
CHECK_ALTERNATE_check7154="extra7154"
CHECK_SERVICENAME_extra7154="cloudformation"
CHECK_RISK_extra7154='Without termination protection enabled; a critical cloudformation stack can be accidently deleted.'
CHECK_REMEDIATION_extra7154='Ensure termination protection is enabled for the cloudformation stacks'
CHECK_DOC_extra7154='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-protect-stacks.html'
CHECK_CAF_EPIC_extra7154='Infrastructure Protection'

extra7154() {
  for regx in $REGIONS; do
    CFN_STACKS=$($AWSCLI cloudformation describe-stacks $PROFILE_OPT --region $regx --output json 2>&1)
    if [[ $(echo "$CFN_STACKS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to describe stacks" "$regx"
        continue
    fi
    LIST_OF_CFN_STACKS=$(echo $CFN_STACKS | jq -r '.Stacks[].StackName')
    if [[ $LIST_OF_CFN_STACKS ]];then
      for stack in $LIST_OF_CFN_STACKS; do
        CFN_STACK_DETAILS=$($AWSCLI cloudformation describe-stacks $PROFILE_OPT --region $regx --stack-name $stack --output json)
        TERMINATION_ENABLED=$(echo $CFN_STACK_DETAILS | jq -r '.Stacks[].EnableTerminationProtection')
        ROOT_ID=$(echo $CFN_STACK_DETAILS | jq -r '.Stacks[].RootId')
        if [[ $ROOT_ID != null && $TERMINATION_ENABLED == "false" ]]; then
          textInfo "$regx: $stack is a nested stack. Enable termination protection on the root stack $ROOT_ID" "$regx" "$stack" "$ROOT_ID"
        elif [[ $TERMINATION_ENABLED == "true" ]]; then
          textPass "$regx: Cloudformation stack $stack has termination protection enabled" "$regx" "$stack"
        else
          textFail "$regx: Cloudformation stack $stack has termination protection disabled" "$regx" "$stack"
        fi
      done
    else
      textInfo "$regx: No Cloudformation stacks found" "$regx"
    fi
  done
}

providers/aws/services/autoscaling/check_extra775 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra775="7.75"
CHECK_TITLE_extra775="[extra775] Find secrets in EC2 Auto Scaling Launch Configuration "
CHECK_SCORED_extra775="NOT_SCORED"
CHECK_CIS_LEVEL_extra775="EXTRA"
CHECK_SEVERITY_extra775="Critical"
CHECK_ALTERNATE_check775="extra775"
CHECK_SERVICENAME_extra775="autoscaling"
CHECK_RISK_extra775='The use of a hard-coded password increases the possibility of password guessing.  If hard-coded passwords are used; it is possible that malicious users gain access through the account in question.'
CHECK_REMEDIATION_extra775='Use Secrets Manager to securely provide database credentials to Lambda functions and secure the databases as well as use the credentials to connect and query them without hardcoding the secrets in code or passing them through environmental variables. '
CHECK_DOC_extra775='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html'
CHECK_CAF_EPIC_extra775='IAM'

extra775(){
  SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM-$PROWLER_START_TIME"
  if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then
    # this folder is deleted once this check is finished
    mkdir $SECRETS_TEMP_FOLDER
  fi

  for regx in $REGIONS; do
    CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
    if [[ $? -eq 241 ]]; then
      textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
    else
      LIST_OF_EC2_AUTOSCALING=$($AWSCLI autoscaling describe-launch-configurations $PROFILE_OPT --region $regx --query LaunchConfigurations[*].LaunchConfigurationName --output text --max-items $MAXITEMS 2>&1 | grep -v None )
      if [[ $(echo "$LIST_OF_EC2_AUTOSCALING" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
          textInfo "$regx: Access Denied trying to describe launch configurations" "$regx"
          continue
      fi
      if [[ $LIST_OF_EC2_AUTOSCALING ]];then
        for autoscaling_configuration in $LIST_OF_EC2_AUTOSCALING; do
          EC2_AUTOSCALING_USERDATA_FILE="$SECRETS_TEMP_FOLDER/extra775-$autoscaling_configuration-userData.decoded"
          EC2_AUTOSCALING_USERDATA=$($AWSCLI autoscaling describe-launch-configurations $PROFILE_OPT --launch-configuration-names $autoscaling_configuration --region $regx --query LaunchConfigurations[*].UserData --output text| grep -v ^None | decode_report > $EC2_AUTOSCALING_USERDATA_FILE)
          if [ -s $EC2_AUTOSCALING_USERDATA_FILE ];then
            FILE_FORMAT_ASCII=$(file -b $EC2_AUTOSCALING_USERDATA_FILE | grep ASCII)
            # This finds ftp or http URLs with credentials and common keywords
            # FINDINGS=$(egrep -i '[[:alpha:]]*://[[:alnum:]]*:[[:alnum:]]*@.*/|key|secret|token|pass' $EC2_AUTOSCALING_USERDATA_FILE |wc -l|tr -d '\ ')
            # New implementation using https://github.com/Yelp/detect-secrets
            if [[ $FILE_FORMAT_ASCII ]]; then
            FINDINGS=$(secretsDetector file $EC2_AUTOSCALING_USERDATA_FILE)
              if [[ $FINDINGS -eq 0 ]]; then
                textPass "$regx: No secrets found in $autoscaling_configuration" "$regx" "$autoscaling_configuration"
                # delete file if nothing interesting is there
                rm -f $EC2_AUTOSCALING_USERDATA_FILE
              else
                textFail "$regx: Potential secret found in $autoscaling_configuration" "$regx" "$autoscaling_configuration"
                # delete file to not leave trace, user must look at the autoscaling_configuration User Data
                rm -f $EC2_AUTOSCALING_USERDATA_FILE
              fi
            else
              mv $EC2_AUTOSCALING_USERDATA_FILE $EC2_AUTOSCALING_USERDATA_FILE.gz ; gunzip $EC2_AUTOSCALING_USERDATA_FILE.gz
              FINDINGS=$(secretsDetector file $EC2_AUTOSCALING_USERDATA_FILE)
              if [[ $FINDINGS -eq 0 ]]; then
                textPass "$regx: No secrets found in $autoscaling_configuration User Data" "$regx" "$autoscaling_configuration"
                rm -f $EC2_AUTOSCALING_USERDATA_FILE
              else
                textFail "$regx: Potential secret found in $autoscaling_configuration" "$regx" "$autoscaling_configuration"
              fi
            fi
          else
            textPass "$regx: No secrets found in $autoscaling_configuration User Data or it is empty" "$regx" "$autoscaling_configuration"
          fi
        done
      else
        textInfo "$regx: No EC2 autoscaling_configurations found" "$regx"
      fi
    fi
  done
  rm -rf $SECRETS_TEMP_FOLDER
}

providers/aws/services/apigateway/check_extra746 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra746="7.46"
CHECK_TITLE_extra746="[extra746] Check if API Gateway has configured authorizers"
CHECK_SCORED_extra746="NOT_SCORED"
CHECK_CIS_LEVEL_extra746="EXTRA"
CHECK_SEVERITY_extra746="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra746="AwsApiGatewayRestApi"
CHECK_ALTERNATE_check746="extra746"
CHECK_SERVICENAME_extra746="apigateway"
CHECK_RISK_extra746='If no authorizer is enabled anyone can use the service.'
CHECK_REMEDIATION_extra746='Implement Amazon Cognito or a Lambda function to control access to your API.'
CHECK_DOC_extra746='https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html'
CHECK_CAF_EPIC_extra746='IAM'

extra746(){
  for regx in $REGIONS; do
    LIST_OF_REST_APIS=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-apis --query 'items[*].id' --output text 2>&1)
    if [[ $(echo "$LIST_OF_REST_APIS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
        continue
    fi
    if [[ $LIST_OF_REST_APIS ]];then
      for api in $LIST_OF_REST_APIS; do
        API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$api\`].name" --output text)
        AUTHORIZER_CONFIGURED=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-authorizers --rest-api-id $api --query items[*].type --output text)
        if [[ $AUTHORIZER_CONFIGURED ]]; then
           textPass "$regx: API Gateway $API_GW_NAME ID $api has authorizer configured" "$regx" "$API_GW_NAME"
        else
           textFail "$regx: API Gateway $API_GW_NAME ID $api has not authorizer configured" "$regx" "$API_GW_NAME"
        fi
      done
    else
      textInfo "$regx: No API Gateways found" "$regx"
    fi
  done
}

providers/aws/services/apigateway/check_extra745 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra745="7.45"
CHECK_TITLE_extra745="[extra745] Check if API Gateway endpoint is public or private"
CHECK_SCORED_extra745="NOT_SCORED"
CHECK_CIS_LEVEL_extra745="EXTRA"
CHECK_SEVERITY_extra745="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra745="AwsApiGatewayRestApi"
CHECK_ALTERNATE_check745="extra745"
CHECK_SERVICENAME_extra745="apigateway"
CHECK_RISK_extra745='If accessible from internet without restrictions opens up attack / abuse surface for any malicious user.'
CHECK_REMEDIATION_extra745='Verify that any public Api Gateway is protected and audited. Detective controls for common risks should be implemented.'
CHECK_DOC_extra745='https://d1.awsstatic.com/whitepapers/api-gateway-security.pdf?svrd_sip6'
CHECK_CAF_EPIC_extra745='Infrastructure Security'

extra745(){
  for regx in $REGIONS; do
    LIST_OF_REST_APIS=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-apis --query 'items[*].id' --output text 2>&1)
    if [[ $(echo "$LIST_OF_REST_APIS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
        continue
    fi
    if [[ $LIST_OF_REST_APIS ]];then
      for api in $LIST_OF_REST_APIS; do
        API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$api\`].name" --output text)
        ENDPOINT_CONFIG_TYPE=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-api --rest-api-id $api --query endpointConfiguration.types --output text)
        if [[ $ENDPOINT_CONFIG_TYPE ]]; then
          case $ENDPOINT_CONFIG_TYPE in
            PRIVATE )
              textPass "$regx: API Gateway $API_GW_NAME ID $api is set as $ENDPOINT_CONFIG_TYPE" "$regx" "$API_GW_NAME"
            ;;
            REGIONAL )
              textFail "$regx: API Gateway $API_GW_NAME ID $api is internet accesible as $ENDPOINT_CONFIG_TYPE" "$regx" "$API_GW_NAME"
            ;;
            EDGE )
              textFail "$regx: API Gateway $API_GW_NAME ID $api is internet accesible as $ENDPOINT_CONFIG_TYPE" "$regx" "$API_GW_NAME"
          esac
        fi
      done
    else
      textInfo "$regx: No API Gateways found" "$regx"
    fi
  done
}

providers/aws/services/apigateway/check_extra744 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra744="7.44"
CHECK_TITLE_extra744="[extra744] Check if API Gateway has a WAF ACL attached"
CHECK_SCORED_extra744="NOT_SCORED"
CHECK_CIS_LEVEL_extra744="EXTRA"
CHECK_SEVERITY_extra744="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra744="AwsApiGatewayRestApi"
CHECK_ALTERNATE_check744="extra744"
CHECK_ASFF_COMPLIANCE_TYPE_extra744="ens-mp.s.2.aws.waf.2"
CHECK_SERVICENAME_extra744="apigateway"
CHECK_RISK_extra744='Potential attacks and / or abuse of service; more even for even for internet reachable services.'
CHECK_REMEDIATION_extra744='Use AWS WAF to protect your API Gateway API from common web exploits; such as SQL injection and cross-site scripting (XSS) attacks. These could affect API availability and performance; compromise security; or consume excessive resources.'
CHECK_DOC_extra744='https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-aws-waf.html'
CHECK_CAF_EPIC_extra744='Infrastructure Security'

extra744(){
  for regx in $REGIONS; do
    LIST_OF_REST_APIS=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-apis --query 'items[*].id' --output text 2>&1)
    if [[ $(echo "$LIST_OF_REST_APIS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
        continue
    fi
    if [[ $LIST_OF_REST_APIS ]];then
      for api in $LIST_OF_REST_APIS; do
        API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$api\`].name" --output text)
        LIST_OF_STAGES=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-stages --rest-api-id $api --query 'item[*].stageName' --output text)
        if [[ $LIST_OF_STAGES ]]; then
          for stage in $LIST_OF_STAGES; do
            CHECK_WAFACL=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-stages --rest-api-id $api --query "item[?stageName==\`$stage\`].webAclArn" --output text)
            if [[ $CHECK_WAFACL ]]; then
                textPass "$regx: API Gateway $API_GW_NAME ID $api in $stage has $CHECK_WAFACL WAF ACL attached" "$regx" "$API_GW_NAME"
              else
                textFail "$regx: API Gateway $API_GW_NAME ID $api in $stage has not WAF ACL attached" "$regx" "$API_GW_NAME"
            fi
          done
        fi
      done
    else
      textInfo "$regx: No API Gateways found" "$regx"
    fi
  done
}

providers/aws/services/apigateway/check_extra743 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra743="7.43"
CHECK_TITLE_extra743="[extra743] Check if API Gateway has client certificate enabled to access your backend endpoint"
CHECK_SCORED_extra743="NOT_SCORED"
CHECK_CIS_LEVEL_extra743="EXTRA"
CHECK_SEVERITY_extra743="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra743="AwsApiGatewayRestApi"
CHECK_ALTERNATE_check743="extra743"
CHECK_SERVICENAME_extra743="apigateway"
CHECK_RISK_extra743='Possible man in the middle attacks and other similar risks.'
CHECK_REMEDIATION_extra743='Enable client certificate. Mutual TLS is recommended and commonly used for business-to-business (B2B) applications. It’s used in standards such as Open Banking. API Gateway now provides integrated mutual TLS authentication at no additional cost.'
CHECK_DOC_extra743='https://aws.amazon.com/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/'
CHECK_CAF_EPIC_extra743='Data Protection'

extra743(){
  for regx in $REGIONS; do
    LIST_OF_REST_APIS=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-apis --query 'items[*].id' --output text 2>&1)
    if [[ $(echo "$LIST_OF_REST_APIS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
        continue
    fi
    if [[ $LIST_OF_REST_APIS ]];then
      for api in $LIST_OF_REST_APIS; do
        API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$api\`].name" --output text)
        LIST_OF_STAGES=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-stages --rest-api-id $api --query 'item[*].stageName' --output text)
        if [[ $LIST_OF_STAGES ]]; then
          for stage in $LIST_OF_STAGES; do
            CHECK_CERTIFICATE=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-stages --rest-api-id $api --query "item[?stageName==\`$stage\`].clientCertificateId" --output text)
            if [[ $CHECK_CERTIFICATE ]]; then
                textPass "$regx: API Gateway $API_GW_NAME ID $api in $stage has client certificate enabled" "$regx" "$API_GW_NAME"
              else
                textFail "$regx: API Gateway $API_GW_NAME ID $api in $stage has not client certificate enabled" "$regx" "$API_GW_NAME"
            fi
          done
        fi
      done
    else
      textInfo "$regx: No API Gateways found" "$regx"
    fi
  done
}

providers/aws/services/apigateway/check_extra722 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra722="7.22"
CHECK_TITLE_extra722="[extra722] Check if API Gateway has logging enabled"
CHECK_SCORED_extra722="NOT_SCORED"
CHECK_CIS_LEVEL_extra722="EXTRA"
CHECK_SEVERITY_extra722="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra722="AwsApiGatewayRestApi"
CHECK_ALTERNATE_check722="extra722"
CHECK_SERVICENAME_extra722="apigateway"
CHECK_RISK_extra722='If not enabled; monitoring of service use is not possible. Real-time monitoring of API calls can be achieved by directing CloudTrail Logs to CloudWatch Logs and establishing corresponding metric filters and alarms.'
CHECK_REMEDIATION_extra722='Monitoring is an important part of maintaining the reliability; availability; and performance of API Gateway and your AWS solutions. You should collect monitoring data from all of the parts of your AWS solution. CloudTrail provides a record of actions taken by a user; role; or an AWS service in API Gateway. Using the information collected by CloudTrail; you can determine the request that was made to API Gateway; the IP address from which the request was made; who made the request; etc.'
CHECK_DOC_extra722='https://docs.aws.amazon.com/apigateway/latest/developerguide/security-monitoring.html'
CHECK_CAF_EPIC_extra722='Logging and Monitoring'

extra722(){
  # "Check if API Gateway has logging enabled "
  for regx in $REGIONS; do
    LIST_OF_API_GW=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query items[*].id --output text 2>&1)
    if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
        textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
        continue
    fi
    if [[ $LIST_OF_API_GW ]];then
      for apigwid in $LIST_OF_API_GW;do
        API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$apigwid\`].name" --output text)
        CHECK_STAGES_NAME=$($AWSCLI apigateway get-stages $PROFILE_OPT --region $regx --rest-api-id $apigwid --query "item[*].stageName" --output text)
        if [[ $CHECK_STAGES_NAME ]];then
          for stagname in $CHECK_STAGES_NAME;do
            CHECK_STAGE_METHOD_LOGGING=$($AWSCLI apigateway get-stages $PROFILE_OPT --region $regx --rest-api-id $apigwid --query "item[?stageName == \`$stagname\` ].methodSettings" --output text |awk '{ print $6 }' |egrep 'ERROR|INFO')
            if [[ $CHECK_STAGE_METHOD_LOGGING ]];then
              textPass "$regx: API Gateway $API_GW_NAME ID $apigwid in $stagname has logging enabled as $CHECK_STAGE_METHOD_LOGGING" "$regx" "$API_GW_NAME"
            else
              textFail "$regx: API Gateway $API_GW_NAME ID $apigwid in $stagname has logging disabled" "$regx" "$API_GW_NAME"
            fi
          done
        else
           textFail "$regx: No Stage name found for $API_GW_NAME" "$regx" "$API_GW_NAME"
        fi
      done
    else
       textInfo "$regx: No API Gateway found" "$regx"
    fi
  done
}

providers/aws/services/apigateway/check_extra7157 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7157="7.157"
CHECK_TITLE_extra7157="[extra7157] Check if API Gateway V2 has configured authorizers"
CHECK_SCORED_extra7157="NOT_SCORED"
CHECK_CIS_LEVEL_extra7157="EXTRA"
CHECK_SEVERITY_extra7157="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7157="AwsApiGatewayV2Api"
CHECK_ALTERNATE_check746="extra7157"
CHECK_SERVICENAME_extra7157="apigateway"
CHECK_RISK_extra7157='If no authorizer is enabled anyone can use the service.'
CHECK_REMEDIATION_extra7157='Implement JWT or Lambda Function to control access to your API.'
CHECK_DOC_extra7157='https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers.html'
CHECK_CAF_EPIC_extra7157='IAM'

extra7157(){
  for regx in $REGIONS; do
    LIST_OF_API_GW=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query "Items[*].ApiId" --output text 2>&1)
    if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
        textInfo "$regx: Access Denied trying to get APIs" "$regx"
        continue
    fi
    if [[ $LIST_OF_API_GW ]];then
      for api in $LIST_OF_API_GW; do
        API_GW_NAME=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query "Items[?ApiId==\`$api\`].Name" --output text)
        AUTHORIZER_CONFIGURED=$($AWSCLI apigatewayv2 --region $regx get-authorizers --api-id $api --query "Items[*].AuthorizerType" --output text)
        if [[ $AUTHORIZER_CONFIGURED ]]; then
           textPass "$regx: API Gateway V2 $API_GW_NAME ID $api has authorizer configured" "$regx" "$API_GW_NAME"
        else
           textFail "$regx: API Gateway V2 $API_GW_NAME ID $api has no authorizer configured" "$regx" "$API_GW_NAME"
        fi
      done
    else
      textInfo "$regx: No API Gateways found" "$regx"
    fi
  done
}

providers/aws/services/apigateway/check_extra7156 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra7156="7.156"
CHECK_TITLE_extra7156="[extra7156] Checks if API Gateway V2 has Access Logging enabled"
CHECK_SCORED_extra7156="NOT_SCORED"
CHECK_CIS_LEVEL_extra7156="EXTRA"
CHECK_SEVERITY_extra7156="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7156="AwsApiGatewayV2Api"
CHECK_ALTERNATE_check7156="extra7156"
CHECK_SERVICENAME_extra7156="apigateway"
CHECK_RISK_extra7156="If not enabled the logging of API calls is not possible. This information is important for monitoring API access."
CHECK_REMEDIATION_extra7156="Enable Access Logging in the API stage."
CHECK_DOC_extra7156="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-stage-accesslogsettings.html"
CHECK_CAF_EPIC_extra7156="Logging and Monitoring"

extra7156(){

    # "Check if API Gateway V2 has Access Logging enabled "
    for regx in $REGIONS; do
        LIST_OF_API_GW=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query Items[*].ApiId --output text 2>&1)
        if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
            textInfo "$regx: Access Denied trying to get APIs" "$regx"
            continue
        fi
        if [[ $LIST_OF_API_GW ]];then
            for apigwid in $LIST_OF_API_GW;do
                API_GW_NAME=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query "Items[?ApiId==\`$apigwid\`].Name" --output text)
                CHECK_STAGES_NAME=$($AWSCLI apigatewayv2 get-stages $PROFILE_OPT --region $regx --api-id $apigwid --query "Items[*].StageName" --output text)
                if [[ $CHECK_STAGES_NAME ]];then
                    for stagename in $CHECK_STAGES_NAME;do
                        CHECK_STAGE_METHOD_LOGGING=$($AWSCLI apigatewayv2 get-stages $PROFILE_OPT --region $regx --api-id $apigwid --query "Items[?StageName == \`$stagename\` ].AccessLogSettings.DestinationArn" --output text)
                        if [[ $CHECK_STAGE_METHOD_LOGGING ]];then
                            textPass "$regx: API Gateway V2 $API_GW_NAME ID: $apigwid with stage: $stagename has access logging enabled to $CHECK_STAGE_METHOD_LOGGING" "$regx" "$API_GW_NAME"
                        else
                            textFail "$regx: API Gateway V2 $API_GW_NAME ID: $apigwid with stage: $stagename has access logging disabled" "$regx" "$API_GW_NAME"
                        fi
                    done
                else
                textFail "$regx: No Stage name found for $API_GW_NAME" "$regx" "$API_GW_NAME"
                fi
            done
        else
            textInfo "$regx: No API Gateway found" "$regx"
        fi
    done
}

providers/aws/services/acm/check_extra730 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

DAYS_TO_EXPIRE_THRESHOLD="7"

CHECK_ID_extra730="7.30"
CHECK_TITLE_extra730="[extra730] Check if ACM Certificates are about to expire in $DAYS_TO_EXPIRE_THRESHOLD days or less"
CHECK_SCORED_extra730="NOT_SCORED"
CHECK_CIS_LEVEL_extra730="EXTRA"
CHECK_SEVERITY_extra730="High"
CHECK_ASFF_RESOURCE_TYPE_extra730="AwsCertificateManagerCertificate"
CHECK_ALTERNATE_check730="extra730"
CHECK_SERVICENAME_extra730="acm"
CHECK_RISK_extra730='Expired certificates can impact service availability.'
CHECK_REMEDIATION_extra730='Monitor certificate expiration and take automated action to renew; replace or remove. Having shorter TTL for any security artifact is a general recommendation; but requires additional automation in place. If not longer required delete certificate. Use AWS config using the managed rule: acm-certificate-expiration-check.'
CHECK_DOC_extra730='https://docs.aws.amazon.com/config/latest/developerguide/acm-certificate-expiration-check.html'
CHECK_CAF_EPIC_extra730='Data Protection'

extra730(){
  # Only RSA key types, needed to recover Amazon Issued, Imported and Private PKIs
  local ACM_KEY_TYPES="RSA_1024,RSA_2048,RSA_3072,RSA_4096"
  local ACM_CERTIFICATE_STATUSES="ISSUED"

  # "Check if ACM Certificates are about to expire in $DAYS_TO_EXPIRE_THRESHOLD days or less"
  for regx in $REGIONS; do
    LIST_OF_ACM_CERTS=$("${AWSCLI}" acm list-certificates ${PROFILE_OPT} --region "${regx}" --include keyTypes="${ACM_KEY_TYPES}" --certificate-statuses "${ACM_CERTIFICATE_STATUSES}" --query 'CertificateSummaryList[].CertificateArn' --output text)
    if [[ $LIST_OF_ACM_CERTS ]]; then
      for cert in $LIST_OF_ACM_CERTS; do
        CERT_DATA=$("${AWSCLI}" acm describe-certificate ${PROFILE_OPT} --region "${regx}" --certificate-arn "${cert}" --query 'Certificate.[DomainName,NotAfter]' --output text)
        # Format: domain.test.com YYYY-MM-DDTHH:MM:SS
        echo "$CERT_DATA" | while read -r FQDN NOTAFTER; do
          EXPIRES_DATE=$(timestamp_to_date "${NOTAFTER}")
          if [[ "${EXPIRES_DATE}" == "" ]]
          then
            textInfo "${regx}: Certificate for ${FQDN} has an incorrect timestamp format: ${NOTAFTER}" "${regx}" "${FQDN}"
          else
            COUNTER_DAYS=$(how_many_days_from_today "${EXPIRES_DATE}")
            if [[ $COUNTER_DAYS -le $DAYS_TO_EXPIRE_THRESHOLD ]]; then
              textFail "${regx}: Certificate for ${FQDN} is about to expire in ${COUNTER_DAYS} days!" "${regx}" "${FQDN}"
            else
              textPass "${regx}: Certificate for ${FQDN} expires in ${COUNTER_DAYS} days" "${regx}" "{$FQDN}"
            fi
          fi
        done
      done
    else
      textInfo "${regx}: No certificates found" "${regx}"
    fi
  done
}

providers/aws/services/acm/check_extra724 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra724="7.24"
CHECK_TITLE_extra724="[extra724] Check if ACM certificates have Certificate Transparency logging enabled"
CHECK_SCORED_extra724="NOT_SCORED"
CHECK_CIS_LEVEL_extra724="EXTRA"
CHECK_SEVERITY_extra724="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra724="AwsCertificateManagerCertificate"
CHECK_ALTERNATE_check724="extra724"
CHECK_SERVICENAME_extra724="acm"
CHECK_RISK_extra724='Domain owners can search the log to identify unexpected certificates; whether issued by mistake or malice. Domain owners can also identify Certificate Authorities (CAs) that are improperly issuing certificates.'
CHECK_REMEDIATION_extra724='Make sure you are logging information about Lambda operations. Create a lifecycle and use cases for each trail.'
CHECK_DOC_extra724='https://aws.amazon.com/blogs/security/how-to-get-ready-for-certificate-transparency/'
CHECK_CAF_EPIC_extra724='Logging and Monitoring'

extra724(){
  # "Check if ACM certificates have Certificate Transparency logging enabled "
  for regx in $REGIONS; do
    LIST_OF_CERTS=$($AWSCLI acm list-certificates $PROFILE_OPT  --region $regx --query CertificateSummaryList[].CertificateArn --output text)
    if [[ $LIST_OF_CERTS ]];then
      for cert_arn in $LIST_OF_CERTS;do
        CT_ENABLED=$($AWSCLI acm describe-certificate $PROFILE_OPT --region $regx --certificate-arn $cert_arn --query Certificate.Options.CertificateTransparencyLoggingPreference --output text)
        CERT_DOMAIN_NAME=$(aws acm describe-certificate $PROFILE_OPT --region $regx --certificate-arn $cert_arn --query Certificate.DomainName --output text)
        CERT_TYPE=$(aws acm describe-certificate $PROFILE_OPT --region $regx --certificate-arn $cert_arn --query Certificate.Type --output text)
        if [[ $CERT_TYPE == "IMPORTED" ]];then
          # Ignore imported certificate
          textInfo "$regx: ACM Certificate $CERT_DOMAIN_NAME is imported." "$regx" "$CERT_DOMAIN_NAME"
        else
          if [[ $CT_ENABLED == "ENABLED" ]];then
            textPass "$regx: ACM Certificate $CERT_DOMAIN_NAME has Certificate Transparency logging enabled!" "$regx" "$CERT_DOMAIN_NAME"
          else
            textFail "$regx: ACM Certificate $CERT_DOMAIN_NAME has Certificate Transparency logging disabled!" "$regx" "$CERT_DOMAIN_NAME"
          fi
        fi
      done
    else
       textInfo "$regx: No ACM Certificates found" "$regx"
    fi
  done
}

providers/aws/services/accessanalyzer/check_extra769 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

CHECK_ID_extra769="7.69"
CHECK_TITLE_extra769="[extra769] Check if IAM Access Analyzer is enabled and its findings "
CHECK_SCORED_extra769="NOT_SCORED"
CHECK_CIS_LEVEL_extra769="EXTRA"
CHECK_SEVERITY_extra769="High"
CHECK_ALTERNATE_check769="extra769"
CHECK_SERVICENAME_extra769="accessanalyzer"
CHECK_RISK_extra769='AWS IAM Access Analyzer helps you identify the resources in your organization and accounts; such as Amazon S3 buckets or IAM roles; that are shared with an external entity. This lets you identify unintended access to your resources and data; which is a security risk. IAM Access Analyzer uses a form of mathematical analysis called automated reasoning; which applies logic and mathematical inference to determine all possible access paths allowed by a resource policy.'
CHECK_REMEDIATION_extra769='Enable IAM Access Analyzer for all accounts; create analyzer and take action over it is recommendations (IAM Access Analyzer is available at no additional cost).'
CHECK_DOC_extra769='https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html'
CHECK_CAF_EPIC_extra769='IAM'

extra769(){
  for regx in $REGIONS; do
    LIST_OF_ACCESS_ANALYZERS=$($AWSCLI accessanalyzer list-analyzers $PROFILE_OPT --region $regx --query analyzers[*].arn --output text 2>&1)
    if [[ $(echo "$LIST_OF_ACCESS_ANALYZERS" | grep -i "argument command: Invalid choice") ]]; then
      textInfo "$regx: list-analyzers not supported: newer awscli needed" "$regx"
      continue
    fi
    if [[ $(echo "$LIST_OF_ACCESS_ANALYZERS" | grep -i "AccessDeniedException") ]]; then
      textInfo "$regx: Access Denied trying to list-analyzers" "$regx"
      continue
    fi
    if [[ $LIST_OF_ACCESS_ANALYZERS ]]; then
      for accessAnalyzerArn in $LIST_OF_ACCESS_ANALYZERS;do
        ANALYZER_ACTIVE_FINDINGS_COUNT=$($AWSCLI accessanalyzer list-findings $PROFILE_OPT --region $regx --analyzer-arn $accessAnalyzerArn --query 'findings[?status == `ACTIVE`].[id,status]' --output text | wc -l | tr -d ' ')
        if [[ $ANALYZER_ACTIVE_FINDINGS_COUNT -eq 0 ]];then
            textPass "$regx: IAM Access Analyzer $accessAnalyzerArn has no active findings" "$regx" "$accessAnalyzerArn"
        else
            textInfo "$regx: IAM Access Analyzer $accessAnalyzerArn has $ANALYZER_ACTIVE_FINDINGS_COUNT active findings" "$regx"
        fi
      done
    else
      textInfo "$regx: No IAM Access Analyzers found" "$regx"
    fi
  done
}

contrib/org-multi-account/serverless_codebuild/templates/ProwlerS3.yaml METASEP
AWSTemplateFormatVersion: 2010-09-09
Description: Create Prowler S3 Bucket for Prowler Reports

Parameters:
  AwsOrgId:
    Type: String
    Description: >
      Enter AWS Organizations ID.
      This is used to restrict permissions to least privilege.
    AllowedPattern: ^o-[a-z0-9]{10,32}$
    ConstraintDescription: The Org Id must be a 12 character string starting with o- and followed by 10 lower case alphanumeric characters.
    Default: o-abcde12345
  S3Prefix:
    Type: String
    Description: >
      Enter S3 Bucket Name Prefix (in lowercase).
      Bucket will be named: prefix-awsaccount-awsregion (i.e., prowler-123456789012-us-east-1)
    AllowedPattern: ^[a-z0-9][a-z0-9-]{1,33}[a-z0-9]$
    ConstraintDescription: >
      Max 35 characters, as "-awsaccount-awsregion" will be added, and max name is 63 characters.
      Can't start or end with dash.  Can use numbers and lowercase letters.
    Default: prowler

Resources:
  ProwlerS3:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${S3Prefix}-${AWS::AccountId}-${AWS::Region}
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: "AES256"
      AccessControl: Private
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      VersioningConfiguration:
        Status: Enabled
      Tags:
        - Key: App
          Value: Prowler
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W35
            reason: "This S3 Bucket is only being used by the AWS Organization to download/upload prowler reports."

  ProwlerS3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref ProwlerS3
      PolicyDocument:
        Statement:
          - Sid: AllowGetPutListObject
            Effect: Allow
            Principal: "*"
            Action:
              - s3:GetObject
              - s3:PutObject
              - s3:ListBucket
              - s3:PutObjectAcl
            Resource:
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
            Condition:
              StringEquals:
                aws:PrincipalOrgId: !Ref AwsOrgId
          - Sid: DenyNonSSLRequests
            Effect: Deny
            Action: s3:*
            Resource:
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
            Principal: "*"
            Condition:
              Bool:
                aws:SecureTransport: false
          - Sid: DenyIncorrectEncryptionHeader
            Effect: Deny
            Principal: "*"
            Action: s3:PutObject
            Resource:
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
            # Allow uploads with No Encryption, as S3 Default Encryption still applies.
            # If Encryption is set, only allow uploads with AES256.
            Condition:
              "Null":
                s3:x-amz-server-side-encryption: false
              StringNotEquals:
                s3:x-amz-server-side-encryption: AES256
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: F16
            reason: "This S3 Bucket Policy has a condition that only allows access to the AWS Organization."


Outputs:
  ProwlerS3:
    Description: S3 Bucket for Prowler Reports
    Value: !Ref ProwlerS3
  ProwlerS3Account:
    Description: AWS Account Number where Prowler S3 Bucket resides.
    Value: !Ref AWS::AccountId

contrib/org-multi-account/serverless_codebuild/templates/ProwlerRole.yaml METASEP
AWSTemplateFormatVersion: 2010-09-09
Description: Create the Cross-Account IAM Prowler Role

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: CodeBuild Settings
        Parameters:
          - ProwlerCodeBuildAccount
          - ProwlerCodeBuildRole
      - Label:
          default: S3 Settings
        Parameters:
          - ProwlerS3
      - Label:
          default: CrossAccount Role
        Parameters:
          - ProwlerCrossAccountRole

Parameters:
  ProwlerS3:
    Type: String
    Description: Enter S3 Bucket for Prowler Reports.  prefix-awsaccount-awsregion
    AllowedPattern: ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$
    Default: prowler-954896828174-ap-northeast-2
  ProwlerCodeBuildAccount:
    Type: String
    Description: Enter AWS Account Number where Prowler CodeBuild Instance will reside.
    AllowedPattern: ^\d{12}$
    ConstraintDescription: An AWS Account Number must be a 12 digit numeric string.
    Default: 411267690458
  ProwlerCodeBuildRole:
    Type: String
    Description: Enter Instance Role that will be given to the Prowler CodeBuild (needed to grant sts:AssumeRole rights).
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerCodeBuild-Role
  ProwlerCrossAccountRole:
    Type: String
    Description: Enter Name for CrossAccount Role to be created for Prowler to assess all Accounts in the AWS Organization.
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerXA-CBRole

Resources:
  ProwlerRole:
    Type: AWS::IAM::Role
    Properties:
      Description: Provides Prowler CodeBuild permissions to assess security of Accounts in AWS Organization
      RoleName: !Ref ProwlerCrossAccountRole
      Tags:
        - Key: App
          Value: Prowler
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                - !Sub arn:${AWS::Partition}:iam::${ProwlerCodeBuildAccount}:root
            Action:
              - sts:AssumeRole
            Condition:
              StringLike:
                aws:PrincipalArn: !Sub arn:${AWS::Partition}:iam::${ProwlerCodeBuildAccount}:role/${ProwlerCodeBuildRole}
      ManagedPolicyArns:
        - !Sub arn:${AWS::Partition}:iam::aws:policy/SecurityAudit
        - !Sub arn:${AWS::Partition}:iam::aws:policy/job-function/ViewOnlyAccess
      Policies:
        - PolicyName: Prowler-Additions-Policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowMoreReadForProwler
                Effect: Allow
                Resource: "*"
                Action:
                  - access-analyzer:List*
                  - apigateway:Get*
                  - apigatewayv2:Get*
                  - aws-marketplace:ViewSubscriptions
                  - dax:ListTables
                  - ds:ListAuthorizedApplications
                  - ds:DescribeRoles
                  - ec2:GetEbsEncryptionByDefault
                  - ecr:Describe*
                  - lambda:GetAccountSettings
                  - lambda:GetFunctionConfiguration
                  - lambda:GetLayerVersionPolicy
                  - lambda:GetPolicy
                  - opsworks-cm:Describe*
                  - opsworks:Describe*
                  - secretsmanager:ListSecretVersionIds
                  - sns:List*
                  - sqs:ListQueueTags
                  - states:ListActivities
                  - support:Describe*
                  - tag:GetTagKeys
                  - shield:GetSubscriptionState
                  - shield:DescribeProtection
                  - elasticfilesystem:DescribeBackupPolicy
        - PolicyName: Prowler-S3-Reports
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowGetPutListObject
                Effect: Allow
                Resource:
                  - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}
                  - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:ListBucket
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W11
            reason: "Prowler requires these rights to perform its Security Assessment."
          - id: W28
            reason: "Using a defined Role Name."

Outputs:
  ProwlerCrossAccountRole:
    Description: CrossAccount Role to be used by Prowler to assess AWS Accounts in the AWS Organization.
    Value: !Ref ProwlerCrossAccountRole

contrib/org-multi-account/serverless_codebuild/templates/ProwlerCodeBuildStack.yaml METASEP
---
AWSTemplateFormatVersion: 2010-09-09
Description: Creates a CodeBuild project to audit an AWS account with Prowler and stores the html report in a S3 bucket.
Parameters:
  AwsOrgId:
    Type: String
    Description: Enter AWS Organizations ID
    AllowedPattern: ^o-[a-z0-9]{10,32}$
    ConstraintDescription: The Org Id must be a 12 character string starting with o- and followed by 10 lower case alphanumeric characters.
    Default: o-itdezkbz6h
  CodeBuildRole:
    Description: Enter Name for CodeBuild Role to create
    Type: String
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerCodeBuild-Role
  CodeBuildSourceS3:
    Type: String
    Description: Enter like <bucket-name>/<path>/<object-name>.zip
    ConstraintDescription: Max 63 characters. Can't start or end with dash.  Can use numbers and lowercase letters.
    Default: prowler-util-411267690458-ap-northeast-2/run-prowler-reports.sh.zip
  ProwlerReportS3:
    Type: String
    Description: Enter S3 Bucket for Prowler Reports.  prefix-awsaccount-awsregion
    AllowedPattern: ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$
    ConstraintDescription: Max 63 characters. Can't start or end with dash.  Can use numbers and lowercase letters.
    Default: prowler-954896828174-ap-northeast-2
  ProwlerReportS3Account:
    Type: String
    Description: Enter AWS Account Number where Prowler S3 Bucket resides.
    AllowedPattern: ^\d{12}$
    ConstraintDescription: An AWS Account Number must be a 12 digit numeric string.
    Default: 954896828174
  CrossAccountRole:
    Type: String
    Description: Enter CrossAccount Role Prowler will be using to assess AWS Accounts in the AWS Organization. (ProwlerCrossAccountRole)
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters [+, =, ., @, -]
    Default: ProwlerXA-CBRole
  ProwlerReportFormat:
    Type: String
    Description: Enter Prowler Option like html, csv, json
    Default: html

Resources:
  ProwlerCodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      Description: Prowler CodeBuild Role
      RoleName: !Ref CodeBuildRole
      Tags:
        - Key: App
          Value: Prowler
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - codebuild.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: Prowler-S3
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowGetPutListObject
                Effect: Allow
                Resource:
                  - !Sub arn:${AWS::Partition}:s3:::${ProwlerReportS3}
                  - !Sub arn:${AWS::Partition}:s3:::${ProwlerReportS3}/*
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:ListBucket
                  - s3:PutObjectAcl
              - Sid: AllowReadOnlyS3Access
                Effect: Allow
                Resource: "*"
                Action:
                  - "s3:Get*"
                  - "s3:List*"
        - PolicyName: Prowler-CrossAccount-AssumeRole
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowStsAssumeRole
                Effect: Allow
                Resource: !Sub arn:${AWS::Partition}:iam::*:role/${CrossAccountRole}
                Action: sts:AssumeRole
                Condition:
                  StringEquals:
                    aws:PrincipalOrgId: !Ref AwsOrgId
        - PolicyName: Prowler-CloudWatch
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowCreateLogs
                Effect: Allow
                Resource: !Sub arn:${AWS::Partition}:logs:*:*:log-group:*
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
              - Sid: AllowPutevent
                Effect: Allow
                Resource: !Sub arn:${AWS::Partition}:logs:*:*:log-group:*:log-stream:*
                Action:
                  - logs:PutLogEvents

  ProwlerCodeBuild:
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts:
        Type: NO_ARTIFACTS
      Source:
        Type: S3
        Location: !Ref CodeBuildSourceS3
        BuildSpec: |
          version: 0.2
          phases:
            install:
              runtime-versions:
                python: 3.8
              commands:
                - echo "Updating yum ..."
                - yum -y update --skip-broken
                - echo "Updating pip ..."
                - python -m pip install --upgrade pip
                - echo "Installing requirements ..."
                - pip install "git+https://github.com/ibm/detect-secrets.git@master#egg=detect-secrets"
            build:
              commands:
                - echo "Running Prowler with script"
                - chmod +x run-prowler-reports.sh
                - ./run-prowler-reports.sh
            post_build:
              commands:
                - echo "Done!"
      Environment:
        # AWS CodeBuild free tier includes 100 build minutes of BUILD_GENERAL1_SMALL per month.
        # BUILD_GENERAL1_SMALL: Use up to 3 GB memory and 2 vCPUs for builds. $0.005/minute.
        # BUILD_GENERAL1_MEDIUM: Use up to 7 GB memory and 4 vCPUs for builds. $0.01/minute.
        # BUILD_GENERAL1_LARGE: Use up to 15 GB memory and 8 vCPUs for builds. $0.02/minute.
        # BUILD_GENERAL1_2XLARGE: Use up to 144 GB memory and 72 vCPUs for builds. $0.20/minute.
        ComputeType: "BUILD_GENERAL1_SMALL"
        Image: "aws/codebuild/amazonlinux2-x86_64-standard:3.0"
        Type: "LINUX_CONTAINER"
        EnvironmentVariables:
          - Name: "S3"
            Value: !Sub s3://${ProwlerReportS3}
            Type: PLAINTEXT
          - Name: "S3ACCOUNT"
            Value: !Ref ProwlerReportS3Account
            Type: PLAINTEXT
          - Name: "ROLE"
            Value: !Ref CrossAccountRole
            Type: PLAINTEXT
          - Name: "FORMAT"
            Value: !Ref ProwlerReportFormat
            Type: PLAINTEXT
      Description: Run Prowler assessment
      ServiceRole: !GetAtt ProwlerCodeBuildRole.Arn
      TimeoutInMinutes: 300

  ProwlerCWRuleRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - events.amazonaws.com
            Action:
              - sts:AssumeRole
      Description: ProwlerCWRuleRole
      RoleName: ProwlerCWRule-Role
      Policies:
        - PolicyName: Rule-Events
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AWSEventInvokeCodeBuild
                Effect: Allow
                Resource: "*"
                Action:
                  - codebuild:StartBuild

  ProwlerRule:
    Type: AWS::Events::Rule
    Properties:
      Description: This rule will trigger CodeBuild to audit AWS Accounts in my Organization with Prowler
      ScheduleExpression: cron(0 21 * * ? *)
      RoleArn: !GetAtt ProwlerCWRuleRole.Arn
      Name: ProwlerExecuteRule
      State: ENABLED
      Targets:
        - Arn: !Sub ${ProwlerCodeBuild.Arn}
          Id: Prowler-CodeBuild-Target
          RoleArn: !GetAtt ProwlerCWRuleRole.Arn


Outputs:
  ProwlerEc2Account:
    Description: AWS Account Number where Prowler EC2 Instance resides.
    Value: !Ref AWS::AccountId
  ProwlerCodeBuildRole:
    Description: Instance Role given to the Prowler EC2 Instance (needed to grant sts:AssumeRole rights).
    Value: !Ref ProwlerCodeBuildRole
  ProwlerReportS3:
    Description: S3 Bucket for Prowler Reports
    Value: !Ref ProwlerReportS3

contrib/org-multi-account/serverless_codebuild/src/run-prowler-reports.sh.zip METASEP

contrib/org-multi-account/serverless_codebuild/src/run-prowler-reports.sh METASEP
#!/bin/bash -e
#
# Run Prowler against All AWS Accounts in an AWS Organization

# Change Directory (rest of the script, assumes your in the ec2-user home directory)
# cd /home/ec2-user || exit

# Show Prowler Version, and Download Prowler, if it doesn't already exist
if ! ./prowler/prowler -V 2>/dev/null; then
    git clone https://github.com/prowler-cloud/prowler.git
    ./prowler/prowler -V
fi

# Source .awsvariables (to read in Environment Variables from CloudFormation Data)
# shellcheck disable=SC1091
# source .awsvariables

# Get Values from Environment Variables Created on EC2 Instance from CloudFormation Data
echo "S3:             $S3"
echo "S3ACCOUNT:      $S3ACCOUNT"
echo "ROLE:           $ROLE"
echo "FORMAT:         $FORMAT"

# CleanUp Last Ran Prowler Reports, as they are already stored in S3.
rm -rf prowler/output/*.html

# Function to unset AWS Profile Variables
unset_aws() {
    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}
unset_aws

# Find THIS Account AWS Number
CALLER_ARN=$(aws sts get-caller-identity --output text --query "Arn")
PARTITION=$(echo "$CALLER_ARN" | cut -d: -f2)
THISACCOUNT=$(echo "$CALLER_ARN" | cut -d: -f5)
echo "THISACCOUNT:    $THISACCOUNT"
echo "PARTITION:      $PARTITION"

# Function to Assume Role to THIS Account & Create Session
this_account_session() {
    unset_aws
    role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$THISACCOUNT":role/"$ROLE" --role-session-name ProwlerRun --output json)
    AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId)
    AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey)
    AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken)
    echo "this_account_session done..."
    export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}

# Find AWS Master Account
this_account_session
AWSMASTER=$(aws organizations describe-organization --query Organization.MasterAccountId --output text)
echo "AWSMASTER:      $AWSMASTER"

# Function to Assume Role to Master Account & Create Session
master_account_session() {
    unset_aws
    role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$AWSMASTER":role/"$ROLE" --role-session-name ProwlerRun --output json)
    AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId)
    AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey)
    AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken)
    echo "master_account_session done..."
    export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}

# Lookup All Accounts in AWS Organization
master_account_session
ACCOUNTS_IN_ORGS=$(aws organizations list-accounts --query Accounts[*].Id --output text)

# Function to Assume Role to S3 Account & Create Session
s3_account_session() {
    unset_aws
    role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$S3ACCOUNT":role/"$ROLE" --role-session-name ProwlerRun --output json)
    AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId)
    AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey)
    AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken)
    echo "s3_account_session done..."
    export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}

# Run Prowler against Accounts in AWS Organization
echo "AWS Accounts in Organization"
echo "$ACCOUNTS_IN_ORGS"
PARALLEL_ACCOUNTS="1"
for accountId in $ACCOUNTS_IN_ORGS; do
    # shellcheck disable=SC2015
    test "$(jobs | wc -l)" -ge $PARALLEL_ACCOUNTS && wait || true
    {
        START_TIME=$SECONDS
        # Unset AWS Profile Variables
        unset_aws
        # Run Prowler
        echo -e "Assessing AWS Account: $accountId, using Role: $ROLE on $(date)"
        # remove -g cislevel for a full report and add other formats if needed
        ./prowler/prowler -R "$ROLE" -A "$accountId" -g cislevel1 -M $FORMAT -z
        echo "Report stored locally at: prowler/output/ directory"
        TOTAL_SEC=$((SECONDS - START_TIME))
        echo -e "Completed AWS Account: $accountId, using Role: $ROLE on $(date)"
        printf "Completed AWS Account: $accountId in %02dh:%02dm:%02ds" $((TOTAL_SEC / 3600)) $((TOTAL_SEC % 3600 / 60)) $((TOTAL_SEC % 60))
        echo ""
    } &
done

# Wait for All Prowler Processes to finish
wait
echo "Prowler Assessments Completed against All Accounts in the AWS Organization. Starting S3 copy operations..."

# Upload Prowler Report to S3
s3_account_session
aws s3 cp prowler/output/ "$S3/reports/" --recursive --include "*.html" --acl bucket-owner-full-control
echo "Assessment reports successfully copied to S3 bucket"

# Final Wait for All Prowler Processes to finish
wait
echo "Prowler Assessments Completed"

# Unset AWS Profile Variables
unset_aws

providers/aws/services/s3/s3_service.py METASEP
import threading
from dataclasses import dataclass

from lib.logger import logger
from providers.aws.aws_provider import current_audit_info, generate_regional_clients


################## S3
class S3:
    def __init__(self, audit_info):
        self.service = "s3"
        self.session = audit_info.audit_session
        self.client = self.session.client(self.service)
        self.audited_account = audit_info.audited_account
        self.regional_clients = generate_regional_clients(self.service, audit_info)
        self.buckets = self.__list_buckets__()
        self.__threading_call__(self.__get_bucket_versioning__)
        self.__threading_call__(self.__get_bucket_logging__)

    def __get_session__(self):
        return self.session

    def __threading_call__(self, call):
        threads = []
        for bucket in self.buckets:
            threads.append(threading.Thread(target=call, args=(bucket,)))
        for t in threads:
            t.start()
        for t in threads:
            t.join()

    def __list_buckets__(self):
        logger.info("S3 - Listing buckets...")
        try:
            buckets = []
            list_buckets = self.client.list_buckets()
            for bucket in list_buckets["Buckets"]:
                bucket_region = self.client.get_bucket_location(Bucket=bucket["Name"])[
                    "LocationConstraint"
                ]
                if not bucket_region:  # If us-east-1, bucket_region is none
                    bucket_region = "us-east-1"
                # Check if there are filter regions
                if current_audit_info.audited_regions:
                    if bucket_region in current_audit_info.audited_regions:
                        buckets.append(Bucket(bucket["Name"], bucket_region))
                else:
                    buckets.append(Bucket(bucket["Name"], bucket_region))
            return buckets
        except Exception as error:
            logger.error(f"{bucket_region} -- {error.__class__.__name__}: {error}")

    def __get_bucket_versioning__(self, bucket):
        logger.info("S3 - Get buckets versioning...")
        try:
            regional_client = self.regional_clients[bucket.region]
            bucket_versioning = regional_client.get_bucket_versioning(
                Bucket=bucket.name
            )
            if "Status" in bucket_versioning:
                if "Enabled" == bucket_versioning["Status"]:
                    bucket.versioning = True
        except Exception as error:
            logger.error(f"{bucket.region} -- {error.__class__.__name__}: {error}")

    def __get_bucket_logging__(self, bucket):
        logger.info("S3 - Get buckets logging...")
        try:
            regional_client = self.regional_clients[bucket.region]
            bucket_logging = regional_client.get_bucket_logging(Bucket=bucket.name)
            if "LoggingEnabled" in bucket_logging:
                bucket.logging = True
        except Exception as error:
            logger.error(
                f"{regional_client.region} -- {error.__class__.__name__}: {error}"
            )


@dataclass
class Bucket:
    name: str
    versioning: bool
    logging: bool
    region: str

    def __init__(self, name, region):
        self.name = name
        self.versioning = False
        self.logging = False
        self.region = region


s3_client = S3(current_audit_info)

providers/aws/services/s3/__init__.py METASEP

providers/aws/services/iam/iam_service.py METASEP
import csv
from dataclasses import dataclass

from lib.logger import logger
from providers.aws.aws_provider import current_audit_info


################## IAM
class IAM:
    def __init__(self, audit_info):
        self.service = "iam"
        self.session = audit_info.audit_session
        self.account = audit_info.audited_account
        self.client = self.session.client(self.service)
        self.region = audit_info.profile_region
        self.users = self.__get_users__()
        self.roles = self.__get_roles__()
        self.account_summary = self.__get_account_summary__()
        self.virtual_mfa_devices = self.__list_virtual_mfa_devices__()
        self.customer_managed_policies = self.__get_customer_managed_policies__()
        self.__get_customer_managed_policies_version__(self.customer_managed_policies)
        self.credential_report = self.__get_credential_report__()
        self.groups = self.__get_groups__()
        self.__get_group_users__()
        self.__list_attached_group_policies__()
        self.__list_mfa_devices__()
        self.password_policy = self.__get_password_policy__()

    def __get_client__(self):
        return self.client

    def __get_session__(self):
        return self.session

    def __get_roles__(self):
        try:
            get_roles_paginator = self.client.get_paginator("list_roles")
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
        else:
            roles = []
            for page in get_roles_paginator.paginate():
                for role in page["Roles"]:
                    roles.append(role)

            return roles

    def __get_credential_report__(self):
        report_is_completed = False
        while not report_is_completed:
            try:
                report_status = self.client.generate_credential_report()
            except Exception as error:
                logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
            else:
                if report_status["State"] == "COMPLETE":
                    report_is_completed = True

        # Convert credential report to list of dictionaries
        credential = self.client.get_credential_report()["Content"].decode("utf-8")
        credential_lines = credential.split("\n")
        csv_reader = csv.DictReader(credential_lines, delimiter=",")
        credential_list = list(csv_reader)
        return credential_list

    def __get_groups__(self):
        try:
            get_groups_paginator = self.client.get_paginator("list_groups")
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
        else:
            groups = []
            for page in get_groups_paginator.paginate():
                for group in page["Groups"]:
                    groups.append(Group(group["GroupName"], group["Arn"]))

            return groups

    def __get_customer_managed_policies__(self):
        try:
            get_customer_managed_policies_paginator = self.client.get_paginator(
                "list_policies"
            )
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
        else:
            customer_managed_policies = []
            # Use --scope Local to list only Customer Managed Policies
            for page in get_customer_managed_policies_paginator.paginate(Scope="Local"):
                for customer_managed_policy in page["Policies"]:
                    customer_managed_policies.append(customer_managed_policy)

            return customer_managed_policies

    def __get_customer_managed_policies_version__(self, customer_managed_policies):
        try:
            for policy in customer_managed_policies:
                response = self.client.get_policy_version(
                    PolicyArn=policy["Arn"], VersionId=policy["DefaultVersionId"]
                )
                policy["PolicyDocument"] = response["PolicyVersion"]["Document"]
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")

    def __get_account_summary__(self):
        try:
            account_summary = self.client.get_account_summary()
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
        else:

            return account_summary

    def __get_password_policy__(self):
        try:
            password_policy = self.client.get_account_password_policy()[
                "PasswordPolicy"
            ]
            # Check if optional keys exist or not
            max_age = None
            reuse_prevention = None
            hard_expiry = None
            if "MaxPasswordAge" in password_policy:
                max_age = password_policy["MaxPasswordAge"]
            if "PasswordReusePrevention" in password_policy:
                reuse_prevention = password_policy["PasswordReusePrevention"]
            if "HardExpiry" in password_policy:
                hard_expiry = password_policy["HardExpiry"]
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
            # Password policy does not exist
            password_policy = None
        else:
            return PasswordPolicy(
                password_policy["MinimumPasswordLength"],
                password_policy["RequireSymbols"],
                password_policy["RequireNumbers"],
                password_policy["RequireUppercaseCharacters"],
                password_policy["RequireLowercaseCharacters"],
                password_policy["AllowUsersToChangePassword"],
                password_policy["ExpirePasswords"],
                max_age,
                reuse_prevention,
                hard_expiry,
            )

    def __get_users__(self):
        try:
            get_users_paginator = self.client.get_paginator("list_users")
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
        else:
            users = []
            for page in get_users_paginator.paginate():
                for user in page["Users"]:
                    if "PasswordLastUsed" not in user:
                        users.append(User(user["UserName"], user["Arn"], None))
                    else:
                        users.append(
                            User(
                                user["UserName"], user["Arn"], user["PasswordLastUsed"]
                            )
                        )

            return users

    def __list_virtual_mfa_devices__(self):
        try:
            list_virtual_mfa_devices_paginator = self.client.get_paginator(
                "list_virtual_mfa_devices"
            )
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
        else:
            mfa_devices = []
            for page in list_virtual_mfa_devices_paginator.paginate():
                for mfa_device in page["VirtualMFADevices"]:
                    mfa_devices.append(mfa_device)

            return mfa_devices

    def __list_attached_group_policies__(self):
        try:
            for group in self.groups:
                list_attached_group_policies_paginator = self.client.get_paginator(
                    "list_attached_group_policies"
                )
                attached_group_policies = []
                for page in list_attached_group_policies_paginator.paginate(
                    GroupName=group.name
                ):
                    for attached_group_policy in page["AttachedPolicies"]:
                        attached_group_policies.append(attached_group_policy)

                group.attached_policies = attached_group_policies
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")

    def __get_group_users__(self):
        try:
            for group in self.groups:
                get_group_paginator = self.client.get_paginator("get_group")
                group_users = []
                for page in get_group_paginator.paginate(GroupName=group.name):
                    for user in page["Users"]:
                        if "PasswordLastUsed" not in user:
                            group_users.append(
                                User(user["UserName"], user["Arn"], None)
                            )
                        else:
                            group_users.append(
                                User(
                                    user["UserName"],
                                    user["Arn"],
                                    user["PasswordLastUsed"],
                                )
                            )
                group.users = group_users
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")

    def __list_mfa_devices__(self):
        try:
            for user in self.users:
                list_mfa_devices_paginator = self.client.get_paginator(
                    "list_mfa_devices"
                )
                mfa_devices = []
                for page in list_mfa_devices_paginator.paginate(UserName=user.name):
                    for mfa_device in page["MFADevices"]:
                        mfa_serial_number = mfa_device["SerialNumber"]
                        mfa_type = (
                            mfa_device["SerialNumber"].split(":")[5].split("/")[0]
                        )
                        mfa_devices.append(MFADevice(mfa_serial_number, mfa_type))
                user.mfa_devices = mfa_devices
        except Exception as error:
            logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")


@dataclass
class MFADevice:
    serial_number: str
    type: str

    def __init__(self, serial_number, type):
        self.serial_number = serial_number
        self.type = type


@dataclass
class User:
    name: str
    arn: str
    mfa_devices: list[MFADevice]
    password_last_used: str

    def __init__(self, name, arn, password_last_used):
        self.name = name
        self.arn = arn
        self.password_last_used = password_last_used
        self.mfa_devices = []


@dataclass
class Group:
    name: str
    arn: str
    attached_policies: list[dict]
    users: list[User]

    def __init__(self, name, arn):
        self.name = name
        self.arn = arn
        self.attached_policies = []
        self.users = []


@dataclass
class PasswordPolicy:
    length: int
    symbols: bool
    numbers: bool
    uppercase: bool
    lowercase: bool
    allow_change: bool
    expiration: bool
    max_age: int
    reuse_prevention: int
    hard_expiry: bool

    def __init__(
        self,
        length,
        symbols,
        numbers,
        uppercase,
        lowercase,
        allow_change,
        expiration,
        max_age,
        reuse_prevention,
        hard_expiry,
    ):
        self.length = length
        self.symbols = symbols
        self.numbers = numbers
        self.uppercase = uppercase
        self.lowercase = lowercase
        self.allow_change = allow_change
        self.expiration = expiration
        self.max_age = max_age
        self.reuse_prevention = reuse_prevention
        self.hard_expiry = hard_expiry


iam_client = IAM(current_audit_info)

providers/aws/services/iam/__init__.py METASEP

providers/aws/services/ec2/ec2_service.py METASEP
import threading
from dataclasses import dataclass

from lib.logger import logger
from providers.aws.aws_provider import current_audit_info, generate_regional_clients


################## EC2
class EC2:
    def __init__(self, audit_info):
        self.service = "ec2"
        self.session = audit_info.audit_session
        self.audited_account = audit_info.audited_account
        self.regional_clients = generate_regional_clients(self.service, audit_info)
        self.instances = []
        self.__threading_call__(self.__describe_instances__)
        self.security_groups = []
        self.__threading_call__(self.__describe_security_groups__)
        self.network_acls = []
        self.__threading_call__(self.__describe_network_acls__)
        self.snapshots = []
        self.__threading_call__(self.__describe_snapshots__)
        self.__get_snapshot_public__()

    def __get_session__(self):
        return self.session

    def __threading_call__(self, call):
        threads = []
        for regional_client in self.regional_clients.values():
            threads.append(threading.Thread(target=call, args=(regional_client,)))
        for t in threads:
            t.start()
        for t in threads:
            t.join()

    def __describe_instances__(self, regional_client):
        logger.info("EC2 - Describing EC2 Instances...")
        try:
            describe_instances_paginator = regional_client.get_paginator(
                "describe_instances"
            )
            for page in describe_instances_paginator.paginate():
                for reservation in page["Reservations"]:
                    for instance in reservation["Instances"]:
                        if (
                            "PublicDnsName" in instance
                            and "PublicIpAddress" in instance
                        ):
                            self.instances.append(
                                Instance(
                                    instance["InstanceId"],
                                    regional_client.region,
                                    instance["InstanceType"],
                                    instance["ImageId"],
                                    instance["LaunchTime"],
                                    instance["PrivateDnsName"],
                                    instance["PrivateIpAddress"],
                                    instance["PublicDnsName"],
                                    instance["PublicIpAddress"],
                                )
                            )
                        else:
                            self.instances.append(
                                Instance(
                                    instance["InstanceId"],
                                    regional_client.region,
                                    instance["InstanceType"],
                                    instance["ImageId"],
                                    instance["LaunchTime"],
                                    instance["PrivateDnsName"],
                                    instance["PrivateIpAddress"],
                                    None,
                                    None,
                                )
                            )
        except Exception as error:
            logger.error(
                f"{regional_client.region} -- {error.__class__.__name__}: {error}"
            )

    def __describe_security_groups__(self, regional_client):
        logger.info("EC2 - Describing Security Groups...")
        try:
            describe_security_groups_paginator = regional_client.get_paginator(
                "describe_security_groups"
            )
            for page in describe_security_groups_paginator.paginate():
                for sg in page["SecurityGroups"]:
                    self.security_groups.append(
                        SecurityGroup(
                            sg["GroupName"],
                            regional_client.region,
                            sg["GroupId"],
                            sg["IpPermissions"],
                            sg["IpPermissionsEgress"],
                        )
                    )
        except Exception as error:
            logger.error(
                f"{regional_client.region} -- {error.__class__.__name__}: {error}"
            )

    def __describe_network_acls__(self, regional_client):
        logger.info("EC2 - Describing Security Groups...")
        try:
            describe_network_acls_paginator = regional_client.get_paginator(
                "describe_network_acls"
            )
            for page in describe_network_acls_paginator.paginate():
                for nacl in page["NetworkAcls"]:
                    self.network_acls.append(
                        NetworkACL(
                            nacl["NetworkAclId"],
                            regional_client.region,
                            nacl["Entries"],
                        )
                    )
        except Exception as error:
            logger.error(
                f"{regional_client.region} -- {error.__class__.__name__}: {error}"
            )

    def __describe_snapshots__(self, regional_client):
        logger.info("EC2 - Describing Snapshots...")
        try:
            describe_snapshots_paginator = regional_client.get_paginator(
                "describe_snapshots"
            )
            encrypted = False
            for page in describe_snapshots_paginator.paginate(
                OwnerIds=[self.audited_account]
            ):
                for snapshot in page["Snapshots"]:
                    if snapshot["Encrypted"]:
                        encrypted = True
                    self.snapshots.append(
                        Snapshot(
                            snapshot["SnapshotId"], regional_client.region, encrypted
                        )
                    )
        except Exception as error:
            logger.error(
                f"{regional_client.region} -- {error.__class__.__name__}: {error}"
            )

    def __get_snapshot_public__(self):
        logger.info("EC2 - Get snapshots encryption...")
        try:
            for snapshot in self.snapshots:
                regional_client = self.regional_clients[snapshot.region]
                snapshot_public = regional_client.describe_snapshot_attribute(
                    Attribute="createVolumePermission", SnapshotId=snapshot.id
                )
                for permission in snapshot_public["CreateVolumePermissions"]:
                    if "Group" in permission:
                        if permission["Group"] == "all":
                            snapshot.public = True
        except Exception as error:
            logger.error(f"{error.__class__.__name__}: {error}")


@dataclass
class Instance:
    id: str
    region: str
    type: str
    image_id: str
    launch_time: str
    private_dns: str
    private_ip: str
    public_dns: str
    public_ip: str

    def __init__(
        self,
        id,
        region,
        type,
        image_id,
        launch_time,
        private_dns,
        private_ip,
        public_dns,
        public_ip,
    ):
        self.id = id
        self.region = region
        self.type = type
        self.image_id = image_id
        self.launch_time = launch_time
        self.private_dns = private_dns
        self.private_ip = private_ip
        self.public_dns = public_dns
        self.public_ip = public_ip


@dataclass
class Snapshot:
    id: str
    region: str
    encrypted: bool
    public: bool

    def __init__(self, id, region, encrypted):
        self.id = id
        self.region = region
        self.encrypted = encrypted
        self.public = False


@dataclass
class SecurityGroup:
    name: str
    region: str
    id: str
    ingress_rules: list[dict]
    egress_rules: list[dict]

    def __init__(self, name, region, id, ingress_rules, egress_rules):
        self.name = name
        self.region = region
        self.id = id
        self.ingress_rules = ingress_rules
        self.egress_rules = egress_rules


@dataclass
class NetworkACL:
    id: str
    entries: list[dict]

    def __init__(self, id, region, entries):
        self.id = id
        self.region = region
        self.entries = entries


ec2_client = EC2(current_audit_info)

providers/aws/services/ec2/__init__.py METASEP

providers/aws/lib/arn/error.py METASEP
class RoleArnParsingFailedMissingFields(Exception):
    # The arn contains a numberof fields different than six separated by :"
    def __init__(self):
        self.message = "The assumed role arn contains a number of fields different than six separated by :, please input a valid arn"
        super().__init__(self.message)


class RoleArnParsingIAMRegionNotEmpty(Exception):
    # The arn contains a non-empty value for region, since it is an IAM arn is not valid
    def __init__(self):
        self.message = "The assumed role arn contains a non-empty value for region, since it is an IAM arn is not valid, please input a valid arn"
        super().__init__(self.message)


class RoleArnParsingPartitionEmpty(Exception):
    # The arn contains an empty value for partition
    def __init__(self):
        self.message = "The assumed role arn does not contain a value for partition, please input a valid arn"
        super().__init__(self.message)


class RoleArnParsingServiceNotIAM(Exception):
    def __init__(self):
        self.message = "The assumed role arn contains a value for service distinct than iam, please input a valid arn"
        super().__init__(self.message)


class RoleArnParsingInvalidAccountID(Exception):
    def __init__(self):
        self.message = "The assumed role arn contains a value for account id empty or invalid, a valid account id must be composed of 12 numbers, please input a valid arn"
        super().__init__(self.message)


class RoleArnParsingInvalidResourceType(Exception):
    def __init__(self):
        self.message = "The assumed role arn contains a value for resource type different than role, please input a valid arn"
        super().__init__(self.message)


class RoleArnParsingEmptyResource(Exception):
    def __init__(self):
        self.message = "The assumed role arn does not contain a value for resource, please input a valid arn"
        super().__init__(self.message)

providers/aws/lib/arn/arn_test.py METASEP
import sure  # noqa

from providers.aws.lib.arn.arn import arn_parsing

ACCOUNT_ID = "123456789012"
RESOURCE_TYPE = "role"
IAM_ROLE = "test-role"


class Test_ARN_Parsing:
    def test_arn_parsing(self):
        test_cases = [
            {
                "input_arn": f"arn:aws:iam::{ACCOUNT_ID}:{RESOURCE_TYPE}/{IAM_ROLE}",
                "expected": {
                    "partition": "aws",
                    "service": "iam",
                    "region": None,
                    "account_id": ACCOUNT_ID,
                    "resource_type": RESOURCE_TYPE,
                    "resource": IAM_ROLE,
                },
            }
        ]
        for test in test_cases:
            input_arn = test["input_arn"]
            parsed_arn = arn_parsing(input_arn)
            parsed_arn.partition.should.equal(test["expected"]["partition"])
            parsed_arn.service.should.equal(test["expected"]["service"])
            parsed_arn.region.should.equal(test["expected"]["region"])
            parsed_arn.account_id.should.equal(test["expected"]["account_id"])
            parsed_arn.resource_type.should.equal(test["expected"]["resource_type"])
            parsed_arn.resource.should.equal(test["expected"]["resource"])

providers/aws/lib/arn/arn.py METASEP
from arnparse import arnparse

from providers.aws.lib.arn.error import (
    RoleArnParsingEmptyResource,
    RoleArnParsingFailedMissingFields,
    RoleArnParsingIAMRegionNotEmpty,
    RoleArnParsingInvalidAccountID,
    RoleArnParsingInvalidResourceType,
    RoleArnParsingPartitionEmpty,
    RoleArnParsingServiceNotIAM,
)


def arn_parsing(arn):
    # check for number of fields, must be six
    if len(arn.split(":")) != 6:
        raise RoleArnParsingFailedMissingFields
    else:
        arn_parsed = arnparse(arn)
        # First check if region is empty (in IAM arns region is always empty)
        if arn_parsed.region != None:
            raise RoleArnParsingIAMRegionNotEmpty
        else:
            # check if needed fields are filled:
            # - partition
            # - service
            # - account_id
            # - resource_type
            # - resource
            if arn_parsed.partition == None:
                raise RoleArnParsingPartitionEmpty
            elif arn_parsed.service != "iam":
                raise RoleArnParsingServiceNotIAM
            elif (
                arn_parsed.account_id == None
                or len(arn_parsed.account_id) != 12
                or not arn_parsed.account_id.isnumeric()
            ):
                raise RoleArnParsingInvalidAccountID
            elif arn_parsed.resource_type != "role":
                raise RoleArnParsingInvalidResourceType
            elif arn_parsed.resource == "":
                raise RoleArnParsingEmptyResource
            else:
                return arn_parsed

providers/aws/lib/arn/__init__.py METASEP

contrib/terraform-kickstarter/docs/tf.md METASEP
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 3.54 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 3.55.0 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [aws_cloudwatch_event_rule.prowler_check_scheduler_event](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |
| [aws_cloudwatch_event_target.run_prowler_scan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |
| [aws_codebuild_project.prowler_codebuild](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project) | resource |
| [aws_iam_policy.prowler_event_trigger_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.prowler_kickstarter_iam_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy_attachment.prowler_event_trigger_policy_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
| [aws_iam_policy_attachment.prowler_kickstarter_iam_policy_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
| [aws_iam_role.prowler_event_trigger_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.prowler_kick_start_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_s3_bucket.prowler_report_storage_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_policy.prowler_report_storage_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.prowler_report_storage_bucket_block_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_securityhub_account.securityhub_resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_account) | resource |
| [aws_securityhub_product_subscription.security_hub_enable_prowler_findings](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_product_subscription) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy.SecurityAudit](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_codebuild_timeout"></a> [codebuild\_timeout](#input\_codebuild\_timeout) | Codebuild timeout setting | `number` | `300` | no |
| <a name="input_enable_security_hub"></a> [enable\_security\_hub](#input\_enable\_security\_hub) | Enable AWS SecurityHub. | `bool` | `true` | no |
| <a name="input_enable_security_hub_prowler_subscription"></a> [enable\_security\_hub\_prowler\_subscription](#input\_enable\_security\_hub\_prowler\_subscription) | Enable a Prowler Subscription. | `bool` | `true` | no |
| <a name="input_prowler_cli_options"></a> [prowler\_cli\_options](#input\_prowler\_cli\_options) | Run Prowler With The Following Command | `string` | `"_q _M json_asff _S _f us_east_1"` | no |
| <a name="input_prowler_schedule"></a> [prowler\_schedule](#input\_prowler\_schedule) | Run Prowler based on cron schedule | `string` | `"cron(0 0 ? * * *)"` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_account_id"></a> [account\_id](#output\_account\_id) | TODO Move these to outputs file |

contrib/other-contrib/multi-account/megaprowler.sh METASEP
#!/bin/bash

BASEDIR=$(dirname "${0}")
# source the configuration data from "config" in this directory
if [[ -f "${BASEDIR}/config" ]]; then
  # shellcheck disable=SC1090
  . "${BASEDIR}/config"

else
  echo "CONFIG file missing - ${BASEDIR}/config"
  exit 255
fi

## Check Environment variables which are set by config
if [[ "${ORG_MASTERS}X" == "X" && "${STANDALONE_ACCOUNTS}X" == "X" ]]; then
  echo "No audit targets specified. Failing."
  exit 15
fi
if [[ -z $SKIP_ACCOUNTS_REGEX ]]; then
  SKIP_ACCOUNTS_REGEX=""
fi

if [[ -z $CHECKGROUP ]]; then
  echo "Missing check group from config file"
  exit 255
fi
if [[ -z $AUDIT_ROLE ]]; then
  echo "Missing audit role from config file"
  exit 255
fi

## ========================================================================================

## Check Arguments
if [ $# -lt 1 ]; then
  echo "NEED AN OUTPUT DIRECTORY"
  exit 2
else
  if [[ -d $1 && -w $1 ]]; then
    OUTBASE=$1
  else
    echo "Output directory missing or write-protected"
    exit 1
  fi
fi


## Check Requirements
if [[ -x $(command -v aws) ]]; then
  aws --version
else
  echo "AWS CLI is not in PATH ... giving up"
  exit 4
fi

if [[ -x $(command -v jq) ]]; then
  jq --version
else
  echo "JQ is not in PATH ... giving up"
  exit 4
fi

# Ensure AWS Credentials are present in environment
if [[ -z $CREDSOURCE ]]; then
  echo "No source for base credentials ... giving up"
  exit 5
fi

if [[ -f ${PROWLER} && -x ${PROWLER} ]]; then
  ${PROWLER} -V
else
  echo "Unable to execute prowler from ${PROWLER}"
  exit 3
fi


## Preflight checks complete

DAYPATH=$(date -u +%Y/%m/%d)
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
## Create output subdirs
OUTDATA="${OUTBASE}/data/${DAYPATH}"
OUTLOGS="${OUTBASE}/logs/${DAYPATH}"
mkdir -p "${OUTDATA}" "${OUTLOGS}"


if [[ -x $(command -v parallel) ]]; then
  # Note: the "standard" codebuild container includes parallel
  echo "Using GNU sem/parallel, with NCPU+4 jobs"
  parallel --citation > /dev/null 2> /dev/null
  PARALLEL_START="parallel --semaphore --fg --id p_${STAMP} --jobs +4 --env AWS_SHARED_CREDENTIALS_FILE"
  PARALLEL_START_SUFFIX=''
  PARALLEL_END="parallel --semaphore --wait --id p_${STAMP}"
else
  echo "Consider installing GNU Parallel to avoid punishing your system"
  PARALLEL_START=''
  PARALLEL_START_SUFFIX=' &'
  # shellcheck disable=SC2089
  PARALLEL_END="echo 'WAITING BLINDLY FOR PROCESSES TO COMPLETE'; wait ; sleep 30 ; wait"
fi

echo "Execution Timestamp: ${STAMP}"

ALL_ACCOUNTS=""


# Create a temporary credential file
AWS_MASTERS_CREDENTIALS_FILE=$(mktemp -t prowler.masters-XXXXXX)
echo "Preparing Credentials ${AWS_MASTERS_CREDENTIALS_FILE} ( ${CREDSOURCE} )"
echo "# Master Credentials ${STAMP}"   >> "${AWS_MASTERS_CREDENTIALS_FILE}"
echo ""                                >> "${AWS_MASTERS_CREDENTIALS_FILE}"

AWS_TARGETS_CREDENTIALS_FILE=$(mktemp -t prowler.targets-XXXXXX)
echo "Preparing Credentials ${AWS_TARGETS_CREDENTIALS_FILE} ( ${CREDSOURCE} )"
echo "# Target Credentials ${STAMP}" >> "${AWS_TARGETS_CREDENTIALS_FILE}"
echo ""                              >> "${AWS_TARGETS_CREDENTIALS_FILE}"


## Visit the Organization Master accounts & build a list of all member accounts
export AWS_SHARED_CREDENTIALS_FILE=$AWS_MASTERS_CREDENTIALS_FILE
for org in $ORG_MASTERS ; do
  echo -n "Preparing organization $org "
  # create credential profile
  {
  echo "[audit_${org}]"
  echo "role_arn = arn:aws:iam::${org}:role${AUDIT_ROLE}"
  echo "credential_source = ${CREDSOURCE}"
  echo ""
  } >> "${AWS_MASTERS_CREDENTIALS_FILE}"

  # Get the Organization ID to use for output paths, collecting info, etc
  org_id=$(aws --output json --profile "audit_${org}" organizations describe-organization | jq -r '.Organization.Id' )

  echo "( $org_id )"
  ORG_ID_LIST="${ORG_ID_LIST} ${org_id}"


  # Build the list of all accounts in the organizations
  aws --output json --profile "audit_${org}" organizations list-accounts > "${OUTLOGS}/${STAMP}-${org_id}-account-list.json"
  # shellcheck disable=SC2002
  ORG_ACCOUNTS=$( cat "${OUTLOGS}/${STAMP}-${org_id}-account-list.json" | jq -r '.Accounts[].Id' | tr "\n" " ")
  ALL_ACCOUNTS="${ALL_ACCOUNTS} ${ORG_ACCOUNTS}"

  # Add the Org's Accounts (including master) to the TARGETS_CREDENTIALS file
  for target in $ORG_ACCOUNTS ; do
    if echo "$target" | grep -qE "${SKIP_ACCOUNTS_REGEX}"; then
      echo " skipping account      ${target} ( ${org_id} )"
      continue
    fi
    # echo "  ${org_id}_${target}"
    {
    echo "[${org_id}_${target}]"
    echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}"
    echo "credential_source = ${CREDSOURCE}"
    echo ""
    } >> "${AWS_TARGETS_CREDENTIALS_FILE}"
  done

done

# Prepare credentials for standalone accounts
if [[ "" != "${STANDALONE_ACCOUNTS}" ]] ; then
  # mkdir -p ${OUTBASE}/data/standalone/${DAYPATH} ${OUTBASE}/logs/standalone/${DAYPATH}
  for target in $STANDALONE_ACCOUNTS ; do
    echo "Preparing account      ${target} ( standalone )"
    {
    echo "[standalone_${target}]"
    echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}"
    echo "credential_source = ${CREDSOURCE}"
    echo ""
    } >> "${AWS_TARGETS_CREDENTIALS_FILE}"
  done
  ALL_ACCOUNTS="${ALL_ACCOUNTS} ${STANDALONE_ACCOUNTS}"
fi

# grep -E '^\[' $AWS_MASTERS_CREDENTIALS_FILE $AWS_TARGETS_CREDENTIALS_FILE


# Switch to Target Credential Set
export AWS_SHARED_CREDENTIALS_FILE=${AWS_TARGETS_CREDENTIALS_FILE}

## visit each target account
NUM_ACCOUNTS=$(grep -cE '^\[' "${AWS_TARGETS_CREDENTIALS_FILE}")
echo "Launching ${CHECKGROUP} audit of ${NUM_ACCOUNTS} accounts"
for member in $(grep -E '^\[' "${AWS_TARGETS_CREDENTIALS_FILE}" | tr -d '][') ; do
  ORG_ID=$(echo "$member" | cut -d'_' -f1)
  ACCOUNT_NUM=$(echo "$member" | cut -d'_' -f2)

  # shellcheck disable=SC2086
  ${PARALLEL_START} "${PROWLER} -p ${member} -n -M csv -g ${CHECKGROUP} 2> ${OUTLOGS}/${STAMP}-${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP}.log  > ${OUTDATA}/${STAMP}-${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP}.csv ; echo \"${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP} finished\" " ${PARALLEL_START_SUFFIX}
done

echo -n "waiting for parallel threads to complete - " ; date
# shellcheck disable=SC2090
${PARALLEL_END}

echo "Completed ${CHECKGROUP} audit with stamp ${STAMP}"

# mkdir -p ${OUTBASE}/logs/debug/${DAYPATH}
# cp "$AWS_MASTERS_CREDENTIALS_FILE" "${OUTLOGS}/${STAMP}-master_creds.txt"
# cp "$AWS_TARGETS_CREDENTIALS_FILE" "${OUTLOGS}/${STAMP}-target_creds.txt"
rm "$AWS_MASTERS_CREDENTIALS_FILE" "$AWS_TARGETS_CREDENTIALS_FILE"

contrib/other-contrib/multi-account/config METASEP
#!/bin/bash
########### CODEBUILD CONFIGURATION ##################
# shellcheck disable=SC2034
## Collect environment parameters set by buildspec
CHECKGROUP=${PROWL_CHECK_GROUP}

if [ "none" == "${PROWL_MASTER_ACCOUNTS}" ]; then
  ORG_MASTERS=""
else
  ORG_MASTERS=$(echo "${PROWL_MASTER_ACCOUNTS}" | tr "," " ")
fi

if [ "none" == "${PROWL_STANDALONE_ACCOUNTS}" ]; then
  STANDALONE_ACCOUNTS=""
else
  STANDALONE_ACCOUNTS=$(echo "${PROWL_STANDALONE_ACCOUNTS}" | tr "," " ")
fi

if [ "none" == "${PROWL_SKIP_ACCOUNTS}" ]; then
  SKIP_ACCOUNTS_REGEX='^$'
else
  skip_inside=$(echo "${PROWL_SKIP_ACCOUNTS}" | tr "," "|")
  # shellcheck disable=SC2116
  SKIP_ACCOUNTS_REGEX=$(echo "(${skip_inside})" )
fi

AUDIT_ROLE=${PROWL_AUDIT_ROLE}

# Adjust if you clone prowler from somewhere other than the default location
PROWLER='prowler/prowler'

# Change this if you want to ensure it breaks in code build
CREDSOURCE='EcsContainer'

contrib/other-contrib/multi-account/Audit_Pipeline.yaml METASEP
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Prowler Auditing Tools Stack

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Organizations and Accounts"
        Parameters:
          - pOrgMasterAccounts
          - pOrgExcludedAccounts
          - pStandAloneAccounts
      - Label:
          default: "Check Group and Execution"
        Parameters:
          - pProwlerCheckGroup
          - pAuditEveryXHours
      - Label:
          default: "Advanced"
        Parameters:
          - pTimeoutMinutes
          - pAuditRolePathName
          - pCustomProwlerRepo
          - pCustomProwlerCloneArgs
    ParameterLabels:
      pOrgMasterAccounts:
        default: "Organization Master Accounts"
      pOrgExcludedAccounts:
        default: "Excluded Organiztion Members"
      pStandAloneAccounts:
        default: "Stand-alone Accounts"
      pProwlerCheckGroup:
        default: "Prowler Check Group"
      pAuditEveryXHours:
        default: "Perform Audit every X hours"
      pTimeoutMinutes:
        default: "Permit Audit to run for X minutes"
      pAuditRolePathName:
        default: "Custom audit role path"
      pCustomProwlerRepo:
        default: "Custom git repo location for prowler"
      pCustomProwlerCloneArgs:
        default: "Custom arguments to git clone --depth 1"

Parameters:
  pAuditEveryXHours:
    Default: 24
    Type: Number
    Description: Number of hours between prowler audit runs.
    MinValue: 2
    MaxValue: 168
  pTimeoutMinutes:
    Default: 30
    Type: Number
    Description: Timeout for running prowler across the fleet
    MinValue: 5
    MaxValue: 480
  pAuditRolePathName:
    Default: '/audit/prowler/XA_AuditRole_Prowler'
    Type: String
    Description: Role path and name which prowler will assume in the target accounts (Audit_Exec_Role.yaml)
    # TODO: Validation: begins with "/" and does NOT end with "/"
  pOrgMasterAccounts:
    Description: Comma Separated list of Organization Master Accounts, or 'none'
    Default: 'none'
    Type: String
    MinLength: 4
    AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$
    ConstraintDescription: comma separated list 12-digit account numbers, or 'none'
  pOrgExcludedAccounts: # Comma Separated list of Org Member Accounts to EXCLUDE
    Description: Comma Separated list of Skipped Organization Member Accounts, or 'none'
    Default: 'none'
    Type: String
    MinLength: 4
    AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$
    ConstraintDescription: comma separated list 12-digit account numbers, or 'none'
  pStandAloneAccounts: # Comma Separated list of Stand-Alone Accounts
    Description: Comma Separated list of Stand-alone Accounts, or 'none'
    Default: 'none'
    Type: String
    MinLength: 4
    AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$
    ConstraintDescription: comma separated list 12-digit account numbers, or 'none'
  pProwlerCheckGroup:
    Default: 'cislevel1'
    Type: String
    Description: Which group of checks should prowler run
    AllowedValues:
     - 'group1'
     - 'group2'
     - 'group3'
     - 'group4'
     - 'cislevel1'
     - 'cislevel2'
     - 'extras'
     - 'forensics-ready'
     - 'gdpr'
     - 'hipaa'
     - 'secrets'
     - 'apigateway'
     - 'rds'
  pCustomProwlerRepo:
    Type: String
    Default: 'https://github.com/prowler-cloud/prowler.git'
    MinLength: 10
  pCustomProwlerCloneArgs:
    Type: String
    Default: '--branch master'
    MinLength: 0
  ##### TODO
  # pResultsBucket: # if specified, use an existing bucket for the data
  # pEnableAthena:
  #   Default: false
  #   Type: Boolean
  #   Description: Set to true to enable creation of Athena/QuickSight resources

#### TODO
# Conditions:
#   cUseAthena: False

Resources:

  # S3 Bucket for Results, Config
  ProwlerResults:
    Type: "AWS::S3::Bucket"
    Properties:
      # BucketName: !Sub "audit-results-${AWS::AccountId}"
      Tags:
        - Key: "data-type"
          Value: "it-audit:sensitive"
        - Key: "data-public"
          Value: "NO"
      AccessControl: Private
      BucketEncryption:
        ServerSideEncryptionConfiguration:
            - ServerSideEncryptionByDefault:
                SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      # LoggingConfiguration:
      # TODO: Enable BucketLogging - requires more parameters
    DeletionPolicy: "Retain"
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W35
            reason: "Bucket logging requires additional configuration not yet supported by this template"

  # Policy to allow assuming the XA_AuditRole_Prowler in target accounts
  ProwlerAuditManagerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: AuditManagerRole_Prowler
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: AssumeRole-XA_AuditRole_Prowler
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - sts:AssumeRole
                Resource:
                  - !Sub "arn:aws:iam::*:role${pAuditRolePathName}"
              - Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetObjectVersion
                Resource:
                  - !Sub "${ProwlerResults.Arn}/*"
              - Effect: Allow
                Action:
                  - s3:ListBucket
                  - s3:HeadBucket
                  - s3:GetBucketLocation
                  - s3:GetBucketAcl
                Resource:
                  - !Sub "${ProwlerResults.Arn}"
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource:
                  - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*"
                  - !Sub "${ProwlerResults.Arn}"
              - Effect: Allow
                Action:
                  - ssm:GetParameters
                Resource:
                  - !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/audit/prowler/config/*"
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W28
            reason: "the role name is intentionally static"
          - id: W11
            reason: "not sure where the violation of w11 is"

  ## Code Build Job
  ProwlerBuildProject:
    Type: "AWS::CodeBuild::Project"
    Properties:
      Name: PerformProwlerAudit
      Description: "Run Prowler audit on accounts in targeted organizations"
      QueuedTimeoutInMinutes: 480
      TimeoutInMinutes: !Ref pTimeoutMinutes
      ServiceRole: !Ref ProwlerAuditManagerRole
      EncryptionKey: !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:alias/aws/s3"
      Environment:
        Type: "LINUX_CONTAINER"
        ComputeType: "BUILD_GENERAL1_MEDIUM"
        PrivilegedMode: False
        Image: "aws/codebuild/standard:2.0-1.12.0"
        ImagePullCredentialsType: "CODEBUILD"
      Artifacts: # s3://stack-prowlerresults-randomness/prowler/results/...
        Name: "results"
        Type: "S3"
        Location: !Ref ProwlerResults
        Path: "prowler"
        NamespaceType: NONE
        Packaging: NONE
        OverrideArtifactName: False
        EncryptionDisabled: False
      LogsConfig: # S3/logs/pipeline/
        CloudWatchLogs:
          Status: ENABLED
          GroupName: "audit/prowler"
          StreamName: "codebuild_runs"
        S3Logs:
          Status: DISABLED
          # Location: !Sub "${ProwlerResults.Arn}/codebuild_run_logs"
          EncryptionDisabled: False
      BadgeEnabled: False
      Tags:
        - Key: "data-type"
          Value: "it-audit:sensitive"
        - Key: "data-public"
          Value: "NO"
      Cache:
        Type: "NO_CACHE"
      Source:
        Type: NO_SOURCE
        BuildSpec: |
          version: 0.2
          env:
            parameter-store:
              PROWL_CHECK_GROUP:         /audit/prowler/config/check_group
              PROWL_MASTER_ACCOUNTS:     /audit/prowler/config/orgmaster_accounts
              PROWL_STANDALONE_ACCOUNTS: /audit/prowler/config/standalone_accounts
              PROWL_SKIP_ACCOUNTS:       /audit/prowler/config/skip_accounts
              PROWL_AUDIT_ROLE:          /audit/prowler/config/audit_role
              PROWLER_REPO:              /audit/prowler/config/gitrepo
              PROWLER_CLONE_ARGS:        /audit/prowler/config/gitcloneargs
          phases:
            install:
              runtime-versions:
                python: 3.7
              commands:
                - aws --version
                - git clone --depth 1 $PROWLER_REPO $PROWLER_CLONE_ARGS
            pre_build:
              commands:
                - env | grep PROWL_
                - export OUTBASE=$(date -u +"out/diagnostics/%Y/%m/%d")
                - export STAMP=$(date -u +"%Y%m%dT%H%M%SZ")
                - mkdir -p $OUTBASE || true
                - prowler/prowler -V
                - aws sts get-caller-identity > ${OUTBASE}/${STAMP}-caller-id.json
            build:
              commands:
                #### Run Prowler against this account, but don't fail the build
                # - export PROWLER_ACCOUNT_ID=$(aws sts get-caller-identity | jq -r '.Account')
                # - /bin/bash prowler/prowler -g cislevel1 -M csv -n -k > ${OUTBASE}/${STAMP}.${PROWLER_ACCOUNT_ID}.prowler.cislevel1.csv || /bin/true
                # - /bin/bash prowler/prowler -g forensics-ready -M csv -n -k > ${OUTBASE}/${STAMP}.${PROWLER_ACCOUNT_ID}.prowler.forensics-ready.csv || /bin/true
                #### Run Prowler targeting all accounts in the configured organizations
                - test -f prowler/util/multi-account/config
                - /bin/bash prowler/util/multi-account/megaprowler.sh out
              finally:
                - ps axuwww | grep -E 'parallel|sem|prowler'
            post_build:
              commands:
                - echo "attempting to collect any prowler credential reports ..."
                - find /tmp/ -name prowler\* | xargs -I % cp % ${OUTDIAG} || true
          artifacts:
            files:
              - '**/*'
            discard-paths: no
            base-directory: out

  ProwlerAuditTriggerRole:
    Type: AWS::IAM::Role
    Properties:
      # RoleName: Let cloudformation create this
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: AssumeRole-XA_AuditRole_Prowler
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - codebuild:StartBuild
                Resource:
                  - !GetAtt ProwlerBuildProject.Arn

  ProwlerAuditTrigger:
    Type: AWS::Events::Rule
    Properties:
      Description: !Sub "Execute Prowler audit every ${pAuditEveryXHours} hours"
      Name: "ScheduledProwler"
      RoleArn: !GetAtt ProwlerAuditTriggerRole.Arn
      ## Other ways to define scheduling
      # ScheduleExpression: "cron(MM HH ? * * *)"
      # ScheduleExpression: "cron(45 15 ? * * *)"
      # ScheduleExpression: !Sub "rate( ${pAuditEveryXHours} hours)"
      ScheduleExpression: !Sub "rate(${pAuditEveryXHours} hours)"
      State: ENABLED
      Targets:
        - Arn: !GetAtt ProwlerBuildProject.Arn
          Id: 'ScheduledProwler'
          RoleArn: !GetAtt ProwlerAuditTriggerRole.Arn

  ProwlerConfigCheckGroup:
    Type: AWS::SSM::Parameter
    Properties:
      Description: "Name of the prowler check group to use"
      Name: "/audit/prowler/config/check_group"
      Type: "String"
      Value: !Ref pProwlerCheckGroup

  ProwlerConfigMasterAccounts:
    Type: AWS::SSM::Parameter
    Properties:
      Description: "List of organization master accounts"
      Name: "/audit/prowler/config/orgmaster_accounts"
      Type: "String"
      Value: !Ref pOrgMasterAccounts

  ProwlerConfigStandAloneAccounts:
    Type: AWS::SSM::Parameter
    Properties:
      Description: "List of stand-alone accounts"
      Name: "/audit/prowler/config/standalone_accounts"
      Type: "String"
      Value: !Ref pStandAloneAccounts

  ProwlerConfigSkipAccounts:
    Type: AWS::SSM::Parameter
    Properties:
      Description: "List of skipped organization member accounts"
      Name: "/audit/prowler/config/skip_accounts"
      Type: "String"
      Value: !Ref pOrgExcludedAccounts

  ProwlerConfigAuditRole:
    Type: AWS::SSM::Parameter
    Properties:
      Description: "Role used to audit target accounts"
      Name: "/audit/prowler/config/audit_role"
      Type: "String"
      Value: !Ref pAuditRolePathName

  ProwlerConfigGitRepo:
    Type: AWS::SSM::Parameter
    Properties:
      Description: "Git repository where prowler is gathered"
      Name: "/audit/prowler/config/gitrepo"
      Type: "String"
      Value: !Ref pCustomProwlerRepo

  ProwlerConfigGitCloneArgs:
    Type: AWS::SSM::Parameter
    Properties:
      Description: "Git clone arguments"
      Name: "/audit/prowler/config/gitcloneargs"
      Type: "String"
      Value: !Ref pCustomProwlerCloneArgs


  # -- Conditional "cUseAthena"
  # Athena
  # QuickSight
  # ???


Outputs:
  ResultsBucket:
    Description: S3 Bucket with Prowler Results, Logs, Configs
    Value: !Ref ProwlerResults

contrib/other-contrib/multi-account/Audit_Exec_Role.yaml METASEP
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Prowler Auditing Role - in Control Tower pick AWSControlTowerStackSetRole for IAM role and AWSControlTowerExecution for execution

Parameters:

  AuditorAccountId:
    Default: 987600001234
    Description: AWS Account ID where the audit tooling executes
    Type: Number
  AuditRolePathName:
    Default: '/audit/prowler/XA_AuditRole_Prowler'
    Description: Path for role name in audit tooling account
    Type: String

Resources:
  XAAuditRole:
    Type: "AWS::IAM::Role"
    Properties: # /audit/prowler/XA_AuditRole_Prowler
      RoleName: XA_AuditRole_Prowler
      Path: "/audit/prowler/"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/SecurityAudit
        - arn:aws:iam::aws:policy/AWSOrganizationsReadOnlyAccess
        - arn:aws:iam::aws:policy/IAMReadOnlyAccess
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS: # TODO: review permissions to see if this can be narrowed down - code build only perhaps
                - !Sub "arn:aws:iam::${AuditorAccountId}:root"
            Action:
              - "sts:AssumeRole"
          - Effect: "Allow"
            Principal:
              Service:
                - "codebuild.amazonaws.com"
            Action:
              - "sts:AssumeRole"
            # TODO: restrict to only AuditorAccount only
      Policies:
        - PolicyName: "ProwlerPolicyAdditions"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: "ProwlerPolicyAdditions"
                Effect: "Allow"
                Resource: "*"
                Action:
                  - "acm:describecertificate"
                  - "acm:listcertificates"
                  - "apigateway:GET"
                  - "cloudtrail:GetEventSelectors"
                  - "ec2:GetEbsEncryptionByDefault"
                  - "es:describeelasticsearchdomainconfig"
                  - "guardduty:ListDetectors"
                  - "guardduty:GetDetector"
                  - "logs:DescribeLogGroups"
                  - "logs:DescribeMetricFilters"
                  - "s3:GetEncryptionConfiguration"
                  - "ses:getidentityverificationattributes"
                  - "sns:listsubscriptionsbytopic"
                  - "support:*"
                  - "trustedadvisor:Describe*"

    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W28
            reason: "the role name is intentionally static"
          - id: W11
            reason: "the policy grants read/view/audit access only, to all resources, by design"
          - id: F3
            reason: "Support does not allow or deny access to individual actions"

contrib/org-multi-account/src/run-prowler-reports.sh METASEP
#!/bin/bash -e
#
# Run Prowler against All AWS Accounts in an AWS Organization

# Change Directory (rest of the script, assumes your in the ec2-user home directory)
cd /home/ec2-user || exit

# Show Prowler Version, and Download Prowler, if it doesn't already exist
if ! ./prowler/prowler -V 2>/dev/null; then
    git clone https://github.com/prowler-cloud/prowler.git
    ./prowler/prowler -V
fi

# Source .awsvariables (to read in Environment Variables from CloudFormation Data)
# shellcheck disable=SC1091
source .awsvariables

# Get Values from Environment Variables Created on EC2 Instance from CloudFormation Data
echo "S3:             $S3"
echo "S3ACCOUNT:      $S3ACCOUNT"
echo "ROLE:           $ROLE"

# CleanUp Last Ran Prowler Reports, as they are already stored in S3.
rm -rf prowler/output/*.html

# Function to unset AWS Profile Variables
unset_aws() {
    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}
unset_aws

# Find THIS Account AWS Number
CALLER_ARN=$(aws sts get-caller-identity --output text --query "Arn")
PARTITION=$(echo "$CALLER_ARN" | cut -d: -f2)
THISACCOUNT=$(echo "$CALLER_ARN" | cut -d: -f5)
echo "THISACCOUNT:    $THISACCOUNT"
echo "PARTITION:      $PARTITION"

# Function to Assume Role to THIS Account & Create Session
this_account_session() {
    unset_aws
    role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$THISACCOUNT":role/"$ROLE" --role-session-name ProwlerRun --output json)
    AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId)
    AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey)
    AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken)
    export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}

# Find AWS Master Account
this_account_session
AWSMASTER=$(aws organizations describe-organization --query Organization.MasterAccountId --output text)
echo "AWSMASTER:      $AWSMASTER"

# Function to Assume Role to Master Account & Create Session
master_account_session() {
    unset_aws
    role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$AWSMASTER":role/"$ROLE" --role-session-name ProwlerRun --output json)
    AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId)
    AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey)
    AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken)
    export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}

# Lookup All Accounts in AWS Organization
master_account_session
ACCOUNTS_IN_ORGS=$(aws organizations list-accounts --query Accounts[*].Id --output text)

# Function to Assume Role to S3 Account & Create Session
s3_account_session() {
    unset_aws
    role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$S3ACCOUNT":role/"$ROLE" --role-session-name ProwlerRun --output json)
    AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId)
    AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey)
    AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken)
    export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}

# Run Prowler against Accounts in AWS Organization
echo "AWS Accounts in Organization"
echo "$ACCOUNTS_IN_ORGS"
PARALLEL_ACCOUNTS="1"
for accountId in $ACCOUNTS_IN_ORGS; do
    # shellcheck disable=SC2015
    test "$(jobs | wc -l)" -ge $PARALLEL_ACCOUNTS && wait || true
    {
        START_TIME=$SECONDS
        # Unset AWS Profile Variables
        unset_aws
        # Run Prowler
        echo -e "Assessing AWS Account: $accountId, using Role: $ROLE on $(date)"
        # remove -g cislevel for a full report and add other formats if needed
        ./prowler/prowler -R "$ROLE" -A "$accountId" -g cislevel1 -M html -z
        echo "Report stored locally at: prowler/output/ directory"
        TOTAL_SEC=$((SECONDS - START_TIME))
        echo -e "Completed AWS Account: $accountId, using Role: $ROLE on $(date)"
        printf "Completed AWS Account: $accountId in %02dh:%02dm:%02ds" $((TOTAL_SEC / 3600)) $((TOTAL_SEC % 3600 / 60)) $((TOTAL_SEC % 60))
        echo ""
    } &
done

# Wait for All Prowler Processes to finish
wait
echo "Prowler Assessments Completed against All Accounts in the AWS Organization. Starting S3 copy operations..."

# Upload Prowler Report to S3
s3_account_session
aws s3 cp prowler/output/ "$S3/reports/" --recursive --include "*.html" --acl bucket-owner-full-control
echo "Assessment reports successfully copied to S3 bucket"

# Final Wait for All Prowler Processes to finish
wait
echo "Prowler Assessments Completed"

# Unset AWS Profile Variables
unset_aws

contrib/org-multi-account/serverless_codebuild/README_kr.md METASEP
# Organizational Prowler with Serverless

Langage: [English](README.md)

이 문서는 AWS Organization 내의 multi account 환경에서 prowler 를 적용하기 위해 작성된 문서입니다.
일정 시간마다 CloudWatch는 CodeBuild 를 트리거합니다.
CodeBuild 는 최신의 [prowler](https://github.com/prowler-cloud/prowler) 소스를 클론받고, 
Organization 내의 모든 Account 에 대해 security assessment 를 수행합니다.
prowler 의 자세한 사용방법은 [이 곳](https://github.com/prowler-cloud/prowler#usagee) 을 참고합니다.

![Untitled](docs/images/prowler_org_architecture.png)

1. **Log Archive Account**에 접속합니다.
   1. 아래 템플릿을 CloudFormation console 에서 배포합니다. 이를 통해 prowler 의 security assessment report 가 저장되는 bucket 과 bucket policy 를 생성합니다.

      [ProwlerS3.yaml](templates/ProwlerS3.yaml)

      - AwsOrgId : AWS Organizations의 Organization ID
      - S3Prefix : 생성될 버킷의 이름에 포함되는 prefix
2. **Master Account** 에 접속합니다.
   1. 아래 템플릿을 이용하여 CloudFormation **Stack**을 생성합니다. StackSet은 Master account 에 적용되지 않으므로 Stack 으로도 배포가 필요합니다.

      [ProwlerRole.yaml](templates/ProwlerRole.yaml)

      - ProwlerCodeBuildAccount : CodeBuild 가 있는 Audit Acccount ID
      - ProwlerCodeBuildRole : CodeBuild의 생성될 Role 이름
      - ProwlerCrossAccountRole : Cross account 용 Assume할 Role 이름
      - ProwlerS3 : report 가 저장될 S3 bucket 명
   2. 아래 템플릿을 이용하여 CloudFormation **StackSet**을 생성하여, Organazation에 포함된 account 대상으로도 아래 템플릿을 배포합니다.

      [ProwlerRole.yaml](templates/ProwlerRole.yaml)

      - ProwlerCodeBuildAccount : CodeBuild 가 있는 Audit Acccount
      - ProwlerCodeBuildRole : CodeBuild에서 사용할 Role 이름
      - ProwlerCrossAccountRole : Cross account 용 Assume할 Role 이름
      - ProwlerS3 : report 가 저장될 S3 bucket 명
      - Permission : Service-managed permissions
      - Deploy target : Deploy to organization 선택, Enable, Delete stacks 선택
      - Specify regions : 배포할 대상 리전을 선택
3. **Audit Account**에 접속합니다.
   1. **S3 console** 로 이동하여 버킷을 생성하고 아래 항목을 **업로드**한 후, 버킷명을 복사해둡니다.

      [run-prowler-reports.sh.zip](src/run-prowler-reports.sh.zip)

      - bucket name : prowler-util-*<Account ID>*-*<region>*

        ![Untitled](docs/images/s3_screenshot.png)

   2. 아래 템플릿으로 **CloudFormation stack** 을 생성합니다. 이 템플릿은 CloudWatch Rule 을 생성하여 일정 시간마다 CodeBuild 를 실행하여 prowler 가 multi accounts 를 audit 할 수 있도록 합니다.

      [ProwlerCodeBuildStack.yaml](templates/ProwlerCodeBuildStack.yaml)

      - AwsOrgId : AWS Organizations의 Organization ID
      - CodeBuildRole : CodeBuild의 서비스 Role 이름
      - CodeBuildSourceS3 : a 에서 업로드한 object 위치
         - prowler-util-*<Account ID>*-*<region>/***run-prowler-reports.sh.zip**
      - CrossAccountRole : 2번에서 생성한 Cross Account 용 Role 이름
      - ProwlerReportS3 : report 가 저장될 S3 bucket 명
      - ProwlerReportS3Account : report 가 저장될 S3 bucket이 위치한 Account
   3. 스케줄 된 시간을 변경하고 싶은 경우
      1. [ProwlerCodeBuildStack.yaml](templates/ProwlerCodeBuildStack.yaml) 내에서 ScheduleExpression의 크론 표현식을 변경할 수 있습니다.
      2. 또는 CloudWatch console 에서 Events > Rules > ProwlerExecuteRule > Actions > Edit 에서 직접 변경할 수 있습니다.
contrib/org-multi-account/serverless_codebuild/README.md METASEP
# Organizational Prowler with Serverless

Langage: [Korean](README_kr.md)

This project is created to apply prowler in a multi-account environment within AWS Organizations.
CloudWatch triggers CodeBuild every fixed time.
CodeBuild executes the script which clones the latest prowler from [here](https://github.com/prowler-cloud/prowler) and performs security assessment on all the accounts in AWS Organizations. The assessment reports are sent to S3 bucket in Log Archive Account.

For more information on how to use prowler, see [here](https://github.com/prowler-cloud/prowler#usage).

![Untitled](docs/images/prowler_org_architecture.png)

1. **Log Archive Account**
   1. Deploy [ProwlerS3.yaml](templates/ProwlerS3.yaml) in CloudFormation console.
      The template creates S3 bucket for reports and bucket policy that limits API actions to principals from its AWS Organizations.
      - AwsOrgId : AWS Organizations' Organization ID
      - S3Prefix : The prefix included in the bucket name
2. **Master Account**
   1. Deploy [ProwlerRole.yaml](templates/ProwlerRole.yaml) stack to CloudFormation in a bid to create resources to master account itself.
      (The template will be also deployed for other member accounts as a StackSet)
      - ProwlerCodeBuildAccount :  Audit Acccount ID where CodeBuild resides. (preferably Audit/Security account)
      - ProwlerCodeBuildRole : Role name to use in CodeBuild service
      - ProwlerCrossAccountRole : Role name to assume for Cross account
      - ProwlerS3 : The S3 bucket name where reports will be put
   1. Create **StackSet** with [ProwlerRole.yaml](templates/ProwlerRole.yaml) to deploy Role into member accounts in AWS Organizations.
      - ProwlerCodeBuildAccount :  Audit Acccount ID where CodeBuild resides. (preferably Audit/Security account)
      - ProwlerCodeBuildRole : Role name to use in CodeBuild service
      - ProwlerCrossAccountRole : Role name to assume for Cross account
      - ProwlerS3 : The S3 bucket name where reports will be put
      - Permission : Service-managed permissions
      - Deploy target : Deploy to organization 선택, Enable, Delete stacks 선택
      - Specify regions : Region to deploy
3. **Audit Account**
   1. Go to S3 console, create a bucket, upload [run-prowler-reports.sh.zip](src/run-prowler-reports.sh.zip)
      - bucket name : prowler-util-*[Account ID]*-*[region]*  
     ![Untitled](docs/images/s3_screenshot.png)

   1. Deploy  [ProwlerCodeBuildStack.yaml](templates/ProwlerCodeBuildStack.yaml) which creates CloudWatch Rule to trigger CodeBuild every fixed time, allowing prowler to audit multi-accounts.
      - AwsOrgId : AWS Organizations' Organization ID
      - CodeBuildRole : Role name to use in CodeBuild service
      - CodeBuildSourceS3 : Object location uploaded from i
         - prowler-util-*[Account ID]*-*[region]/**run-prowler-reports.sh.zip**
      - CrossAccountRole : Role name for cross account created in the process **2** above.
      - ProwlerReportS3 : The S3 bucket name where reports will be put
      - ProwlerReportS3Account : The account where the report S3 bucket resides.
   1. If you'd like to change the scheduled time,
      1. You can change the cron expression of ScheduleExpression within [ProwlerCodeBuildStack.yaml](templates/ProwlerCodeBuildStack.yaml).
      2. Alternatively, you can make changes directrly from Events > Rules > ProwlerExecuteRule > Actions > Edit in CloudWatch console.
contrib/multi-account-securityhub/templates/CF-Prowler-IAM.yml METASEP
AWSTemplateFormatVersion: 2010-09-09
Description: This Template will create the IAM Roles needed for the Prowler infrastructure
Parameters:
  ProwlerCrossAccountRoleName:
    Type: String
    Description: Name of the cross account Prowler IAM Role
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerXA-Role
  ECSExecutionRoleName:
    Type: String
    Description: Name for the ECS Task Execution Role
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ECSTaskExecution-Role
  ProwlerTaskRoleName:
    Type: String
    Description: Name for the ECS Prowler Task Role
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerECSTask-Role
  ECSEventRoleName:
    Type: String
    Description: Name for the Eventbridge Task Role
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerEvents-Role
Resources:
  ECSExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref ECSExecutionRoleName
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: ECSExecutionTrust
          Effect: Allow
          Principal:
            Service: ecs-tasks.amazonaws.com
          Action: sts:AssumeRole
  ProwlerTaskRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref ProwlerTaskRoleName
      Policies:
      - PolicyName: ProwlerAssumeRole
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Sid: AllowProwlerAssumeRole
            Effect: Allow
            Action: sts:AssumeRole
            Resource:
            - !Sub arn:aws:iam::*:role/${ProwlerCrossAccountRoleName}
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: ECSExecutionTrust
          Effect: Allow
          Principal:
            Service: ecs-tasks.amazonaws.com
          Action: sts:AssumeRole
  ECSEventRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref ECSEventRoleName
      Policies:
      - PolicyName: AllowProwlerEventsECS
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - ecs:RunTask
            Resource:
            - "*"
            Sid: EventRunECS
          - Effect: Allow
            Action: iam:PassRole
            Resource:
            - "*"
            Sid: EventPassRole
            Condition:
              StringLike:
                iam:PassedToService: ecs-tasks.amazonaws.com
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EventsECSExecutionTrust
          Effect: Allow
          Principal:
            Service: events.amazonaws.com
          Action: sts:AssumeRole
Outputs:
  ECSExecutionRoleARN:
    Description: ARN of the ECS Task Execution Role
    Value: !GetAtt ECSExecutionRole.Arn
  ProwlerTaskRoleARN:
    Description: ARN of the ECS Prowler Task Role
    Value: !GetAtt ProwlerTaskRole.Arn
  ECSEventRoleARN:
    Description: ARN of the Eventbridge Task Role
    Value: !GetAtt ECSEventRole.Arn
contrib/multi-account-securityhub/templates/CF-Prowler-ECS.yml METASEP
AWSTemplateFormatVersion: 2010-09-09
Description: This Template will create the infrastructure for Prowler with ECS Fargate
Parameters:
  ProwlerClusterName:
    Type: String
    Description: Name of the ECS Cluster that the Prowler Fargate Task will run in
    Default: ProwlerCluster
  ProwlerContainerName: 
    Type: String
    Description: Name of the Prowler Container Definition within the ECS Task
    Default: prowler
  ProwlerContainerInfo: 
    Type: String
    Description: ECR URI of the Prowler container
  ProwlerECSLogGroupName: 
    Type: String
    Description: Name for the log group to be created
    Default: /aws/ecs/SecurityHub-Prowler
  SecurityGroupVPCId:
    Type: String
    Description: VPC Id for the Security Group to be created
  ProwlerScheduledSubnet1:
    Type: String
    Description: Subnet Id in which Prowler can be scheduled to Run
  ProwlerScheduledSubnet2:
    Type: String
    Description: A secondary Subnet Id in which Prowler can be scheduled to Run
  ECSExecutionRole:
    Type: String
    Description: ECS Execution Task Role ARN.
  ProwlerTaskRole:
    Type: String
    Description: Prowler ECS Task Role ARN.
  ECSEventRole:
    Type: String
    Description: Eventbridge Task Role ARN.
  CronExpression:
    Type: String
    Description: Cron schedule for the event rule.
    Default: cron(0 23 * * ? *)
Resources:
  ProwlerECSCloudWatchLogsGroup:
    Type: AWS::Logs::LogGroup
    Properties: 
      LogGroupName: !Ref ProwlerECSLogGroupName
      RetentionInDays: 90
  ProwlerECSCluster:
    Type: AWS::ECS::Cluster
    Properties: 
      ClusterName: !Ref ProwlerClusterName
  ProwlerECSTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties: 
      ContainerDefinitions: 
        - Image: !Ref ProwlerContainerInfo
          Name: !Ref ProwlerContainerName
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref ProwlerECSCloudWatchLogsGroup
              awslogs-region: !Ref 'AWS::Region'
              awslogs-stream-prefix: ecs
      Cpu: 1024
      ExecutionRoleArn: !Ref ECSExecutionRole
      Memory: 2048
      NetworkMode: awsvpc
      TaskRoleArn: !Ref ProwlerTaskRole
      Family: SecurityHubProwlerTask
      RequiresCompatibilities:
        - FARGATE
  ProwlerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Allow HTTPS Out - Prowler
        VpcId: !Ref SecurityGroupVPCId
        SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
  ProwlerTaskScheduler:
    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: !Ref CronExpression
      State: ENABLED
      Targets:
        - Arn: !GetAtt ProwlerECSCluster.Arn
          RoleArn: !Ref ECSEventRole
          Id: prowlerTaskScheduler
          EcsParameters:
            TaskDefinitionArn: !Ref ProwlerECSTaskDefinition
            TaskCount: 1
            LaunchType: FARGATE
            PlatformVersion: 'LATEST'
            NetworkConfiguration:
              AwsVpcConfiguration:
                AssignPublicIp: DISABLED
                SecurityGroups:
                  - !Ref ProwlerSecurityGroup
                Subnets:
                  - !Ref ProwlerScheduledSubnet1
                  - !Ref ProwlerScheduledSubnet2
contrib/multi-account-securityhub/templates/CF-Prowler-CrossAccountRole.yml METASEP
AWSTemplateFormatVersion: 2010-09-09
Description: Create the Cross-Account IAM Prowler Role
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: ECS Settings
        Parameters:
          - ProwlerEcsAccount
          - ProwlerTaskRoleName
      - Label:
          default: CrossAccount Role
        Parameters:
          - ProwlerCrossAccountRole
Parameters:
  ProwlerEcsAccount:
    Type: String
    Description: Enter AWS Account Number where Prowler ECS Task will reside.
    AllowedPattern: ^\d{12}$
    ConstraintDescription: An AWS Account Number must be a 12 digit numeric string.
  ProwlerTaskRoleName:
    Type: String
    Description: Enter Instance Role that will be given to the Prowler ECS Instance (needed to grant sts:AssumeRole rights).
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerECSTask-Role
  ProwlerCrossAccountRole:
    Type: String
    Description: Enter Name for CrossAccount Role to be created for Prowler to assess all Accounts in the AWS Organization.
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerXA-Role
Resources:
  ProwlerRole:
    Type: AWS::IAM::Role
    Properties:
      Description: Provides Prowler ECS tasks permissions to assess security of Accounts in AWS Organization
      RoleName: !Ref ProwlerCrossAccountRole
      Tags:
        - Key: App
          Value: Prowler
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                - !Sub arn:${AWS::Partition}:iam::${ProwlerEcsAccount}:role/${ProwlerTaskRoleName}
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - !Sub arn:${AWS::Partition}:iam::aws:policy/SecurityAudit
        - !Sub arn:${AWS::Partition}:iam::aws:policy/job-function/ViewOnlyAccess
      Policies:
        - PolicyName: Prowler-Additions-Policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowMoreReadForProwler
                Effect: Allow
                Resource: "*"
                Action:
                  - ds:ListAuthorizedApplications
                  - ec2:GetEbsEncryptionByDefault
                  - ecr:Describe*
                  - elasticfilesystem:DescribeBackupPolicy
                  - glue:GetConnections
                  - glue:GetSecurityConfiguration
                  - glue:SearchTables
                  - lambda:GetFunction
                  - s3:GetAccountPublicAccessBlock
                  - shield:DescribeProtection
                  - shield:GetSubscriptionState
                  - ssm:GetDocument
                  - support:Describe*
                  - tag:GetTagKeys
        - PolicyName: Prowler-Security-Hub
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowProwlerSecurityHub
                Effect: Allow
                Resource: "*"
                Action:
                  - securityhub:BatchImportFindings
                  - securityhub:GetFindings
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W11
            reason: "Prowler requires these rights to perform its Security Assessment."
          - id: W28
            reason: "Using a defined Role Name."
Outputs:
  ProwlerCrossAccountRole:
    Description: CrossAccount Role to be used by Prowler to assess AWS Accounts in the AWS Organization.
    Value: !Ref ProwlerCrossAccountRole
contrib/cloudshell/screenshots/download_prompt.png METASEP

contrib/cloudshell/screenshots/cloudshell_icon.png METASEP

contrib/cloudshell/screenshots/action_upload_icon.png METASEP

contrib/cloudshell/screenshots/action_download_icon.png METASEP

providers/aws/services/__init__.py METASEP

providers/aws/lib/security_hub.py METASEP
import json
from itertools import groupby
from operator import itemgetter

from boto3 import session

from config.config import json_asff_file_suffix, timestamp_utc
from lib.logger import logger
from lib.outputs.models import Check_Output_JSON_ASFF
from providers.aws.models import AWS_Audit_Info


def send_to_security_hub(
    region: str, finding_output: Check_Output_JSON_ASFF, session: session.Session
):
    try:
        logger.info("Sending findings to Security Hub.")
        # Check if security hub is enabled in current region
        security_hub_client = session.client("securityhub", region_name=region)
        security_hub_client.describe_hub()

        # Check if Prowler integration is enabled in Security Hub
        if "prowler/prowler" not in str(
            security_hub_client.list_enabled_products_for_import()
        ):
            logger.error(
                f"Security Hub is enabled in {region} but Prowler integration does not accept findings. More info: https://github.com/prowler-cloud/prowler/#security-hub-integration"
            )

        # Send finding to Security Hub
        batch_import = security_hub_client.batch_import_findings(
            Findings=[finding_output.dict()]
        )
        if batch_import["FailedCount"] > 0:
            failed_import = batch_import["FailedFindings"][0]
            logger.error(
                f"Failed to send archived findings to AWS Security Hub -- {failed_import['ErrorCode']} -- {failed_import['ErrorMessage']}"
            )

    except Exception as error:
        logger.error(f"{error.__class__.__name__} -- {error} in region {region}")


# Move previous Security Hub check findings to ARCHIVED (as prowler didn't re-detect them)
def resolve_security_hub_previous_findings(
    output_directory: str, audit_info: AWS_Audit_Info
) -> list:
    logger.info("Checking previous findings in Security Hub to archive them.")
    # Read current findings from json-asff file
    with open(
        f"{output_directory}/prowler-output-{audit_info.audited_account}-{json_asff_file_suffix}"
    ) as f:
        json_asff_file = json.load(f)

    # Sort by region
    json_asff_file = sorted(json_asff_file, key=itemgetter("ProductArn"))
    # Group by region
    for product_arn, current_findings in groupby(
        json_asff_file, key=itemgetter("ProductArn")
    ):
        region = product_arn.split(":")[3]
        try:
            # Check if security hub is enabled in current region
            security_hub_client = audit_info.audit_session.client(
                "securityhub", region_name=region
            )
            security_hub_client.describe_hub()
            # Get current findings IDs
            current_findings_ids = []
            for finding in current_findings:
                current_findings_ids.append(finding["Id"])
            # Get findings of that region
            security_hub_client = audit_info.audit_session.client(
                "securityhub", region_name=region
            )
            findings_filter = {
                "ProductName": [{"Value": "Prowler", "Comparison": "EQUALS"}],
                "RecordState": [{"Value": "ACTIVE", "Comparison": "EQUALS"}],
                "AwsAccountId": [
                    {"Value": audit_info.audited_account, "Comparison": "EQUALS"}
                ],
                "Region": [{"Value": region, "Comparison": "EQUALS"}],
            }
            get_findings_paginator = security_hub_client.get_paginator("get_findings")
            findings_to_archive = []
            for page in get_findings_paginator.paginate(Filters=findings_filter):
                # Archive findings that have not appear in this execution
                for finding in page["Findings"]:
                    if finding["Id"] not in current_findings_ids:
                        finding["RecordState"] = "ARCHIVED"
                        finding["UpdatedAt"] = timestamp_utc.strftime(
                            "%Y-%m-%dT%H:%M:%SZ"
                        )

                        findings_to_archive.append(finding)
            logger.info(f"Archiving {len(findings_to_archive)} findings.")
            # Send archive findings to SHub
            list_chunked = [
                findings_to_archive[i : i + 100]
                for i in range(0, len(findings_to_archive), 100)
            ]
            for findings in list_chunked:
                batch_import = security_hub_client.batch_import_findings(
                    Findings=findings
                )
                if batch_import["FailedCount"] > 0:
                    failed_import = batch_import["FailedFindings"][0]
                    logger.error(
                        f"Failed to send archived findings to AWS Security Hub -- {failed_import['ErrorCode']} -- {failed_import['ErrorMessage']}"
                    )
        except Exception as error:
            logger.error(f"{error.__class__.__name__} -- {error} in region {region}")

providers/aws/lib/__init__.py METASEP

providers/aws/aws_regions_by_service.json METASEP
{
  "services": {
    "acm": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-west-1",
          "me-south-1",
          "sa-east-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-west-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "us-east-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "ahl": {
      "regions": {
        "aws": [
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "aiq": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "ap-east-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "me-south-1",
          "us-west-1",
          "us-west-2",
          "ap-northeast-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "alexaforbusiness": {
      "regions": {
        "aws": [
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "amazonlocationservice": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "amplify": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "me-south-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-1",
          "ap-south-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-west-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "apigateway": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-3",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "sa-east-1",
          "us-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-2",
          "us-west-2",
          "ap-south-1",
          "ca-central-1",
          "eu-west-3",
          "me-south-1",
          "us-west-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "appflow": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ca-central-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "application-autoscaling": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "af-south-1",
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-south-1",
          "us-east-2",
          "us-west-2",
          "eu-central-1",
          "eu-north-1",
          "sa-east-1",
          "us-east-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "appmesh": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-3",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-north-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ca-central-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": {}
      }
    },
    "appstream": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-west-2",
          "ap-southeast-2",
          "us-east-1",
          "us-east-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "appsync": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-southeast-1",
          "eu-central-1",
          "eu-north-1",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-west-2",
          "eu-south-1",
          "eu-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": {}
      }
    },
    "aps": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "artifact": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ca-central-1",
          "eu-south-1",
          "eu-west-1",
          "sa-east-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-3",
          "eu-north-1",
          "us-east-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "athena": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-2",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-south-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "auditmanager": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-southeast-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "augmentedairuntime": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-2",
          "ap-south-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "aurora": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-1",
          "us-east-2",
          "us-west-1",
          "ap-south-1",
          "ap-southeast-3",
          "eu-north-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "autoscaling": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-3",
          "us-west-2",
          "ap-northeast-1",
          "ap-south-1",
          "eu-west-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "backup": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "us-west-1",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-north-1",
          "eu-west-1",
          "me-south-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-3",
          "ca-central-1",
          "eu-south-1",
          "sa-east-1",
          "us-east-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "batch": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "sa-east-1",
          "us-west-2",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "me-south-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "braket": {
      "regions": {
        "aws": [
          "us-east-1",
          "us-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "budgets": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-2",
          "us-west-2",
          "ap-northeast-2",
          "ca-central-1",
          "eu-west-1",
          "sa-east-1",
          "us-east-1",
          "us-west-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": {}
      }
    },
    "chatbot": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-east-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "eu-north-1",
          "eu-west-1",
          "sa-east-1",
          "us-west-1",
          "us-west-2",
          "af-south-1",
          "us-east-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "chime": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-west-2",
          "af-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2",
          "us-east-2",
          "us-west-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "cloud9": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "me-south-1",
          "sa-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "eu-west-3"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "clouddirectory": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "cloudenduredisasterrecovery": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-1",
          "eu-west-3",
          "us-east-1",
          "me-south-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "cloudenduremigration": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-southeast-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "af-south-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "eu-south-1",
          "eu-west-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "cloudformation": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-west-2",
          "eu-west-3",
          "us-east-2",
          "af-south-1",
          "ap-northeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "me-south-1",
          "us-east-1",
          "us-west-2",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-south-1",
          "sa-east-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "cloudfront": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-north-1",
          "eu-south-1",
          "eu-west-2",
          "me-south-1",
          "af-south-1",
          "ap-northeast-2",
          "ap-southeast-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": {}
      }
    },
    "cloudhsmv2": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-west-2",
          "eu-west-3",
          "us-west-1",
          "us-west-2",
          "ap-northeast-2",
          "eu-north-1",
          "eu-west-1",
          "sa-east-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "cloudsearch": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "sa-east-1",
          "us-east-1",
          "us-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "cloudshell": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "cloudtrail": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "eu-south-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-2",
          "ap-northeast-3",
          "ap-southeast-2",
          "ap-southeast-3",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "me-south-1",
          "us-east-2",
          "ap-south-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-west-2",
          "us-east-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "cloudwatch": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-northeast-3",
          "ca-central-1",
          "eu-north-1",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-central-1",
          "eu-south-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "codeartifact": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-southeast-1",
          "us-east-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "codebuild": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "us-west-1",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-3",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2",
          "ap-southeast-3",
          "ca-central-1",
          "sa-east-1",
          "us-east-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "codecommit": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-3",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "sa-east-1",
          "us-west-1",
          "ap-northeast-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "codedeploy": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-south-1",
          "me-south-1",
          "us-west-1",
          "us-west-2",
          "ap-northeast-2",
          "ap-northeast-3",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-2",
          "ap-northeast-1",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-3",
          "us-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "codeguruprofiler": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "codepipeline": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "us-west-2",
          "ap-northeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-southeast-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "codestar": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-southeast-2",
          "eu-central-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "cognito-identity": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-west-1",
          "eu-west-3",
          "us-east-1",
          "us-west-1",
          "ap-northeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "comprehend": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-2",
          "us-west-2",
          "ap-southeast-2",
          "us-east-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "comprehendmedical": {
      "regions": {
        "aws": [
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "compute-optimizer": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-north-1",
          "eu-west-3",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "sa-east-1",
          "us-west-1",
          "us-west-2",
          "ap-east-1",
          "eu-south-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": {}
      }
    },
    "config": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-central-1",
          "eu-south-1",
          "me-south-1",
          "us-east-1",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-north-1",
          "eu-west-2",
          "sa-east-1",
          "us-west-2",
          "af-south-1",
          "ap-southeast-2",
          "eu-west-1",
          "eu-west-3",
          "us-east-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "connect": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "us-east-1",
          "us-west-2",
          "eu-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "controltower": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "sa-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "costexplorer": {
      "regions": {
        "aws": [
          "us-east-1"
        ],
        "cn": [
          "cn-northwest-1"
        ],
        "gov": {}
      }
    },
    "cur": {
      "regions": {
        "aws": [
          "us-east-1"
        ],
        "cn": [
          "cn-northwest-1"
        ],
        "gov": {}
      }
    },
    "dataexchange": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "datapipeline": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "datasync": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "ap-east-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-north-1",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "eu-south-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "deepcomposer": {
      "regions": {
        "aws": [
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "deeplens": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "eu-central-1",
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "deepracer": {
      "regions": {
        "aws": [
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "detective": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "me-south-1",
          "sa-east-1",
          "us-west-1",
          "af-south-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ca-central-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "devicefarm": {
      "regions": {
        "aws": [
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "devops-guru": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "directconnect": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-north-1",
          "sa-east-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-northeast-3",
          "eu-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "us-west-1",
          "ap-northeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-3",
          "me-south-1",
          "us-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "discovery": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "dms": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-west-1",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "us-west-2",
          "af-south-1",
          "ap-south-1",
          "ca-central-1",
          "eu-north-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "docdb": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "drs": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "us-west-1",
          "us-west-2",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "eu-north-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "ds": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-2",
          "sa-east-1",
          "us-east-2",
          "us-west-2",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-west-2",
          "us-east-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "dynamodb": {
      "regions": {
        "aws": [
          "ap-southeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "af-south-1",
          "eu-south-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "ebs": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "me-south-1",
          "af-south-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "us-east-1",
          "us-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "ec2": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-south-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ca-central-1",
          "eu-central-1",
          "me-south-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "ecr": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "af-south-1",
          "ap-south-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "ca-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "eu-central-1",
          "eu-north-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "ecs": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-3",
          "us-east-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-west-1",
          "af-south-1",
          "eu-central-1",
          "me-south-1",
          "sa-east-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "efs": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-south-1",
          "eu-west-3",
          "sa-east-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "eks": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-north-1",
          "me-south-1",
          "us-east-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "sa-east-1",
          "us-west-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-south-1",
          "ca-central-1",
          "eu-west-3",
          "us-east-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "elastic-inference": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "elasticache": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "ap-east-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "eu-central-1",
          "eu-south-1",
          "me-south-1",
          "us-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "elasticbeanstalk": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-north-1",
          "eu-west-1",
          "sa-east-1",
          "us-west-1",
          "af-south-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "ap-northeast-1",
          "ca-central-1",
          "eu-west-3",
          "us-east-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "elastictranscoder": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-west-1",
          "us-east-1",
          "us-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "elb": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-2",
          "me-south-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "emr": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-north-1",
          "eu-west-2",
          "me-south-1",
          "sa-east-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "eu-west-3",
          "ap-east-1",
          "eu-south-1",
          "us-east-1",
          "us-east-2",
          "us-west-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "es": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-south-1",
          "eu-west-3",
          "us-west-1",
          "us-west-2",
          "af-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-3",
          "sa-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "eventbridge": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "eu-central-1",
          "eu-north-1",
          "eu-west-3",
          "us-east-1",
          "us-west-2",
          "ap-east-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-south-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "fargate": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-west-2",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "us-west-2",
          "ap-east-1",
          "ap-southeast-2",
          "eu-west-3",
          "us-east-2",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "firehose": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "af-south-1",
          "ap-east-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "ca-central-1",
          "eu-south-1",
          "me-south-1",
          "sa-east-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "fis": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "me-south-1",
          "us-west-1",
          "us-east-1",
          "us-east-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "fms": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-southeast-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-3",
          "sa-east-1",
          "eu-north-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "forecast": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "frauddetector": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "freertosota": {
      "regions": {
        "aws": [
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": {}
      }
    },
    "fsx-lustre": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "me-south-1",
          "us-east-2",
          "af-south-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-west-1",
          "ap-east-1",
          "eu-south-1",
          "sa-east-1",
          "us-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "fsx-ontap": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-1",
          "me-south-1",
          "us-east-2",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-1",
          "ap-south-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "eu-north-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "fsx-openzfs": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "fsx-windows": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-1",
          "us-west-1",
          "us-west-2",
          "ap-east-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "us-east-2",
          "af-south-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "me-south-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "gamelift": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-2",
          "ca-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-3",
          "eu-central-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": {}
      }
    },
    "globalaccelerator": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-south-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-2",
          "af-south-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "eu-west-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "glue": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "us-east-1",
          "us-west-1",
          "us-west-2",
          "ap-northeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "af-south-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "grafana": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "greengrass": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-2",
          "us-west-2",
          "ap-southeast-2",
          "eu-central-1",
          "us-east-1"
        ],
        "cn": [
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "groundstation": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "eu-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "guardduty": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "us-east-2",
          "ap-northeast-2",
          "eu-north-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "honeycode": {
      "regions": {
        "aws": [
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "iam": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-north-1",
          "us-west-1",
          "us-west-2",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-south-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-2",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "ap-northeast-2",
          "ap-northeast-3",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "us-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "inspector2": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-south-1",
          "eu-south-1",
          "eu-west-1",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "iot": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-west-1",
          "sa-east-1",
          "us-east-1",
          "ap-northeast-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "iot1click-projects": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "iotanalytics": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1"
        ],
        "gov": {}
      }
    },
    "iotdevicedefender": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-3",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-west-2",
          "ap-northeast-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "iotdevicemanagement": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-west-3",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2",
          "eu-west-1",
          "sa-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "iotevents": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "eu-west-1"
        ],
        "cn": [
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "iotsitewise": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-2",
          "us-east-2"
        ],
        "cn": [
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "iotthingsgraph": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "iottwinmaker": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "ivs": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "kafka": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "eu-west-3",
          "us-east-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "eu-north-1",
          "eu-south-1",
          "eu-west-2",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "sa-east-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "kendra": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "kinesis": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-west-2",
          "af-south-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "ap-east-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-central-1",
          "eu-south-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "kinesisanalytics": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-3",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-2",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "us-east-1",
          "us-west-1",
          "af-south-1",
          "ap-northeast-3",
          "ap-south-1",
          "eu-central-1",
          "eu-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "kinesisvideo": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ca-central-1",
          "eu-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "kms": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "me-south-1",
          "sa-east-1",
          "ca-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "us-west-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "lakeformation": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "us-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-west-2",
          "ap-south-1",
          "ap-southeast-2",
          "sa-east-1",
          "us-east-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "lambda": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "ap-east-1",
          "ap-northeast-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-2",
          "af-south-1",
          "ap-northeast-3",
          "ca-central-1",
          "eu-central-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "lex-runtime": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2",
          "ap-southeast-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "license-manager": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "me-south-1",
          "ap-east-1",
          "ap-south-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-west-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "lightsail": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "ca-central-1",
          "eu-west-3",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "lookoutmetrics": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "lookoutvision": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "lumberyard": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "sa-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "macie": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-3",
          "ca-central-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-west-1",
          "us-west-2",
          "eu-south-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "managedblockchain": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "managedservices": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "sa-east-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "marketplace": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ca-central-1",
          "me-south-1",
          "us-west-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-1",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "ap-southeast-2",
          "eu-north-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "mcs": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "eu-west-1",
          "eu-west-3",
          "us-east-2",
          "us-west-1",
          "ap-southeast-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "mediaconnect": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "mediaconvert": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-2",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "sa-east-1",
          "us-west-1"
        ],
        "cn": [
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "medialive": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "eu-north-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "mediapackage": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "mediastore": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "mediatailor": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "mgh": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "mgn": {
      "regions": {
        "aws": [
          "ap-northeast-3",
          "ap-southeast-2",
          "ca-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "mq": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2",
          "us-east-2",
          "us-west-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "mwaa": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-southeast-1",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "us-east-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "neptune": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-west-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-south-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ca-central-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "network-firewall": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-northeast-2",
          "ap-south-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "us-east-1",
          "sa-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "nimble": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "opsworks": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "ca-central-1",
          "eu-west-1",
          "sa-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "opsworkschefautomate": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "opsworkspuppetenterprise": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "organizations": {
      "regions": {
        "aws": [
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "me-south-1",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "eu-north-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "af-south-1",
          "ap-east-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "outposts": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ca-central-1",
          "eu-south-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "eu-north-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "personalize": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-2"
        ],
        "cn": [
          "cn-north-1"
        ],
        "gov": {}
      }
    },
    "phd": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "ca-central-1",
          "me-south-1",
          "sa-east-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-1",
          "ap-south-1",
          "eu-north-1",
          "eu-west-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "pinpoint": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-west-2",
          "ap-southeast-1",
          "us-east-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "polly": {
      "regions": {
        "aws": [
          "af-south-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-1"
        ],
        "cn": [
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "privatelink": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-west-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-northeast-1",
          "eu-north-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "proton": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "qldb": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-2",
          "us-west-2",
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "quicksight": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ca-central-1",
          "eu-west-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-2",
          "us-west-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "us-east-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "ram": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "us-east-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "eu-north-1",
          "sa-east-1",
          "us-east-1",
          "us-west-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-2",
          "me-south-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "rds": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-south-1",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "us-west-1",
          "us-west-2",
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "rdsvmware": {
      "regions": {
        "aws": [
          "us-east-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "redshift": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-2",
          "eu-west-2",
          "me-south-1",
          "us-east-2",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-north-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "rekognition": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-south-1",
          "eu-west-1",
          "eu-west-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "resiliencehub": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "robomaker": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "rosa": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "me-south-1",
          "us-east-2",
          "af-south-1",
          "ap-northeast-2",
          "ap-south-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-1",
          "us-west-1",
          "us-west-2",
          "eu-west-3"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "route53": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-3",
          "ap-south-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "sa-east-1",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "ca-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-3",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": {}
      }
    },
    "s3": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-west-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "af-south-1",
          "ca-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "us-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "sagemaker": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "af-south-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "ap-east-1",
          "ap-southeast-1",
          "sa-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "secretsmanager": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-west-3",
          "me-south-1",
          "us-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-2",
          "us-west-2",
          "ca-central-1",
          "eu-south-1",
          "sa-east-1",
          "us-west-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "securityhub": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-2",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-northeast-3",
          "ap-southeast-3",
          "eu-south-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "us-west-1",
          "ap-northeast-1",
          "ap-south-1",
          "eu-central-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "serverlessrepo": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-west-1",
          "us-west-1",
          "us-west-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "ca-central-1",
          "us-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "servicecatalog": {
      "regions": {
        "aws": [
          "ca-central-1",
          "eu-north-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "me-south-1",
          "sa-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-south-1",
          "eu-west-1",
          "us-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "servicediscovery": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "us-west-1",
          "af-south-1",
          "ap-northeast-3",
          "ap-southeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "me-south-1",
          "us-east-2",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-west-1",
          "sa-east-1",
          "us-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "ses": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-west-2",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ca-central-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "shield": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "us-west-2",
          "af-south-1",
          "ap-east-1",
          "ap-northeast-3",
          "ap-southeast-3",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "us-east-2",
          "us-west-1",
          "ap-southeast-2",
          "ca-central-1"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "sms": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "sa-east-1",
          "us-east-1",
          "us-west-2",
          "af-south-1",
          "ap-northeast-2",
          "ap-south-1",
          "ca-central-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "ap-east-1",
          "ap-southeast-2",
          "eu-central-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "snowball": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "eu-west-3",
          "us-east-1",
          "us-west-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-south-1",
          "eu-west-1",
          "eu-west-2",
          "sa-east-1",
          "us-west-1",
          "ap-east-1",
          "ca-central-1",
          "eu-central-1",
          "us-east-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "snowcone": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "ap-northeast-1",
          "us-east-2",
          "us-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "snowmobile": {
      "regions": {
        "aws": [
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "sns": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-west-3",
          "me-south-1",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-1",
          "us-west-1",
          "ca-central-1",
          "eu-west-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "sqs": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-3",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-south-1",
          "sa-east-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "ssm": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "eu-west-2",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-3",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "us-east-1",
          "us-west-2",
          "af-south-1",
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-3",
          "eu-west-1",
          "eu-west-3"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "sso": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-3",
          "ap-southeast-2",
          "eu-central-1",
          "eu-south-1",
          "eu-west-1",
          "me-south-1",
          "sa-east-1",
          "us-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-north-1",
          "eu-west-3",
          "us-east-2",
          "us-west-2",
          "eu-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "stepfunctions": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-2",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-1",
          "us-east-1",
          "ap-east-1",
          "ap-northeast-3",
          "ap-southeast-3",
          "us-west-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "storagegateway": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-west-1",
          "eu-west-3",
          "us-west-2",
          "af-south-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-2",
          "us-east-2",
          "us-west-1",
          "ap-south-1",
          "eu-north-1",
          "eu-south-1",
          "me-south-1",
          "sa-east-1",
          "us-east-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "sumerian": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-west-1",
          "us-west-2",
          "ap-northeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "support": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ca-central-1",
          "eu-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-west-1",
          "me-south-1",
          "us-east-1",
          "us-west-2",
          "eu-central-1",
          "eu-north-1",
          "eu-west-2",
          "eu-west-3"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "swf": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "sa-east-1",
          "af-south-1",
          "ap-east-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-2",
          "me-south-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "eu-west-1",
          "eu-west-3",
          "us-east-1",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "textract": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-1",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "ap-northeast-2",
          "ap-south-1",
          "eu-central-1",
          "eu-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "timestream": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "eu-west-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "transcribe": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-3",
          "me-south-1",
          "us-east-1",
          "af-south-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-2",
          "us-west-1",
          "us-west-2",
          "ap-southeast-1"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "transcribemedical": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "transfer": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-central-1",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ca-central-1",
          "eu-north-1",
          "eu-west-2",
          "eu-west-3",
          "me-south-1",
          "us-west-2",
          "ap-northeast-1",
          "eu-south-1",
          "eu-west-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "transitgateway": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "sa-east-1",
          "us-east-1",
          "af-south-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "us-east-2",
          "us-west-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-south-1",
          "me-south-1",
          "us-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "translate": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "ap-southeast-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "us-west-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "trustedadvisor": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-3",
          "me-south-1",
          "us-east-1",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-south-1",
          "eu-west-2",
          "us-west-1",
          "af-south-1",
          "ap-northeast-3",
          "sa-east-1",
          "us-east-2",
          "us-west-2"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "vmwarecloudonaws": {
      "regions": {
        "aws": [
          "ap-northeast-2",
          "ap-southeast-1",
          "ca-central-1",
          "eu-central-1",
          "eu-south-1",
          "eu-west-2",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-2",
          "eu-north-1",
          "eu-west-1",
          "eu-west-3",
          "sa-east-1",
          "us-west-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "vpc": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-north-1",
          "eu-south-1",
          "us-east-1",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-3",
          "ca-central-1",
          "eu-central-1",
          "eu-west-3",
          "us-east-2",
          "us-west-2",
          "ap-northeast-2",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "sa-east-1"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "vpn": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "eu-central-1",
          "eu-north-1",
          "eu-south-1",
          "eu-west-3",
          "me-south-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-south-1",
          "ap-southeast-1",
          "eu-west-2",
          "sa-east-1",
          "us-east-2",
          "ca-central-1",
          "eu-west-1",
          "us-east-1",
          "us-west-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "waf": {
      "regions": {
        "aws": [
          "af-south-1",
          "ap-east-1",
          "ap-southeast-3",
          "eu-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "me-south-1",
          "us-west-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-northeast-3",
          "ap-southeast-1",
          "sa-east-1",
          "us-east-1",
          "us-east-2",
          "us-west-2",
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-south-1",
          "eu-west-3"
        ],
        "cn": [
          "cn-north-1",
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1",
          "us-gov-east-1"
        ]
      }
    },
    "wam": {
      "regions": {
        "aws": [
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "wellarchitectedtool": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-north-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2",
          "ap-east-1",
          "ap-northeast-2",
          "ap-south-1",
          "eu-central-1",
          "eu-west-3",
          "me-south-1",
          "sa-east-1",
          "us-east-2",
          "us-west-1"
        ],
        "cn": {},
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    },
    "workdocs": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "workmail": {
      "regions": {
        "aws": [
          "eu-west-1",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "workspaces": {
      "regions": {
        "aws": [
          "ap-south-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "sa-east-1",
          "us-east-1",
          "us-west-2",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-southeast-1",
          "eu-west-2"
        ],
        "cn": [
          "cn-northwest-1"
        ],
        "gov": [
          "us-gov-west-1"
        ]
      }
    },
    "workspaces-web": {
      "regions": {
        "aws": [
          "ap-northeast-1",
          "ap-south-1",
          "ap-southeast-1",
          "ap-southeast-2",
          "ca-central-1",
          "eu-central-1",
          "eu-west-1",
          "eu-west-2",
          "us-east-1",
          "us-west-2"
        ],
        "cn": {},
        "gov": {}
      }
    },
    "xray": {
      "regions": {
        "aws": [
          "ap-east-1",
          "ap-southeast-1",
          "eu-central-1",
          "eu-west-2",
          "eu-west-3",
          "us-east-1",
          "us-east-2",
          "us-west-1",
          "af-south-1",
          "ap-northeast-1",
          "ap-northeast-2",
          "ap-south-1",
          "ap-southeast-2",
          "ap-southeast-3",
          "ca-central-1",
          "eu-south-1",
          "eu-west-1",
          "ap-northeast-3",
          "eu-north-1",
          "me-south-1",
          "sa-east-1",
          "us-west-2"
        ],
        "cn": [
          "cn-northwest-1",
          "cn-north-1"
        ],
        "gov": [
          "us-gov-east-1",
          "us-gov-west-1"
        ]
      }
    }
  }
}
docs/images/prowler-single-account-environment.png METASEP

docs/images/prowler-pro-light.png METASEP

docs/images/prowler-pro-dark.png METASEP

docs/images/prowler-multi-account-environment.png METASEP

contrib/wazuh/prowler_rules.xml METASEP
<!--
  Rules for parsing Prowler output
  Authored by Jeremy Phillips <jeremy@uranusbytes.com>
  Copyright: Apache License 2.0
  ID: 110000-110009
  Prowler - https://github.com/toniblyx/prowler
-->

<group name="local,amazon,prowler,">
    <!-- Filter 1: Only prowler events -->
    <rule id="110001" level="0">
        <field name="integration">prowler</field>
        <description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
    </rule>
    <!-- Check Result: Pass -->
    <rule id="110002" level="1">
        <if_sid>110001</if_sid>
        <field name="prowler.status">Pass</field>
        <description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
    </rule>
    <!-- Check Result: Info -->
    <rule id="110003" level="3">
        <if_sid>110001</if_sid>
        <field name="prowler.status">Info</field>
        <description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
    </rule>
    <!-- Check Result: Error -->
    <rule id="110004" level="5">
        <if_sid>110001</if_sid>
        <field name="prowler.status">Error</field>
        <description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
    </rule>
    <!-- Check Result: Fail, Scored -->
    <rule id="110005" level="9">
        <if_sid>110001</if_sid>
        <field name="prowler.status">Fail</field>
        <description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
    </rule>
    <!-- Check Result: Fail, Not Scored -->
    <rule id="110006" level="7">
        <if_sid>110005</if_sid>
        <field name="prowler.scored">Not Scored</field>
        <description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
    </rule>
</group>

contrib/wazuh/README.md METASEP
# Prowler integration with Wazuh (DRAFT)

## Table of Contents

- [Description](#description)
- [Features](#features)
- [Requirements](#requirements)
- [Integration steps](#integration-steps)
- [Troubleshooting](#troubleshooting)
- [Thanks](#thanks)
- [License](#license)

## Description

Prowler integration with WAZUH using a python wrapper. Due to the wrapper limitations, this integration can be considered as a proof of concept at this time.

## Features

Wazuh, using a wodle, runs Prowler every certain time and stores alerts (failed checks) using JSON output which Wazuh processes and sends to Elastic Search to be queried from Kibana.

## Requirements

1. Latest AWS-CLI client (`pip install awscli`). If you have it already installed, make sure you are using the latest version, upgrade it: `pip install awscli --upgrade`.
2. Also `jq` is needed (`pip install jq`).

Remember, you must have AWS-CLI credentials already configured in the same instance running Wazuh (run `aws configure` if needed). In this DRAFT I'm using `/root/.aws/credentials` file with [default] as AWS-CLI profile and access keys but you can use assume role configuration as well. For the moment instance profile is not supported in this wrapper.

It may work in previous versions of Wazuh, but this document and integration was tested on Wazuh 3.7.1. So to have a Wazuh running installation is obviously required.

## Integration steps

Add Prowler to Wazuh's integrations:
```
cd /var/ossec/integrations/
git clone https://github.com/toniblyx/prowler
```
Copy `prowler-wrapper.py` to integrations folder:

```
cp /var/ossec/integrations/prowler/integrations/prowler-wrapper.py /var/ossec/integrations/prowler-wrapper.py
```
Then make sure it is executable:
```
chmod +x /var/ossec/integrations/prowler-wrapper.py
```
Run Prowler wrapper manually to make sure it works fine, use `--debug 1` or `--debug 2`):
```
/var/ossec/integrations/prowler-wrapper.py --aws_profile default --aws_account_alias default --debug 2
```

Copy rules file to its location:

```
cp /var/ossec/integrations/prowler/integrations/prowler_rules.xml /var/ossec/etc/rules/prowler_rules.xml
```

Edit `/var/ossec/etc/ossec.conf` and add the following wodle configuration. Remember that here `timeout 21600 seconds` is 6 hours, just to allow Prowler runs completely in case of a large account. The interval recommended is 1d:
```xml 
  <wodle name="command">
    <disabled>no</disabled>
    <tag>aws-prowler: account1</tag>
    <command>/var/ossec/integrations/prowler-wrapper.py --aws_profile default --aws_account_alias default</command>
    <interval>1d</interval>
    <ignore_output>no</ignore_output>
    <run_on_start>no</run_on_start>
    <timeout>21600</timeout>
  </wodle>
```
To check multiple AWS accounts, add a wodle per account.

Now restart `wazuh-manager` and look at `/var/ossec/logs/alerts/alerts.json`, eventually you should see FAIL checks detected by Prowler, then you will find them using Kibana. Some Kibana search examples are:
```
data.integration:"prowler" and data.prowler.status:"Fail"
data.integration:"prowler" AND rule.level >= 5
data.integration:"prowler" AND rule.level : 7 or 9
```

Adjust the level range to what alerts you want to include, as alerts, Elastic Search only gets fail messages (7 and 9).

1 - pass
3 - info
5 - error
7 - fail: not scored
9 - fail: scored

## Troubleshooting

To make sure rules are working fine, run `/var/ossec/bin/ossec-logtest` and copy/paste this sample JSON:

```json
{"prowler":{"Timestamp":"2018-11-29T03:15:50Z","Region":"us-east-1","Profile":"default","Account Number”:”1234567890”,”Control":"[check34] Ensure a log metric filter and alarm exist for IAM policy changes (Scored)","Message":"No CloudWatch group found for CloudTrail events","Status":"Fail","Scored":"Scored","Level":"Level 1","Control ID":"3.4"}, "integration": "prowler"}
```
You must see 3 phases goin on.

To check if there is any error you can enable the debug mode of `modulesd` setting the `wazuh_modules.debug=0` variable to 2 in `/var/ossec/etc/internal_options.conf` file. Restart wazun-manager and errors should appear in the `/var/ossec/logs/ossec.log` file.

## Thanks

To Jeremy Phillips <jeremy@uranusbytes.com>, who wrote the initial rules file and wrapper and helped me to understand how it works and debug it.

To [Marta Gomez](https://github.com/mgmacias95) and the [Wazuh](https://www.wazuh.com) team for their support to debug this integration and make it work properly. Their job on Wazuh and willingness to help is invaluable.

## License

All CIS based checks in the checks folder are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License.
The link to the license terms can be found at
<https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode>
Any other piece of code is licensed as Apache License 2.0 as specified in each file. You may obtain a copy of the License at
<http://www.apache.org/licenses/LICENSE-2.0>

NOTE: If you are interested in using Prowler for commercial purposes remember that due to the CC4.0 license “The distributors or partners that are interested and using Prowler would need to enroll as CIS SecureSuite Members to incorporate this product, which includes references to CIS resources, in their offering.". Information about CIS pricing for vendors here: <https://www.cisecurity.org/cis-securesuite/pricing-and-categories/product-vendor/>

**I'm not related anyhow with CIS organization, I just write and maintain Prowler to help companies over the world to make their cloud infrastructure more secure.**

If you want to contact me visit <https://blyx.com/contact>

contrib/terraform-kickstarter/variables.tf METASEP
/*
© 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non_exclusive, no_charge, royalty_free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non_exclusive, no_charge, royalty_free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross_claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third_party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON_INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third_party archives.

   Copyright [2020] [© 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE_2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

/*
Security Hub Import Commands

Run this to get state of SecurityHub

terraform import aws_securityhub_account.securityhubresource 123456789012

*/

variable "select_region" {
  description = "Uses the following AWS Region."
  type        = string
  default     = "us-east-1"
}

variable "enable_security_hub" {
  description = "Enable AWS SecurityHub."
  type        = bool
  default     = true
}

variable "enable_security_hub_prowler_subscription" {
  description = "Enable a Prowler Subscription."
  type        = bool
  default     = true
}

variable "prowler_cli_options" {
  description = "Run Prowler With The Following Command"
  type        = string
  default     =  "-q -M json-asff -S -f us-east-1"
}

variable "prowler_schedule"{
  description = "Run Prowler based on cron schedule"
  default="cron(0 0 ? * * *)"
  type=string

}

variable "codebuild_timeout" {
  description = "Codebuild timeout setting"
  default = 300
  type=number
}

contrib/terraform-kickstarter/tf_install.sh METASEP
#!/bin/bash
#AMZN-Linux Terraform Install Script
git clone https://github.com/singergs/prowler.git
git fetch
cd prowler
git checkout -t origin/terraform-kickstart
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
sudo yum -y install terraform
contrib/terraform-kickstarter/readme.md METASEP
# Install Security Baseline Kickstarter with Prowler

## Introduction

The following demonstartes how to quickly install the resources necessary to perform a security baseline using Prowler.  The speed is based on the prebuilt terraform module that can configure all the resources necessuary to run Prowler with the findings being sent to AWS Security Hub.

## Install

Installing Prowler with Terraform is simple and can be completed in under 1 minute.

- Start AWS CloudShell
- Run the following commands to install Terraform and clone the Prowler git repo
  ```
  git clone https://github.com/prowler-cloud/prowler.git
  cd prowler
  sudo yum install -y yum-utils
  sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
  sudo yum -y install terraform
  cd util/terraform-kickstarter
  ```
- Issue a `terraform init`

- Issue a `terraform apply`

  ![Prowler Install](https://prowler-docs.s3.amazonaws.com/Prowler-Terraform-Install.gif)

   - It is likely an error will return related to the SecurityHub subscription.  This appears to be Terraform related and you can validate the configuration by navigating to the SecurityHub console.  Click Integreations and search for Prowler. Take note of the green check where it says *Accepting findings*

  ![Prowler Subscription](https://prowler-docs.s3.amazonaws.com/Validate-Prowler-Subscription.gif)


Thats it!  Install is now complete.  The resources include a Cloudwatch event that will trigger the AWS Codebuild to run daily at 00:00 GMT.  If you'd like to run an assessment after the deployment then simply navigate to the Codebuild console and start the job manually.

## Terraform Resources

## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 3.54 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 3.56.0 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [aws_cloudwatch_event_rule.prowler_check_scheduler_event](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |
| [aws_cloudwatch_event_target.run_prowler_scan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |
| [aws_codebuild_project.prowler_codebuild](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project) | resource |
| [aws_iam_policy.prowler_event_trigger_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.prowler_kickstarter_iam_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy_attachment.prowler_event_trigger_policy_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
| [aws_iam_policy_attachment.prowler_kickstarter_iam_policy_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
| [aws_iam_role.prowler_event_trigger_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.prowler_kick_start_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_s3_bucket.prowler_report_storage_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_policy.prowler_report_storage_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.prowler_report_storage_bucket_block_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_securityhub_account.securityhub_resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_account) | resource |
| [aws_securityhub_product_subscription.security_hub_enable_prowler_findings](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_product_subscription) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy.SecurityAudit](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_codebuild_timeout"></a> [codebuild\_timeout](#input\_codebuild\_timeout) | Codebuild timeout setting | `number` | `300` | no |
| <a name="input_enable_security_hub"></a> [enable\_security\_hub](#input\_enable\_security\_hub) | Enable AWS SecurityHub. | `bool` | `true` | no |
| <a name="input_enable_security_hub_prowler_subscription"></a> [enable\_security\_hub\_prowler\_subscription](#input\_enable\_security\_hub\_prowler\_subscription) | Enable a Prowler Subscription. | `bool` | `true` | no |
| <a name="input_prowler_cli_options"></a> [prowler\_cli\_options](#input\_prowler\_cli\_options) | Run Prowler With The Following Command | `string` | `"-q -M json-asff -S -f us-east-1"` | no |
| <a name="input_prowler_schedule"></a> [prowler\_schedule](#input\_prowler\_schedule) | Run Prowler based on cron schedule | `string` | `"cron(0 0 ? * * *)"` | no |
| <a name="input_select_region"></a> [select\_region](#input\_select\_region) | Uses the following AWS Region. | `string` | `"us-east-1"` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_account_id"></a> [account\_id](#output\_account\_id) | n/a |

## Kickoff Prowler Assessment From Install to Assessment Demo (Link to YouTube)

  [![Prowler Install](https://img.youtube.com/vi/ShhzIArO8X0/0.jpg)](https://www.youtube.com/watch?v=ShhzIArO8X0 "Prowler Install")

contrib/terraform-kickstarter/prowler_build_spec.yml METASEP
version: 0.2
phases:
  install:
    runtime-versions:
      python: 3.8
    commands:
      - echo "Installing Prowler and dependencies..."
      - pip3 install detect-secrets 
      - yum -y install jq
      - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
      - unzip awscliv2.zip
      - ./aws/install
      - git clone https://github.com/prowler-cloud/prowler
      - cd prowler
      
  build:
    commands:
      - echo "Running Prowler as ./prowler $PROWLER_OPTIONS"
      - ./prowler $PROWLER_OPTIONS || true
  post_build:
    commands:
      - echo "Scan Complete" 
      - aws s3 cp --sse AES256 output/ s3://$BUCKET_REPORT/ --recursive
      - echo "Done!"

contrib/terraform-kickstarter/outputs.tf METASEP
/*
© 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non_exclusive, no_charge, royalty_free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non_exclusive, no_charge, royalty_free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross_claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third_party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON_INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third_party archives.

   Copyright [2020] [© 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE_2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

output "account_id" {
  value = data.aws_caller_identity.current.account_id
}
contrib/terraform-kickstarter/main.tf METASEP
/*
© 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non_exclusive, no_charge, royalty_free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non_exclusive, no_charge, royalty_free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross_claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third_party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON_INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third_party archives.

   Copyright [2020] [© 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE_2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

/*
Security Hub Import Commands

Run this to get state of SecurityHub

terraform import aws_securityhub_account.securityhubresource 123456789012

*/

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.54"
    }
  }
}
provider "aws" {
  region = var.select_region
}

resource "aws_iam_role" "prowler_kick_start_role" {
  name                = "security_baseline_kickstarter_iam_role"
  managed_policy_arns = ["${data.aws_iam_policy.SecurityAudit.arn}",
                         "arn:aws:iam::aws:policy/job-function/SupportUser",
                         "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"]
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action="sts:AssumeRole"
        Effect="Allow"
        Sid = "CodeBuildProwler"
        Principal = {Service="codebuild.amazonaws.com"}
      }
    ]
  })
  force_detach_policies=true
}
resource "aws_iam_role" "prowler_event_trigger_role" {
    name = "security_baseline_kickstarter_event_trigger_iam_role"
    assume_role_policy = jsonencode({
                              Version = "2012-10-17"
                              Statement = [
                                {
                                  Action="sts:AssumeRole"
                                  Effect="Allow"
                                  Sid = "TriggerCodeBuild"
                                  Principal = {Service="events.amazonaws.com"}
                                }
                              ]
                            })
    
}
resource "aws_iam_policy" "prowler_event_trigger_policy" {
  depends_on        = [aws_codebuild_project.prowler_codebuild]
  name        = "security_baseline_kickstarter_trigger_iam_policy"
  path        = "/"
  description = "IAM Policy used to trigger the Prowler in AWS Codebuild"
  policy = jsonencode({
     Version = "2012-10-17"
    Statement = [
      {
        Action =  ["codebuild:StartBuild"],
        Effect   = "Allow"
        Resource =  aws_codebuild_project.prowler_codebuild.arn
      }]
  })
}
resource "aws_iam_policy_attachment" "prowler_event_trigger_policy_attach" {
  depends_on = [aws_iam_policy.prowler_event_trigger_policy]
  name       = "prowler_event_trigger_policy_attach"
  roles      = toset([aws_iam_role.prowler_event_trigger_role.id])
  policy_arn = aws_iam_policy.prowler_event_trigger_policy.arn
}
resource "aws_iam_policy" "prowler_kickstarter_iam_policy" {
  name        = "security_baseline_kickstarter_iam_policy"
  path        = "/"
  description = "IAM Policy used to run prowler from codebuild"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action =  [
            "logs:PutLogEvents"
            ],
        Effect   = "Allow"
        Resource =  "arn:aws:logs:*:${data.aws_caller_identity.current.account_id}:log-group:*:log-stream:*"
      },
       {
        Action =  [
            "logs:CreateLogStream",
            "logs:CreateLogGroup"
            ],
        Effect   = "Allow"
        Resource =  "arn:aws:logs:*:${data.aws_caller_identity.current.account_id}:log-group:*"
      },
       {
        Action =  ["sts:AssumeRole"],
        Effect   = "Allow"
        Resource =  "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${aws_iam_role.prowler_kick_start_role.name}"
      },
      {
        Action =  [
                  "ds:ListAuthorizedApplications",
                  "ec2:GetEbsEncryptionByDefault",
                  "ecr:Describe*",
                  "elasticfilesystem:DescribeBackupPolicy",
                  "glue:GetConnections",
                  "glue:GetSecurityConfiguration",
                  "glue:SearchTables",
                  "lambda:GetFunction",
                  "s3:GetAccountPublicAccessBlock",
                  "shield:DescribeProtection",
                  "shield:GetSubscriptionState",
                  "ssm:GetDocument",
                  "support:Describe*",
                  "tag:GetTagKeys"
                  ]
        Effect   = "Allow"
        Resource = "arn:aws:glue:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:catalog"
                                    
      },
      {
        Action =  [
                "codebuild:CreateReportGroup",
                "codebuild:CreateReport",
                "codebuild:UpdateReport",
                "codebuild:BatchPutTestCases",
                "codebuild:BatchPutCodeCoverages"
                                    ]
        Effect   = "Allow"
        Resource = "arn:aws:codebuild:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:report-group/*"
                                    
      },
       {
        Action =  [ "securityhub:BatchImportFindings"]
        Effect   = "Allow"
        Resource = "*"                      
      },
             {
        Action =  [ "securityhub:GetFindings"]
        Effect   = "Allow"
        Resource = "*"                      
      },
      { 
            "Action": "codebuild:StartBuild",
            "Resource": "arn:aws:codebuild:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:project/*",
            "Effect": "Allow"
      },
      {
            "Action": ["s3:PutObject", "s3:GetObject", "s3:GetObjectVersion", "s3:GetBucketAcl", "s3:GetBucketLocation"],
            "Resource": "arn:aws:s3:::prowler-kickstart-${data.aws_region.current.name}-${data.aws_caller_identity.current.account_id}-reports/*",
            "Effect": "Allow"
      },
    ]
  })
}
resource "aws_iam_policy_attachment" "prowler_kickstarter_iam_policy_attach" {
  depends_on = [aws_iam_policy.prowler_kickstarter_iam_policy]
  name       = "security_baseline_kickstarter_policy_attach"
  roles      = toset([aws_iam_role.prowler_kick_start_role.id])
  policy_arn = aws_iam_policy.prowler_kickstarter_iam_policy.arn
}
resource "aws_s3_bucket" "prowler_report_storage_bucket" {
    bucket = "prowler-kickstart-${data.aws_region.current.name}-${data.aws_caller_identity.current.account_id}-reports"
    acl = "log-delivery-write"
    versioning {
        enabled = true
    }
    server_side_encryption_configuration {
        rule {
            apply_server_side_encryption_by_default {
                sse_algorithm     = "AES256"
            }
        }
    }
    }

resource "aws_s3_bucket_policy" "prowler_report_storage_bucket_policy" {
    depends_on = [aws_s3_bucket.prowler_report_storage_bucket]
    bucket = aws_s3_bucket.prowler_report_storage_bucket.id
    policy = jsonencode({Version = "2012-10-17"
                            Id      = "ProwlerBucketReportPolicy"
                            Statement = [
                            {
                                Sid       = "S3ForceSSL"
                                Effect    = "Deny"
                                Principal = "*"
                                Action    = "s3:*"
                                Resource = ["${aws_s3_bucket.prowler_report_storage_bucket.arn}/*"]
                                Condition = {
                                Bool = {
                                    "aws:SecureTransport" = "false"
                                    }
                                }
                            },
                            {
                                Sid       = "DenyUnEncryptedObjectUploads"
                                Effect    = "Deny"
                                Principal = "*"
                                Action    = "s3:*"
                                Resource = ["${aws_s3_bucket.prowler_report_storage_bucket.arn}/*"]
                                Condition = {
                                Null = {
                                    "s3:x-amz-server-side-encryption" = "true"
                                    }
                                }
                            }
                            
                            ]
                        })
                        }



resource "aws_s3_bucket_public_access_block" "prowler_report_storage_bucket_block_public" {
    depends_on = [aws_s3_bucket.prowler_report_storage_bucket, aws_s3_bucket_policy.prowler_report_storage_bucket_policy]
    bucket = aws_s3_bucket.prowler_report_storage_bucket.id
    block_public_acls   = true
    block_public_policy = true
    ignore_public_acls = true
    restrict_public_buckets = true
    }

resource "aws_codebuild_project" "prowler_codebuild" {
  name          = "security_baseline_kickstarter_codebuild"
  description   = "Run a Prowler Assessment with Prowler"
  build_timeout = var.codebuild_timeout
  service_role  = aws_iam_role.prowler_kick_start_role.arn

  artifacts {
    type = "NO_ARTIFACTS"
  }

  environment {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/amazonlinux2-x86_64-standard:3.0"
    type                        = "LINUX_CONTAINER"

    environment_variable {
      name  = "BUCKET_REPORT"
      value = "${aws_s3_bucket.prowler_report_storage_bucket.id}"
    }

    environment_variable {
      name  = "PROWLER_OPTIONS"
      type  = "PLAINTEXT"
      value = var.prowler_cli_options
        }
    }


  source {
    type            = "NO_SOURCE"
    buildspec       =  "${file("prowler_build_spec.yml")}"
    }

  tags = {
    Environment = "Prowler KickStarter"
        }
    }





resource "aws_securityhub_account" "securityhub_resource" { 
    }

resource "aws_securityhub_product_subscription" "security_hub_enable_prowler_findings" {
  depends_on  = [aws_securityhub_account.securityhub_resource]
  //arn:aws:securityhub:<REGION>::product/prowler/prowler
  product_arn = "arn:aws:securityhub:${data.aws_region.current.name}::product/prowler/prowler"
    }

resource "aws_cloudwatch_event_rule" "prowler_check_scheduler_event" {

    name = "security_baseline_kickstarter_event_cron"
    description = "Run Prowler every night"
    schedule_expression = var.prowler_schedule
    }

resource "aws_cloudwatch_event_target" "run_prowler_scan" {

  arn       = aws_codebuild_project.prowler_codebuild.arn
  rule      = aws_cloudwatch_event_rule.prowler_check_scheduler_event.name
  role_arn  = aws_iam_role.prowler_event_trigger_role.arn

    }
contrib/terraform-kickstarter/data.tf METASEP
/*
© 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non_exclusive, no_charge, royalty_free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non_exclusive, no_charge, royalty_free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross_claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third_party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON_INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third_party archives.

   Copyright [2020] [© 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE_2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
data "aws_iam_policy" "SecurityAudit" {
  arn = "arn:aws:iam::aws:policy/SecurityAudit"
}
data "aws_caller_identity" "current" {
}
data "aws_region" "current" {
}
contrib/org-multi-account/README.md METASEP
# Example Solution:  Organizational Prowler Deployment

Deploys [Prowler](https://github.com/prowler-cloud/prowler) to assess all Accounts in an AWS Organization on a schedule, creates assessment reports in HTML, and stores them in an S3 bucket.

---

## Example Solution Goals

- Using minimal technologies, so solution can be more easily adopted, and further enhanced as needed.
  - [Amazon EC2](https://aws.amazon.com/ec2/), to run Prowler
  - [Amazon S3](https://aws.amazon.com/s3/), to store Prowler script & reports.
  - [AWS CloudFormation](https://aws.amazon.com/cloudformation/), to provision the AWS resources.
  - [AWS Systems Manager Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html), Optional, but recommended, to manage the Prowler EC2 instance, without having to allow inbound ssh.
- Staying cohesive with Prowler, for scripting, only leveraging:
  - Bash Shell
  - AWS CLI
- Adhering to the principle of least privilege.
- Supporting an AWS Multi-Account approach
  - Runs Prowler against All accounts in the AWS Organization
- ***NOTE: If using this solution, you are responsible for making your own independent assessment of the solution and ensuring it complies with your company security and operational standards.***

---

## Components

1. [ProwlerS3.yaml](ProwlerS3.yaml)
    - Creates Private S3 Bucket for Prowler script and reports.
    - Enables [Amazon S3 Block Public Access](https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html)
    - Enables SSE-S3 with [Amazon S3 Default Encryption](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html)
    - Versioning Enabled
    - Bucket Policy limits API actions to Principals from the same AWS Organization.
1. [ProwlerRole.yaml](ProwlerRole.yaml)
    - Creates Cross-Account Role for Prowler to assess accounts in AWS Organization
    - Allows Role to be assumed by the Prowler EC2 instance role in the AWS account where Prowler EC2 resides (preferably the Audit/Security account).
    - Role has [permissions](https://github.com/prowler-cloud/prowler#custom-iam-policy) needed for Prowler to assess accounts.
    - Role has rights to Prowler S3 from Component #1.
1. [ProwlerEC2.yaml](ProwlerEC2.yaml)
    - Creates Prowler EC2 instance
      - Uses the Latest Amazon Linux 2 AMI
      - Uses ```t2.micro``` Instance Type
      - Encrypts Root Volume with AWS Managed Key "aws/ebs"
    - Uses [cfn-init](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-init.html) for prepping the Prowler EC2
      - Installs necessary [packages](https://github.com/prowler-cloud/prowler#requirements-and-installation) for Prowler
      - Downloads [run-prowler-reports.sh](src/run-prowler-reports.sh) script from Prowler S3 from Component #1.
      - Creates ```/home/ec2-user/.awsvariables```, to store CloudFormation data as variables to be used in script.
      - Creates cron job for Prowler to run on a schedule.
    - Creates Prowler Security Group
      - Denies inbound access.  If using ssh to manage Prowler, then update Security Group with pertinent rule.
      - Allows outbound 80/443 for updates, and Amazon S3 communications      -
    - Creates Instance Role that is used for Prowler EC2
      - Role has permissions for [Systems Manager Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) communications, and [Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html)
      - Role has rights to Prowler S3 from Component #1.
      - Role has rights to Assume Cross-Account Role from Component #2.
1. [run-prowler-reports.sh](src/run-prowler-reports.sh)
    - Script is documented accordingly.
    - Script loops through all AWS Accounts in AWS Organization, and by default, Runs Prowler as follows:
      - -R: used to specify Cross-Account role for Prowler to assume to run its assessment.
      - -A: used to specify AWS Account number for Prowler to run assessment against.
      - -g cislevel1: used to specify cislevel1 checks for Prowler to assess

        ```bash
        ./prowler/prowler -R "$ROLE" -A "$accountId" -g cislevel1 -M html
        ```

      - NOTE: Script can be modified to run Prowler as desired.
    - Script runs Prowler against 1 AWS Account at a time.
      - Update PARALLEL_ACCOUNTS variable in script, to specify how many Accounts to assess with Prowler in parallel.
      - If running against multiple AWS Accounts in parallel, monitor performance, and upgrade Instance Type as necessary.

        ```bash
        PARALLEL_ACCOUNTS="1"
        ```

    - In summary:
      - Download latest version of [Prowler](https://github.com/prowler-cloud/prowler)
      - Find AWS Master Account
      - Lookup All Accounts in AWS Organization
      - Run Prowler against All Accounts in AWS Organization
      - Save Reports to reports prefix in S3 from Component #1
      - Report Names: date+time-accountid-report.html

---

## Instructions

1. Deploy [ProwlerS3.yaml](ProwlerS3.yaml) in the Logging Account.
    - Could be deployed to any account in the AWS Organizations, if desired.
    - See [How to get AWS Organization ID](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_org_details.html#orgs_view_org)
    - Take Note of CloudFormation Outputs, that will be needed in deploying the below CloudFormation templates.
1. Upload [run-prowler-reports.sh](src/run-prowler-reports.sh) to the root of the S3 Bucket created in Step #1.
1. Deploy [ProwlerRole.yaml](ProwlerRole.yaml) in the Master Account
    - Use CloudFormation Stacks, to deploy to Master Account, as organizational StackSets don't apply to the Master Account.
    - Use CloudFormation StackSet, to deploy to all Member Accounts. See [Create Stack Set with Service-Managed Permissions](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-getting-started-create.html#stacksets-orgs-associate-stackset-with-org)
    - Take Note of CloudFormation Outputs, that will be needed in deploying the below CloudFormation templates.
1. Deploy [ProwlerEC2.yaml](ProwlerEC2.yaml) in the Audit/Security Account
    - Could be deployed to any account in the AWS Organizations, if desired.
1. Prowler will run against all Accounts in AWS Organization, per the schedule you provided, and set in a cron job for ```ec2-user```

---

## Post-Setup

### Run Prowler on a Schedule against all Accounts in AWS Organization

1. Prowler will run on the Schedule you provided.
1. Cron job for ```ec2-user``` is managing the schedule.
1. This solution implemented this automatically. Nothing for you to do.

### Ad hoc Run Prowler against all Accounts in AWS Organization

1. Connect to Prowler EC2 Instance
    - If using Session Manager, then after login, switch to ```ec2-user```, via: ```sudo bash``` and ```su - ec2-user```
    - If using SSH, then login as ```ec2-user```
1. Run Prowler Script

    ```bash
    cd /home/ec2-user
    ./run-prowler-reports.sh
    ```

### Ad hoc Run Prowler Interactively

1. Connect to Prowler EC2 Instance
    - If using Session Manager, then after login, switch to ```ec2-user```, via: ```sudo bash``` and ```su - ec2-user```
    - If using SSH, then login as ```ec2-user```
1. See Cross-Account Role and S3 Bucket being used for Prowler

      ```bash
      cd /home/ec2-user
      cat .awsvariables
      ```

1. Run Prowler interactively. See [Usage Examples](https://github.com/prowler-cloud/prowler#usage)

      ```bash
      cd /home/ec2-user
      ./prowler/prowler
      ```

### Upgrading Prowler to Latest Version

1. Connect to Prowler EC2 Instance
    - If using Session Manager, then after login, switch to ```ec2-user```, via: ```sudo bash``` and ```su - ec2-user```
    - If using SSH, then login as ```ec2-user```
1. Delete the existing version of Prowler, and download the latest version of Prowler

    ```bash
    cd /home/ec2-user
    rm -rf prowler
    git clone https://github.com/prowler-cloud/prowler.git
    ```

contrib/org-multi-account/ProwlerS3.yaml METASEP
AWSTemplateFormatVersion: 2010-09-09
Description: Create Prowler S3 Bucket for Prowler Reports

Parameters:
  AwsOrgId:
    Type: String
    Description: >
      Enter AWS Organizations ID.
      This is used to restrict permissions to least privilege.
    AllowedPattern: ^o-[a-z0-9]{10,32}$
    ConstraintDescription: The Org Id must be a 12 character string starting with o- and followed by 10 lower case alphanumeric characters.
    Default: o-abcde12345
  S3Prefix:
    Type: String
    Description: >
      Enter S3 Bucket Name Prefix (in lowercase).
      Bucket will be named: prefix-awsaccount-awsregion (i.e., prowler-123456789012-us-east-1)
    AllowedPattern: ^[a-z0-9][a-z0-9-]{1,33}[a-z0-9]$
    ConstraintDescription: >
      Max 35 characters, as "-awsaccount-awsregion" will be added, and max name is 63 characters.
      Can't start or end with dash.  Can use numbers and lowercase letters.
    Default: prowler

Resources:
  ProwlerS3:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${S3Prefix}-${AWS::AccountId}-${AWS::Region}
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: "AES256"
      AccessControl: Private
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      VersioningConfiguration:
        Status: Enabled
      Tags:
        - Key: App
          Value: Prowler
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W35
            reason: "This S3 Bucket is only being used by the AWS Organization to download/upload prowler reports."

  ProwlerS3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref ProwlerS3
      PolicyDocument:
        Statement:
          - Sid: AllowGetPutListObject
            Effect: Allow
            Principal: "*"
            Action:
              - s3:GetObject
              - s3:PutObject
              - s3:ListBucket
              - s3:PutObjectAcl
            Resource:
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
            Condition:
              StringEquals:
                aws:PrincipalOrgId: !Ref AwsOrgId
          - Sid: DenyNonSSLRequests
            Effect: Deny
            Action: s3:*
            Resource:
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
            Principal: "*"
            Condition:
              Bool:
                aws:SecureTransport: false
          - Sid: DenyIncorrectEncryptionHeader
            Effect: Deny
            Principal: "*"
            Action: s3:PutObject
            Resource:
              - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
            # Allow uploads with No Encryption, as S3 Default Encryption still applies.
            # If Encryption is set, only allow uploads with AES256.
            Condition:
              "Null":
                s3:x-amz-server-side-encryption: false
              StringNotEquals:
                s3:x-amz-server-side-encryption: AES256
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: F16
            reason: "This S3 Bucket Policy has a condition that only allows access to the AWS Organization."


Outputs:
  ProwlerS3:
    Description: S3 Bucket for Prowler Reports
    Value: !Ref ProwlerS3
  ProwlerS3Account:
    Description: AWS Account Number where Prowler S3 Bucket resides.
    Value: !Ref AWS::AccountId

contrib/org-multi-account/ProwlerRole.yaml METASEP
AWSTemplateFormatVersion: 2010-09-09
Description: Create the Cross-Account IAM Prowler Role

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: EC2 Settings
        Parameters:
          - ProwlerEc2Account
          - ProwlerEc2Role
      - Label:
          default: S3 Settings
        Parameters:
          - ProwlerS3
      - Label:
          default: CrossAccount Role
        Parameters:
          - ProwlerCrossAccountRole

Parameters:
  ProwlerS3:
    Type: String
    Description: Enter S3 Bucket for Prowler Reports.  prefix-awsaccount-awsregion
    AllowedPattern: ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$
    Default: prowler-123456789012-us-east-1
  ProwlerEc2Account:
    Type: String
    Description: Enter AWS Account Number where Prowler EC2 Instance will reside.
    AllowedPattern: ^\d{12}$
    ConstraintDescription: An AWS Account Number must be a 12 digit numeric string.
  ProwlerEc2Role:
    Type: String
    Description: Enter Instance Role that will be given to the Prowler EC2 Instance (needed to grant sts:AssumeRole rights).
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerEC2-Role
  ProwlerCrossAccountRole:
    Type: String
    Description: Enter Name for CrossAccount Role to be created for Prowler to assess all Accounts in the AWS Organization.
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerXA-Role

Resources:
  ProwlerRole:
    Type: AWS::IAM::Role
    Properties:
      Description: Provides Prowler EC2 instance permissions to assess security of Accounts in AWS Organization
      RoleName: !Ref ProwlerCrossAccountRole
      Tags:
        - Key: App
          Value: Prowler
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                - !Sub arn:${AWS::Partition}:iam::${ProwlerEc2Account}:root
            Action:
              - sts:AssumeRole
            Condition:
              StringLike:
                aws:PrincipalArn: !Sub arn:${AWS::Partition}:iam::${ProwlerEc2Account}:role/${ProwlerEc2Role}
      ManagedPolicyArns:
        - !Sub arn:${AWS::Partition}:iam::aws:policy/SecurityAudit
        - !Sub arn:${AWS::Partition}:iam::aws:policy/job-function/ViewOnlyAccess
      Policies:
        - PolicyName: Prowler-Additions-Policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowMoreReadForProwler
                Effect: Allow
                Resource: "*"
                Action:
                  - ds:ListAuthorizedApplications
                  - ec2:GetEbsEncryptionByDefault
                  - ecr:Describe*
                  - elasticfilesystem:DescribeBackupPolicy
                  - glue:GetConnections
                  - glue:GetSecurityConfiguration
                  - glue:SearchTables
                  - lambda:GetFunction
                  - s3:GetAccountPublicAccessBlock
                  - shield:DescribeProtection
                  - shield:GetSubscriptionState
                  - ssm:GetDocument
                  - support:Describe*
                  - tag:GetTagKeys
        - PolicyName: Prowler-S3-Reports
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowGetPutListObject
                Effect: Allow
                Resource:
                  - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}
                  - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:ListBucket
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W11
            reason: "Prowler requires these rights to perform its Security Assessment."
          - id: W28
            reason: "Using a defined Role Name."

Outputs:
  ProwlerCrossAccountRole:
    Description: CrossAccount Role to be used by Prowler to assess AWS Accounts in the AWS Organization.
    Value: !Ref ProwlerCrossAccountRole

contrib/org-multi-account/ProwlerEC2.yaml METASEP
AWSTemplateFormatVersion: 2010-09-09
Description: Create Prowler EC2 with UserData (Shell Scripts, & AWS CLI Profiles)

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Prowler EC2 Instance Settings
        Parameters:
          - BuildNumber
          - ProwlerEc2Name
          - InstanceType
          - KeyPair
          - SubnetId
          - VpcId
          - Ec2Role
          - LatestAmazonLinux2AmiId
          - ProwlerCron
      - Label:
          default: S3 Settings
        Parameters:
          - ProwlerS3
          - ProwlerS3Account
      - Label:
          default: CrossAccount Role
        Parameters:
          - AwsOrgId
          - CrossAccountRole

Parameters:
  BuildNumber:
    Type: String
    Description: Enter Build Number (increment with Updates for cfn-init)
    AllowedPattern: ^\d*$
    ConstraintDescription: Build Number must be a numeric string.
    Default: 1
  ProwlerEc2Name:
    Type: String
    Description: Enter Name for Prowler EC2 Instance to create
    AllowedPattern: ^[\w\s_.\/=+-]{1,128}$
    ConstraintDescription: Max 128 alphanumeric characters. Also special characters supported [whitespace, _, ., /, =, +, -]
    Default: Prowler-EC2
  InstanceType:
    Description: Enter Instance Type
    Type: String
    Default: t2.micro
  KeyPair:
    Description: Choose a KeyPair
    Type: AWS::EC2::KeyPair::KeyName
  SubnetId:
    Description: Choose Subnet
    Type: AWS::EC2::Subnet::Id
  VpcId:
    Description: Choose VPC
    Type: AWS::EC2::VPC::Id
  Ec2Role:
    Description: Enter Name for EC2 Instance Role to create and attach to Prowler EC2 Instance
    Type: String
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -]
    Default: ProwlerEC2-Role
  ProwlerCron:
    Description: Enter cron schedule.  Default, runs everyday at 1am. See https://crontab.guru/, for syntax help.
    Type: String
    Default: "0 1 * * *"
  LatestAmazonLinux2AmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Description: Latest AMI ID for Amazon Linux 2 (via AWS Publis SSM Parameters. See https://tinyurl.com/aws-public-ssm-parameters.
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-ebs

  ProwlerS3:
    Type: String
    Description: Enter S3 Bucket for Prowler Reports.  prefix-awsaccount-awsregion
    AllowedPattern: ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$
    ConstraintDescription: Max 63 characters. Can't start or end with dash.  Can use numbers and lowercase letters.
    Default: prowler-123456789012-us-east-1
  ProwlerS3Account:
    Type: String
    Description: Enter AWS Account Number where Prowler S3 Bucket resides.
    AllowedPattern: ^\d{12}$
    ConstraintDescription: An AWS Account Number must be a 12 digit numeric string.
    Default: 123456789012

  AwsOrgId:
    Type: String
    Description: Enter AWS Organizations ID
    AllowedPattern: ^o-[a-z0-9]{10,32}$
    ConstraintDescription: The Org Id must be a 12 character string starting with o- and followed by 10 lower case alphanumeric characters.
    Default: o-abcde12345
  CrossAccountRole:
    Type: String
    Description: Enter CrossAccount Role Prowler will be using to assess AWS Accounts in the AWS Organization. (ProwlerCrossAccountRole)
    AllowedPattern: ^[\w+=,.@-]{1,64}$
    ConstraintDescription: Max 64 alphanumeric characters. Also special characters [+, =, ., @, -]
    Default: ProwlerXA-Role

Resources:
  ProwlerEc2:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M
    Properties:
      KeyName: !Ref KeyPair
      ImageId: !Ref LatestAmazonLinux2AmiId
      IamInstanceProfile: !Ref ProwlerInstanceProfile
      InstanceType: !Ref InstanceType
      SubnetId: !Ref SubnetId
      SecurityGroupIds:
        - !Ref ProwlerSecurityGroup
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            Encrypted: true
            KmsKeyId: alias/aws/ebs
            VolumeType: standard
            DeleteOnTermination: true
            VolumeSize: 8
      Tags:
        - Key: Name
          Value: !Ref ProwlerEc2Name
        - Key: App
          Value: Prowler
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash
            yum update -y aws-cfn-bootstrap
            /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ProwlerEc2 --configsets onfirstboot --region ${AWS::Region}
            yum -y update
            /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ProwlerEc2 --region ${AWS::Region}
    Metadata:
      AWS::CloudFormation::Authentication:
        S3AccessCreds:
          type: S3
          buckets:
            - !Ref ProwlerS3
          roleName:
            Ref: ProwlerEc2Role
      AWS::CloudFormation::Init:
        configSets:
          onfirstboot:
            - build-number
            - configure-cfn
            - prowler-prereqs
            - prowler-reports
            - prowler-schedule
          onupdate:
            - build-number
            - prowler-prereqs
            - prowler-reports
            - prowler-schedule
        build-number:
          commands:
            show-build-number:
              command: !Sub |
                echo "BUILDNUMBER:   ${BuildNumber}"
        configure-cfn:
          files:
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.ProwlerEc2.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ProwlerEc2 --configsets onupdate --region ${AWS::Region}
                runas=root
              mode: "000400"
              owner: root
              group: root
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                verbose=true
                interval=5
              mode: "000400"
              owner: root
              group: root
          services:
            sysvinit:
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
        prowler-prereqs:
          files:
            /home/ec2-user/.awsvariables:
              content: !Sub |
                export S3=s3://${ProwlerS3}
                export S3ACCOUNT=${ProwlerS3Account}
                export ROLE=${CrossAccountRole}
              mode: "000600"
              owner: ec2-user
              group: ec2-user
          commands:
            01-install-prowler-prereqs-yum:
              command: |
                yum install python-pip git jq -y
            02-install-prowler-prereqs-pip:
              command: |
                sudo -u ec2-user pip install --user boto3 awscli detect-secrets
        prowler-reports:
          files:
            /home/ec2-user/run-prowler-reports.sh:
              source: !Sub https://${ProwlerS3}.s3.${AWS::Region}.amazonaws.com/run-prowler-reports.sh
              mode: "000700"
              owner: ec2-user
              group: ec2-user
        prowler-schedule:
          files:
            /home/ec2-user/mycron-prowler:
              content: !Sub |
                ${ProwlerCron} bash -lc ./run-prowler-reports.sh > mycron-prowler.log
              mode: "000600"
              owner: ec2-user
              group: ec2-user
          commands:
            01-create-prowler-cron-job:
              command: |
                sudo -u ec2-user crontab /home/ec2-user/mycron-prowler

  ProwlerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: Prowler-EC2-RemoteAdministration
      GroupDescription: Allow Remote Administration
      Tags:
        - Key: App
          Value: Prowler
      VpcId: !Ref VpcId
      SecurityGroupEgress:
        - Description: Allow HTTP Outbound
          IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - Description: Allow HTTPS Outbound
          IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W5
            reason: "Using http/https to Internet for updates."
          - id: W28
            reason: "Using a defined Security Group Name."

  ProwlerInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref ProwlerEc2Role

  ProwlerEc2Role:
    Type: AWS::IAM::Role
    Properties:
      Description: Prowler EC2 Instance Role
      RoleName: !Ref Ec2Role
      Tags:
        - Key: App
          Value: Prowler
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: SSM-Agent
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowSsmAgent
                Effect: Allow
                Resource: "*"
                Action:
                  - ssm:UpdateInstanceInformation
                  - ssm:ListInstanceAssociations
                  - ssm:UpdateInstanceAssociationStatus
                  - ssm:PutConfigurePackageResult
                  - ssm:GetManifest
                  - ssm:PutComplianceItems
                  - ec2messages:AcknowledgeMessage
                  - ec2messages:DeleteMessage
                  - ec2messages:FailMessage
                  - ec2messages:GetEndpoint
                  - ec2messages:GetMessages
                  - ec2messages:SendReply
        - PolicyName: SSM-Inventory
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowPutInventory
                Effect: Allow
                Resource: "*"
                Action:
                  - ssm:PutInventory
              - Sid: AllowGatherInventory
                Effect: Allow
                Resource: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-GatherSoftwareInventory
                Action:
                  - ssm:GetDocument
                  - ssm:DescribeDocument
        - PolicyName: SSM-SessionManager
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowSessionManager
                Effect: Allow
                Resource: "*"
                Action:
                  - ssmmessages:CreateControlChannel
                  - ssmmessages:CreateDataChannel
                  - ssmmessages:OpenControlChannel
                  - ssmmessages:OpenDataChannel
        - PolicyName: Prowler-S3-Reports
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowGetPutListObject
                Effect: Allow
                Resource:
                  - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}
                  - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/*
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:ListBucket
                  - s3:PutObjectAcl
        - PolicyName: Prowler-CrossAccount-AssumeRole
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowStsAssumeRole
                Effect: Allow
                Resource: !Sub arn:${AWS::Partition}:iam::*:role/${CrossAccountRole}
                Action: sts:AssumeRole
                Condition:
                  StringEquals:
                    aws:PrincipalOrgId: !Ref AwsOrgId
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W28
            reason: "Using a defined Role Name."
          - id: W11
            reason: "Needed for SSM features."

Outputs:
  ProwlerEc2Account:
    Description: AWS Account Number where Prowler EC2 Instance resides.
    Value: !Ref AWS::AccountId
  ProwlerEc2Role:
    Description: Instance Role given to the Prowler EC2 Instance (needed to grant sts:AssumeRole rights).
    Value: !Ref ProwlerEc2Role
  ProwlerS3:
    Description: S3 Bucket for Prowler Reports
    Value: !Ref ProwlerS3

contrib/multi-account-securityhub/run-prowler-securityhub.sh METASEP
#!/bin/bash
# Run Prowler against All AWS Accounts in an AWS Organization

# Change Directory (rest of the script, assumes you're in the root directory)
cd / || exit

# Show Prowler Version
./prowler/prowler -V

# Source .awsvariables
# shellcheck disable=SC1091
source .awsvariables

# Get Values from Environment Variables
echo "ROLE:               $ROLE"
echo "PARALLEL_ACCOUNTS:  $PARALLEL_ACCOUNTS"
echo "REGION:             $REGION"

# Function to unset AWS Profile Variables
unset_aws() {
    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}
unset_aws

# Find THIS Account AWS Number
CALLER_ARN=$(aws sts get-caller-identity --output text --query "Arn")
PARTITION=$(echo "$CALLER_ARN" | cut -d: -f2)
THISACCOUNT=$(echo "$CALLER_ARN" | cut -d: -f5)
echo "THISACCOUNT:    $THISACCOUNT"
echo "PARTITION:      $PARTITION"

# Function to Assume Role to THIS Account & Create Session
this_account_session() {
    unset_aws
    role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$THISACCOUNT":role/"$ROLE" --role-session-name ProwlerRun --output json)
    AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId)
    AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey)
    AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken)
    export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}

# Find AWS Master Account
this_account_session
AWSMASTER=$(aws organizations describe-organization --query Organization.MasterAccountId --output text)
echo "AWSMASTER:      $AWSMASTER"

# Function to Assume Role to Master Account & Create Session
master_account_session() {
    unset_aws
    role_credentials=$(aws sts assume-role --role-arn arn:"$PARTITION":iam::"$AWSMASTER":role/"$ROLE" --role-session-name ProwlerRun --output json)
    AWS_ACCESS_KEY_ID=$(echo "$role_credentials" | jq -r .Credentials.AccessKeyId)
    AWS_SECRET_ACCESS_KEY=$(echo "$role_credentials" | jq -r .Credentials.SecretAccessKey)
    AWS_SESSION_TOKEN=$(echo "$role_credentials" | jq -r .Credentials.SessionToken)
    export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
}

# Lookup All Accounts in AWS Organization
master_account_session
ACCOUNTS_IN_ORGS=$(aws organizations list-accounts --query Accounts[*].Id --output text)

# Run Prowler against Accounts in AWS Organization
echo "AWS Accounts in Organization"
echo "$ACCOUNTS_IN_ORGS"
for accountId in $ACCOUNTS_IN_ORGS; do
    # shellcheck disable=SC2015
    test "$(jobs | wc -l)" -ge $PARALLEL_ACCOUNTS && wait -n || true
    {
        START_TIME=$SECONDS
        # Unset AWS Profile Variables
        unset_aws
        # Run Prowler
        echo -e "Assessing AWS Account: $accountId, using Role: $ROLE on $(date)"
        # Pipe stdout to /dev/null to reduce unnecessary Cloudwatch logs
        ./prowler/prowler -R "$ROLE" -A "$accountId" -M json-asff -q -S -f "$REGION" > /dev/null
        TOTAL_SEC=$((SECONDS - START_TIME))
        printf "Completed AWS Account: $accountId in %02dh:%02dm:%02ds" $((TOTAL_SEC / 3600)) $((TOTAL_SEC % 3600 / 60)) $((TOTAL_SEC % 60))
        echo ""
    } &
done

# Wait for All Prowler Processes to finish
wait
echo "Prowler Assessments Completed against All Accounts in AWS Organization"

# Unset AWS Profile Variables
unset_aws

contrib/multi-account-securityhub/README.md METASEP
# Example Solution:  Serverless Organizational Prowler Deployment with SecurityHub

Deploys [Prowler](https://github.com/prowler-cloud/prowler) with AWS Fargate to assess all Accounts in an AWS Organization on a schedule, and sends the results to Security Hub.

## Context
Originally based on [org-multi-account](https://github.com/prowler-cloud/prowler/tree/master/util/org-multi-account), but changed in the following ways:

 - No HTML reports and no S3 buckets
 - Findings sent directly to Security Hub using the native integration
 - AWS Fargate Task with EventBridge Rule instead of EC2 instance with cronjob
 - Based on amazonlinux:2022 to leverage "wait -n" for improved parallelization as new jobs are launched as one finishes

## Architecture Explanation

 The solution is designed to be very simple. Prowler is run via an ECS Task definition that launches a single Fargate container. This Task Definition is executed on a schedule using an EventBridge Rule.

## CloudFormation Templates
 
 ### CF-Prowler-IAM.yml
Creates the following IAM Roles:

 1. **ECSExecutionRole**: Required for the Task Definition to be able to fetch the container image from ECR and launch the container.
 2. **ProwlerTaskRole**: Role that the container itself runs with. It allows it to assume the ProwlerCrossAccountRole.
 3. **ECSEventRoleName**: Required for the EventBridge Rule to execute the Task Definition.

### CF-Prowler-ECS.yml
Creates the following resources:

 1. **ProwlerECSCluster**: Cluster to be used to execute the Task Definition.
 2. **ProwlerECSCloudWatchLogsGroup**: Log group for the Prowler container logs. This is required because it's the only log driver supported by Fargate. The Prowler executable logs are suppressed to prevent unnecessary logs, but error logs are kept for debugging.
 3. **ProwlerECSTaskDefinition**: Task Definition for the Fargate container. CPU and memory can be increased as needed. In my experience, 1 CPU per parallel Prowler job is ideal, but further performance testing may be required to find the optimal configuration for a specific organization. Enabling container insights helps a lot with this.
 4. **ProwlerSecurityGroup**: Security Group for the container. It only allows TCP 443 outbound, as it is the only port needed for awscli.
 5. **ProwlerTaskScheduler**: EventBridge Rule that schedules the execution of the Task Definition. The cron expression is specified as a CloudFormation template parameter.

### CF-Prowler-CrossAccountRole.yml
Creates the cross account IAM Role required for Prowler to run. Deploy it as StackSet in every account in the AWS Organization.  

## Docker Container

### Dockerfile
The Dockerfile does the following:
 1. Uses amazonlinux:2022 as a base.
 2. Downloads required dependencies.
 3. Copies the .awsvariables and run-prowler-securityhub.sh files into the root.
 4. Downloads the specified version of Prowler as recommended in the release notes. 
 5. Assigns permissions to a lower privileged user and then drops to it.
 6. Runs the script.

### .awsvariables
The .awsvariables file is used to pass required configuration to the script:

 1. **ROLE**: The cross account Role to be assumed for the Prowler assessments.
 2. **PARALLEL_ACCOUNTS**: The number of accounts to be scanned in parallel.
 3. **REGION**: Region where Prowler will run its assessments.

### run-prowler-securityhub.sh
The script gets the list of accounts in AWS Organizations, and then executes Prowler as a job for each account, up to PARALLEL_ACCOUNT accounts at the same time.
The logs that are generated and sent to Cloudwatch are error logs, and assessment start and finish logs.

## Instructions
 1. Create a Private ECR Repository in the account that will host the Prowler container. The Audit account is recommended, but any account can be used. 
 2.  Configure the .awsvariables file. Note the ROLE name chosen as it will be the CrossAccountRole.
 3. Follow the steps from "View Push Commands" to build and upload the container image. You need to have Docker and AWS CLI installed, and use the cli to login to the account first.  After upload note the Image URI, as it is required for the CF-Prowler-ECS template.
 4.  Make sure SecurityHub is enabled in every account in AWS Organizations, and that the SecurityHub integration is enabled as explained in [Prowler - Security Hub Integration](https://github.com/prowler-cloud/prowler#security-hub-integration) 
 5. Deploy **CF-Prowler-CrossAccountRole.yml** in the Master Account as a single stack. You will have to choose the CrossAccountRole name (ProwlerXA-Role by default) and the ProwlerTaskRoleName (ProwlerECSTask-Role by default)
 6. Deploy **CF-Prowler-CrossAccountRole.yml** in every Member Account as a StackSet. Choose the same CrossAccountName and ProwlerTaskRoleName as the previous step.
 7. Deploy **CF-Prowler-IAM.yml** in the account that will host the Prowler container (the same from step 1).  The following template parameters must be provided:
    - **ProwlerCrossAccountRoleName**: Name of the from CF-Prowler-CrossAccountRole (default ProwlerXA-Role).
    - **ECSExecutionRoleName**: Name for the ECS Task Execution Role (default ECSTaskExecution-Role).
    - **ProwlerTaskRoleName**: Name for the ECS Prowler Task Role (default ProwlerECSTask-Role).
    - **ECSEventRoleName**: Name for the Eventbridge Task Role (default ProwlerEvents-Role).
 8. Deploy **CF-Prowler-ECS.yml** in the account that will host the Prowler container (the same from step 1).  The following template parameters must be provided:
	- **ProwlerClusterName**: Name for the ECS Cluster (default ProwlerCluster)
	- **ProwlerContainerName**: Name for the Prowler container (default prowler)
	- **ProwlerContainerInfo**: ECR URI from step 1. 
	- **ProwlerECSLogGroupName**: CloudWatch Log Group name (default /aws/ecs/SecurityHub-Prowler)
	- **SecurityGroupVPCId**: VPC ID for the VPC where the container will run.
	- **ProwlerScheduledSubnet1 and 2**: Subnets IDs from the VPC specified. Choose private subnets if possible.
	- **ECSExecutionRole**: ECS Execution Task Role ARN from CF-Prowler-IAM outputs.
	- **ProwlerTaskRole**: Prowler ECS Task Role ARN from CF-Prowler-IAM outputs.
	- **ECSEventRole**: Eventbridge Task Role ARN from CF-Prowler-IAM outputs.
	- **CronExpression**: Valid Cron Expression for the scheduling of the Task Definition.
 9. Verify that Prowler runs correctly by checking the CloudWatch logs after the scheduled task is executed.

---
## Troubleshooting

If you permission find errors in the CloudWatch logs, the culprit might be a [Service Control Policy (SCP)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html). You will need to exclude the Prowler Cross Account Role from those SCPs.

---
## Upgrading Prowler

Prowler version is controlled by the PROWLERVER argument in the Dockerfile, change it to the desired version and follow the ECR Push Commands to update the container image. 
Old images can be deleted from the ECR Repository after the new image is confirmed to work. They will show as "untagged" as only one image can hold the "latest" tag.
contrib/multi-account-securityhub/Dockerfile METASEP
# Build command
# docker build --platform=linux/amd64  --no-cache -t prowler:latest .

FROM public.ecr.aws/amazonlinux/amazonlinux:2022

ARG PROWLERVER=2.9.0
ARG USERNAME=prowler
ARG USERID=34000

# Install Dependencies
RUN \
    dnf update -y && \
    dnf install -y bash file findutils git jq python3 python3-pip \
                   python3-setuptools python3-wheel shadow-utils tar unzip which && \
    dnf remove -y awscli && \
    dnf clean all && \
    useradd -l -s /bin/sh -U -u ${USERID} ${USERNAME} && \
    curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
    unzip awscliv2.zip && \
    ./aws/install && \
    pip3 install --no-cache-dir --upgrade pip && \
    pip3 install --no-cache-dir "git+https://github.com/ibm/detect-secrets.git@master#egg=detect-secrets" && \
    rm -rf aws awscliv2.zip /var/cache/dnf

# Place script and env vars
COPY .awsvariables run-prowler-securityhub.sh /

# Installs prowler and change permissions
RUN \
    curl -L "https://github.com/prowler-cloud/prowler/archive/refs/tags/${PROWLERVER}.tar.gz" -o "prowler.tar.gz" && \
    tar xvzf prowler.tar.gz && \
    rm -f prowler.tar.gz && \
    mv prowler-${PROWLERVER} prowler && \
    chown ${USERNAME}:${USERNAME} /run-prowler-securityhub.sh && \
    chmod 500 /run-prowler-securityhub.sh && \
    chown ${USERNAME}:${USERNAME} /.awsvariables && \
    chmod 400 /.awsvariables && \
    chown ${USERNAME}:${USERNAME} -R /prowler && \
    chmod +x /prowler/prowler

# Drop to user
USER ${USERNAME}

# Run script
ENTRYPOINT ["/run-prowler-securityhub.sh"]
contrib/multi-account-securityhub/.awsvariables METASEP
export ROLE=ProwlerXA-Role
export PARALLEL_ACCOUNTS=1
export REGION=us-east-1
contrib/k8s/secret.yml METASEP
apiVersion: v1
kind: Secret
metadata:
  name: devsecops-prowler-cronjob-secret
  namespace: defectdojo
type: Opaque
stringData:
  awsId: myAWSSecretID
  awsSecretKey: myAWSSecretKey
  awsS3Bucket: myAWSS3Bucket

contrib/k8s/cronjob.yml METASEP
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: devsecops-prowler-cronjob-secret
  namespace: defectdojo
spec:
#Cron Time is set according to server time, ensure server time zone and set accordingly.
  successfulJobsHistoryLimit: 2
  failedJobsHistoryLimit: 1
  schedule: "5 3 * * 0,2,4"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: prowler
            image: toniblyx/prowler:latest
            imagePullPolicy: Always
            command: 
            - "./prowler"
            args: [ "-g", "hipaa", "-M", "csv,json,html", "-B", "$(awsS3Bucket)" ]
            env:
            - name: AWS_ACCESS_KEY_ID
              valueFrom:
                secretKeyRef:
                  name:  devsecops-prowler-cronjob-secret
                  key: awsId
            - name: AWS_SECRET_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name:  devsecops-prowler-cronjob-secret
                  key: awsSecretKey
            - name: awsS3Bucket 
              valueFrom:
                secretKeyRef:
                  name:  devsecops-prowler-cronjob-secret
                  key: awsS3Bucket 
            imagePullPolicy: IfNotPresent
          restartPolicy: OnFailure
      backoffLimit: 3

contrib/k8s/README.md METASEP
## K8S - Cronjob
Simple instructions to add a cronjob on K8S to execute a prowler and save the results on AWS S3.

### Files: 
cronjob.yml ---> is a **cronjob** for K8S, you must set the frequency and probes from yours scans \
secret.yml -----> is a **secret** file with AWS ID/Secret and the name of bucket 

### To apply:

`$ kubectl -f cronjob.yml` \
`$ kubectl -f secret.yml`

contrib/html/prowler-logo.png METASEP

contrib/html/prowler-logo-new.png METASEP

contrib/html/generate-html-from-csv.sh METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.


## This script helps to generate a single html report from a single or multiple csv 
# output reports.
# I use it when I want to visualize multiple accounts reports in a single view.
# Report information and Assessment Summary will be empty due to the variables 
# that are not set here.

## First: Remove the CSV header from each output report.

## Second: If you want to aggretate all csv files in you can do like this: 
# find . -type f -name '*.csv' -exec cat {} + > prowler-output-unified-csv.file
# use .file instead of .csv unless you want to get into an infinite loop ;)

## Third: Usage ./generate-html-from-csv.sh aggregated-reports-csv.file


OUTPUT_FILE_NAME="report-unified-csv"
EXTENSION_HTML="html"
INPUT=$1
IFS=',' # used inside the while loop for csv delimiter 
HTML_LOGO_URL="https://github.com/prowler-cloud/prowler/"
HTML_LOGO_IMG="https://raw.githubusercontent.com/prowler-cloud/prowler/master/util/html/prowler-logo-new.png"


[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }

addHtmlHeader() {
  if [[ $PROFILE == "" ]];then
    PROFILE="ENV"
  fi
  if [[ -z $HTML_REPORT_INIT ]]; then 
  cat <<EOF
<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <!-- Required meta tags -->
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/jqc-1.12.4/dt-1.10.21/b-1.6.2/sl-1.3.1/datatables.min.css"/>
  <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
  <script type="text/javascript" src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
  <script type="text/javascript" src="https://cdn.datatables.net/v/dt/jqc-1.12.4/dt-1.10.21/b-1.6.2/sl-1.3.1/datatables.min.js"></script>
  <title>Prowler - AWS Security Assesments</title>
</head>
<body>
  <nav class="navbar navbar-expand-xl sticky-top navbar-dark bg-dark">
      <a class="navbar-brand" href="#">Prowler - Security Assesments in AWS</a>
  </nav>
  <div class="container-fluid">
    <div class="row mt-3">
      <div class="col-md-4">
        <div class="card">
          <div class="card-header">
            Report Information
          </div>
          <ul class="list-group list-group-flush">
            <li class="list-group-item">
              <div class="row">
                <div class="col-md-auto">
                  <b>Version:</b> $PROWLER_VERSION
                </div>
              </div>
            </li>
            <li class="list-group-item">
              <b>Parameters used:</b> $PROWLER_PARAMETERS
            </li>
            <li class="list-group-item">
              <b>Date:</b> $TIMESTAMP
            </li>
            <li class="list-group-item">
              <a href="$HTML_LOGO_URL"><img src="$HTML_LOGO_IMG"
                  alt="prowler-logo"></a>
            </li>
          </ul>
        </div>
      </div>
      <div class="col-md-8">
        <div class="card">
          <div class="card-header">
            Assesment Summary
          </div>
          <ul class="list-group list-group-flush">
            <li class="list-group-item">
              <b>AWS Account:</b> $ACCOUNT_NUM
            </li>
            <li class="list-group-item">
              <b>AWS-CLI Profile:</b> $PROFILE
            </li>
            <li class="list-group-item">
              <b>API Region:</b> $REGION
            </li>
            <li class="list-group-item">
              <b>User Id:</b> $USER_ID
            </li>
            <li class="list-group-item">
              <b>Caller Identity ARN:</b> $CALLER_ARN
            </li>
          </ul>
        </div>
        * Sortable columns are CheckID (default) and Result 
      </div>
    </div>
    <div class="row mt-3">
      <div class="col-md-12">
        <table class="table compact stripe row-border ordering" id="findingsTable" data-order='[[ 5, "asc" ]]' data-page-length='100'>
          <thead class="thead-light">
            <tr>
              <th style="align-content:center" scope="col">Status</th>
              <th scope="col">Result</th>
              <th scope="col">Severity</th>
              <th scope="col">AccountID</th>
              <th scope="col">Region</th>
              <th scope="col">Compliance</th>
              <th scope="col">Service</th>
              <th scope="col">CheckID</th>
              <th style="width:40%" scope="col">Check Title</th>
              <th style="width:40%" scope="col">Check Output</th>
            </tr>
          </thead>
          <tbody>
EOF

fi 
}

addHtmlFooter() {
  cat <<EOF

</tbody>
        </table>
      </div>
    </div>
  </div>
  </div>
  <!-- Table search and paginator -->
  <!-- Optional JavaScript -->
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js" integrity="sha384-1CmrxMRARb6aLqgBO7yyAxTOQE2AKb9GfXnEo760AUcUmFx3ibVJJAzGytlQcNXd" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
  <!-- JQuery-->
  <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
  <!-- dataTables-->
  <script src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.min.js"></script>
  <script>
    \$(document).ready(function(){ \$('#findingsTable').dataTable( { "lengthMenu": [ [50, 100, -1], [50, 100, "All"] ], "ordering": true } ); });
  </script>
</body>
</html>
EOF

unset HTML_REPORT_INIT
}

addHtmlHeader > ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
while IFS=, read -r PROFILE ACCOUNT_NUM REPREGION TITLE_ID RESULT SCORED LEVEL TITLE_TEXT NOTES ASFF_COMPLIANCE_TYPE CHECK_SEVERITY CHECK_SERVICENAME;do
  if [[ $RESULT == "INFO" ]]; then 
    echo '<tr class="table-info">' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td><i class="fas fa-info-circle"></i></td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>INFO</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$CHECK_SEVERITY'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$ACCOUNT_NUM'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$REPREGION'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$ASFF_COMPLIANCE_TYPE'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$CHECK_SERVICENAME'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$TITLE_ID'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$TITLE_TEXT'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$NOTES'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
    echo '</tr>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
  fi 
  if [[ $RESULT == "PASS" ]]; then 
    echo '<tr class="p-3 mb-2 bg-success">' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td><i class="fas fa-thumbs-up"></i></td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>PASS</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$CHECK_SEVERITY'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$ACCOUNT_NUM'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$REPREGION'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$ASFF_COMPLIANCE_TYPE'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$CHECK_SERVICENAME'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$TITLE_ID'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$TITLE_TEXT'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$NOTES'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
    echo '</tr>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
  fi 
  if [[ $RESULT == "FAIL" ]]; then 
    echo '<tr class="table-danger" >' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td> <i class="fas fa-thumbs-down"></i></td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>FAIL</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$CHECK_SEVERITY'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$ACCOUNT_NUM'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$REPREGION'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$ASFF_COMPLIANCE_TYPE'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$CHECK_SERVICENAME'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$TITLE_ID'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$TITLE_TEXT'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$NOTES'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
    echo '</tr>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
  fi
  if [[ $RESULT == "WARNING" ]]; then 
    echo '<tr class="table-warning">' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td><i class="fas fa-exclamation-triangle"></i></td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>WARN</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$CHECK_SEVERITY'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$ACCOUNT_NUM'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$REPREGION'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$ASFF_COMPLIANCE_TYPE'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$CHECK_SERVICENAME'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$TITLE_ID'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$TITLE_TEXT'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
      echo '<td>'$NOTES'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
    echo '</tr>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
  fi
done < $INPUT
addHtmlFooter >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML




contrib/codebuild/codebuild-prowler-audit-account-cfn.yaml METASEP
---
AWSTemplateFormatVersion: 2010-09-09
Description: Creates a CodeBuild project to audit an AWS account with Prowler and stores the html report in a S3 bucket. This will run onece at the beginning and on a schedule afterwards. Partial contribution from https://github.com/stevecjones
Parameters:
  ServiceName:
    Description: 'Specifies the service name used within component naming'
    Type: String
    Default: 'prowler'

  LogsRetentionInDays:
    Description: 'Specifies the number of days you want to retain CodeBuild run log events in the specified log group. Junit reports are kept for 30 days, HTML reports in S3 are not deleted'
    Type: Number
    Default: 3
    AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 180, 365]
  
  ProwlerOptions:
    Description: 'Options to pass to Prowler command, make sure at least -M junit-xml is used for CodeBuild reports. Use -r for the region to send API queries, -f to filter only one region, -M output formats, -c for comma separated checks, for all checks do not use -c or -g, for more options see -h. For a complete assessment use  "-M text,junit-xml,html,csv,json", for SecurityHub integration use "-r region -f region -M text,junit-xml,html,csv,json,json-asff -S -q"'
    Type: String
    # Prowler command below runs a set of checks, configure it base on your needs, no options will run all regions all checks.
    # option -M junit-xml is requirede in order to get the report in CodeBuild.
    Default: -r eu-west-1 -f eu-west-1 -M text,junit-xml,html,csv,json -c check11,check12,check13,check14
  
  ProwlerScheduler:
    Description: The time when Prowler will run in cron format. Default is daily at 22:00h or 10PM 'cron(0 22 * * ? *)', for every 5 hours also works 'rate(5 hours)'. More info here https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html.
    Type: String
    Default: 'cron(0 22 * * ? *)'

Resources:
  CodeBuildStartBuild:
    Type: 'Custom::CodeBuildStartBuild'
    DependsOn:
      - CodeBuildLogPolicy
      - CodeBuildStartLogPolicy
    Properties:
      Build: !Ref ProwlerCodeBuild
      ServiceToken: !GetAtt CodeBuildStartBuildLambda.Arn

  CodeBuildStartBuildLambdaRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: !Sub lambda.${AWS::URLSuffix}
            Action: 'sts:AssumeRole'
      Description: !Sub 'DO NOT DELETE - Used by Lambda. Created by CloudFormation Stack ${AWS::StackId}'
      Policies:
        - PolicyName: StartBuildInline
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action: 'codebuild:StartBuild'
                Resource: !GetAtt ProwlerCodeBuild.Arn

  CodeBuildStartBuildLambda:
    Type: 'AWS::Lambda::Function'
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W58
            reason: 'This Lambda has permissions to write Logs'
          - id: W89
            reason: 'VPC is not needed'
          - id: W92
            reason: 'ReservedConcurrentExecutions not needed'
    Properties:
      Handler: index.lambda_handler
      MemorySize: 128
      Role: !Sub ${CodeBuildStartBuildLambdaRole.Arn}
      Timeout: 120
      Runtime: python3.9
      Code:
        ZipFile: |
          import boto3
          import cfnresponse
          from botocore.exceptions import ClientError

          def lambda_handler(event,context):
            props = event['ResourceProperties']
            codebuild_client = boto3.client('codebuild')

            if (event['RequestType'] == 'Create' or event['RequestType'] == 'Update'):
              try:
                  response = codebuild_client.start_build(projectName=props['Build'])
                  print(response)
                  print("Respond: SUCCESS")
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
              except Exception as ex:
                  print(ex.response['Error']['Message'])
                  cfnresponse.send(event, context, cfnresponse.FAILED, ex.response)
            else:
              cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

  CodeBuildStartLogGroup:
    Type: 'AWS::Logs::LogGroup'
    DeletionPolicy: Delete
    UpdateReplacePolicy: Delete
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W84
            reason: 'KMS encryption is not needed.'
    Properties:
      LogGroupName: !Sub '/aws/lambda/${CodeBuildStartBuildLambda}'
      RetentionInDays: !Ref LogsRetentionInDays

  CodeBuildStartLogPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action:
              - logs:CreateLogStream
              - logs:PutLogEvents
            Effect: Allow
            Resource: !GetAtt CodeBuildStartLogGroup.Arn
      PolicyName: LogGroup
      Roles:
        - !Ref CodeBuildStartBuildLambdaRole

  ArtifactBucket:
    Type: AWS::S3::Bucket
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W35
            reason: 'S3 Access Logging is not needed'
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${ServiceName}-${AWS::AccountId}-S3-Prowler-${AWS::StackName}'
      BucketName: !Sub '${ServiceName}-reports-${AWS::Region}-prowler-${AWS::AccountId}'
      AccessControl: LogDeliveryWrite
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

  ArtifactBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref ArtifactBucket
      PolicyDocument:
        Id: Content
        Version: '2012-10-17'
        Statement:
          - Action: '*'
            Condition:
              Bool:
                aws:SecureTransport: false
            Effect: Deny
            Principal: '*'
            Resource: !Sub '${ArtifactBucket.Arn}/*'
            Sid: S3ForceSSL
          - Action: 's3:PutObject'
            Condition:
              'Null':
                s3:x-amz-server-side-encryption: true
            Effect: Deny
            Principal: '*'
            Resource: !Sub '${ArtifactBucket.Arn}/*'
            Sid: DenyUnEncryptedObjectUploads

  CodeBuildServiceRole:
    Type: AWS::IAM::Role
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W11
            reason: 'Role complies with the least privilege principle.'
    Properties:
      Description: !Sub 'DO NOT DELETE - Used by CodeBuild. Created by CloudFormation Stack ${AWS::StackId}'
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/job-function/SupportUser'
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/job-function/ViewOnlyAccess'
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/SecurityAudit'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action: 'sts:AssumeRole'
            Effect: Allow
            Principal:
              Service: !Sub codebuild.${AWS::URLSuffix}
      Policies:
        - PolicyName: S3
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:GetBucketAcl
                  - s3:GetBucketLocation
                Effect: Allow
                Resource: !Sub '${ArtifactBucket.Arn}/*'
        - PolicyName: ProwlerAdditions
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - ds:ListAuthorizedApplications
                  - ec2:GetEbsEncryptionByDefault
                  - ecr:Describe*
                  - elasticfilesystem:DescribeBackupPolicy
                  - glue:GetConnections
                  - glue:GetSecurityConfiguration
                  - glue:SearchTables
                  - lambda:GetFunction
                  - s3:GetAccountPublicAccessBlock
                  - shield:DescribeProtection
                  - shield:GetSubscriptionState
                  - ssm:GetDocument
                  - support:Describe*
                  - tag:GetTagKeys
                Effect: Allow
                Resource: '*'
        - PolicyName: CodeBuild
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - codebuild:CreateReportGroup
                  - codebuild:CreateReport
                  - codebuild:UpdateReport
                  - codebuild:BatchPutTestCases
                  - codebuild:BatchPutCodeCoverages
                Effect: Allow
                Resource: !Sub 'arn:${AWS::Partition}:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/*'
        - PolicyName: SecurityHubBatchImportFindings
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action: securityhub:BatchImportFindings
                Effect: Allow
                Resource: !Sub 'arn:${AWS::Partition}:securityhub:${AWS::Region}::product/prowler/prowler'

  CodeBuildLogPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action:
              - logs:CreateLogStream
              - logs:PutLogEvents
            Effect: Allow
            Resource: !GetAtt ProwlerLogGroup.Arn
      PolicyName: LogGroup
      Roles:
        - !Ref CodeBuildServiceRole

  CodeBuildAssumePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action: 'sts:AssumeRole'
            Effect: Allow
            Resource: !GetAtt CodeBuildServiceRole.Arn
      PolicyName: AssumeRole
      Roles:
        - !Ref CodeBuildServiceRole

  ProwlerCodeBuild:
    Type: AWS::CodeBuild::Project
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W32
            reason: 'KMS encryption is not needed.'
    Properties:
      Artifacts:
        Type: NO_ARTIFACTS
      ConcurrentBuildLimit: 1
      Source:
        GitCloneDepth: 1
        Location: https://github.com/prowler-cloud/prowler
        Type: GITHUB
        ReportBuildStatus: false
        BuildSpec: |
          version: 0.2
          phases:
            install:
              runtime-versions:
                python: 3.9
              commands:
                - echo "Installing Prowler and dependencies..."
                - pip3 install detect-secrets 
            build:
              commands:
                - echo "Running Prowler as ./prowler $PROWLER_OPTIONS"
                - ./prowler $PROWLER_OPTIONS 
            post_build:
              commands:
                - echo "Uploading reports to S3..." 
                - aws s3 cp --sse AES256 output/ s3://$BUCKET_REPORT/ --recursive
                - echo "Done!"
          reports:
            prowler:
              files:
                - '**/*'
              base-directory: 'junit-reports'
              file-format: JunitXml
      Environment:
        # AWS CodeBuild free tier includes 100 build minutes of BUILD_GENERAL1_SMALL per month.
        # BUILD_GENERAL1_SMALL: Use up to 3 GB memory and 2 vCPUs for builds. $0.005/minute.
        # BUILD_GENERAL1_MEDIUM: Use up to 7 GB memory and 4 vCPUs for builds. $0.01/minute.
        # BUILD_GENERAL1_LARGE: Use up to 15 GB memory and 8 vCPUs for builds. $0.02/minute.
        # BUILD_GENERAL1_2XLARGE: Use up to 144 GB memory and 72 vCPUs for builds. $0.20/minute.
        ComputeType: "BUILD_GENERAL1_SMALL"
        Image: "aws/codebuild/amazonlinux2-x86_64-standard:3.0"
        Type: "LINUX_CONTAINER"
        EnvironmentVariables:
          - Name: BUCKET_REPORT
            Value: !Ref ArtifactBucket
            Type: PLAINTEXT
          - Name: PROWLER_OPTIONS
            Value: !Ref ProwlerOptions
            Type: PLAINTEXT
      Description: Run Prowler assessment
      ServiceRole: !GetAtt CodeBuildServiceRole.Arn
      TimeoutInMinutes: 300

  ProwlerLogGroup:
    Type: 'AWS::Logs::LogGroup'
    DeletionPolicy: Delete
    UpdateReplacePolicy: Delete
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W84
            reason: 'KMS encryption is not needed.'
    Properties:
      LogGroupName: !Sub '/aws/codebuild/${ProwlerCodeBuild}'
      RetentionInDays: !Ref LogsRetentionInDays

  EventBridgeServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Description: !Sub 'DO NOT DELETE - Used by EventBridge. Created by CloudFormation Stack ${AWS::StackId}'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action: 'sts:AssumeRole'
            Effect: Allow
            Principal:
              Service: !Sub events.${AWS::URLSuffix}
      Policies:
        - PolicyName: CodeBuild
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action: 'codebuild:StartBuild'
                Resource: !GetAtt ProwlerCodeBuild.Arn

  ProwlerSchedule:
    Type: 'AWS::Events::Rule'
    Properties:
      Description: A schedule to trigger Prowler in CodeBuild
      ScheduleExpression: !Ref ProwlerScheduler
      State: ENABLED
      Targets:
        - Arn: !GetAtt ProwlerCodeBuild.Arn
          Id: ProwlerSchedule
          RoleArn: !GetAtt EventBridgeServiceRole.Arn

Outputs:
  ArtifactBucketName:
    Description: Artifact Bucket Name
    Value: !Ref ArtifactBucket

contrib/cloudshell/shortcut.sh METASEP
#!/bin/bash

# ShortCut - Run Prowler and ScoutSuite in Customer's environment using AWS CloudShell
# DozerCat - Team DragonCat - AWS

# Package Prerequisites
sudo yum update -y
sudo yum install python3 -y
sudo yum install screen -y
sudo yum install zip -y

# Variable and Environment Prerequisites
account=$(aws sts get-caller-identity | jq --raw-output '.Account')
mkdir ${account}-results

# Prowler
cd ~
git clone https://github.com/prowler-cloud/prowler
pip3 install detect-secrets --user
cd prowler 
screen -dmS prowler sh -c "./prowler -M csv,html;cd ~;zip -r ${account}-results/prowler-${account}.zip /home/cloudshell-user/prowler/output"

# ScoutSuite
cd ~
git clone https://github.com/nccgroup/ScoutSuite
cd ScoutSuite
sudo yum install python-pip -y
sudo pip install virtualenv
virtualenv -p python3 venv
source venv/bin/activate
pip install -r requirements.txt
sleep 2
screen -dmS scoutsuite sh -c "python scout.py aws;cd ~;zip -r ${account}-results/scoutsuite-${account}.zip /home/cloudshell-user/ScoutSuite/scoutsuite-report"

# Check on screen sessions
screen -ls

contrib/cloudshell/README.md METASEP
# ShortCut script: run Prowler and ScoutSuite in Customer's environment using AWS CloudShell

### Use Case:

Customers look to use multiple auditing tools in order to provide quick assessments about their AWS environments. These tools allow for reports to be generated for review by the customer and appropriate teams, which in turns helps them begin security remediation efforts. 

Prowler and ScoutSuite are two publicly available security auditing tools that provide comprehensive reports for customers using AWS.

ShortCut is a mechanism for customers to use to run both Prowler and ScoutSuite within an AWS account, using AWS CloudShell. When customers use ShortCut, this allows for customers to quickly perform an audit on their environment, without having to provision IAM Access Keys or EC2 instances. 

### Prerequisites:

Note: The current version of this script is ran in a single account.

In order to use CloudShell, the customer will need the following permissions within their AWS Account:
```
cloudshell:*
```

In addition, the following IAM Policies are needed in order to run ScoutSuite & Prowler:
```
arn:aws:iam::aws:policy/SecurityAudit
arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
```

### Instructions
1. Log into the AWS Console
2. Go to AWS CloudShell. There's a screenshot of the AWS CloudShell icon below, or if you're logged into AWS already, you can click this link: console.aws.amazon.com/cloudshell

![Alt text](screenshots/cloudshell_icon.png)

3. Once the session begins, upload the shortcut.sh file into the AWS CloudShell session by selecting Actions -> Upload File.

![Alt text](screenshots/action_upload_icon.png)

4. Once the file is uploaded, run the following command within your AWS CloudShell session:
```
bash shortcut.sh
```
5. The results for Prowler and ScoutSuite will be located in the following directory:
```
/home/cloudshell-user/<account number>-results
```
6. You can check the status of each screen session by typing the following commands:
```
# Prowler:
screen -r prowler
# ScoutSuite
screen -r scoutsuite
```
7. To download the results from AWS CloudShell, select Actions -> Download File.

![Alt text](screenshots/action_download_icon.png)

8. In the Download File prompt, use the file path and file name to download the results.

![Alt text](screenshots/download_prompt.png)
providers/aws/models.py METASEP
from dataclasses import dataclass
from datetime import datetime

from boto3 import session


@dataclass
class AWS_Credentials:
    aws_access_key_id: str
    aws_session_token: str
    aws_secret_access_key: str
    expiration: datetime


@dataclass
class AWS_Assume_Role:
    role_arn: str
    session_duration: int
    external_id: str


@dataclass
class AWS_Organizations_Info:
    account_details_email: str
    account_details_name: str
    account_details_arn: str
    account_details_org: str
    account_details_tags: str


@dataclass
class AWS_Audit_Info:
    original_session: session.Session
    audit_session: session.Session
    audited_account: int
    audited_identity_arn: str
    audited_user_id: str
    audited_partition: str
    profile: str
    profile_region: str
    credentials: AWS_Credentials
    assumed_role_info: AWS_Assume_Role
    audited_regions: list
    organizations_metadata: AWS_Organizations_Info

providers/aws/aws_provider_test.py METASEP
import json

import boto3
import sure  # noqa
from moto import mock_iam, mock_organizations, mock_sts

from providers.aws.aws_provider import (
    assume_role,
    get_organizations_metadata,
    validate_credentials,
)
from providers.aws.models import AWS_Assume_Role, AWS_Audit_Info

ACCOUNT_ID = 123456789012


class Test_AWS_Provider:
    @mock_sts
    @mock_iam
    def test_validate_credentials(self):
        # Create a mock IAM user
        iam_client = boto3.client("iam", region_name="us-east-1")
        iam_user = iam_client.create_user(UserName="test-user")["User"]
        # Create a mock IAM access keys
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]
        access_key_id = access_key["AccessKeyId"]
        secret_access_key = access_key["SecretAccessKey"]
        # Create AWS session to validate
        session = boto3.session.Session(
            aws_access_key_id=access_key_id,
            aws_secret_access_key=secret_access_key,
            region_name="us-east-1",
        )
        # Validate AWS session
        get_caller_identity = validate_credentials(session)

        get_caller_identity["Arn"].should.equal(iam_user["Arn"])
        get_caller_identity["UserId"].should.equal(iam_user["UserId"])
        # assert get_caller_identity["UserId"] == str(ACCOUNT_ID)

    @mock_iam
    @mock_sts
    def test_assume_role(self):
        # Variables
        role_name = "test-role"
        role_arn = f"arn:aws:iam::{ACCOUNT_ID}:role/{role_name}"
        session_duration_seconds = 900
        audited_regions = "eu-west-1"
        sessionName = "ProwlerProAsessmentSession"
        # Boto 3 client to create our user
        iam_client = boto3.client("iam", region_name="us-east-1")
        # IAM user
        iam_user = iam_client.create_user(UserName="test-user")["User"]
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]
        access_key_id = access_key["AccessKeyId"]
        secret_access_key = access_key["SecretAccessKey"]
        # New Boto3 session with the previously create user
        session = boto3.session.Session(
            aws_access_key_id=access_key_id,
            aws_secret_access_key=secret_access_key,
            region_name="us-east-1",
        )

        # Fulfil the input session object for Prowler
        audit_info = AWS_Audit_Info(
            original_session=session,
            audit_session=None,
            audited_account=None,
            audited_partition=None,
            audited_identity_arn=None,
            audited_user_id=None,
            profile=None,
            profile_region=None,
            credentials=None,
            assumed_role_info=AWS_Assume_Role(
                role_arn=role_arn,
                session_duration=session_duration_seconds,
                external_id=None,
            ),
            audited_regions=audited_regions,
            organizations_metadata=None,
        )

        # Call assume_role
        assume_role_response = assume_role(audit_info)
        # Recover credentials for the assume role operation
        credentials = assume_role_response["Credentials"]
        # Test the response
        ## SessionToken
        credentials["SessionToken"].should.have.length_of(356)
        credentials["SessionToken"].startswith("FQoGZXIvYXdzE")
        ## AccessKeyId
        credentials["AccessKeyId"].should.have.length_of(20)
        credentials["AccessKeyId"].startswith("ASIA")
        ## SecretAccessKey
        credentials["SecretAccessKey"].should.have.length_of(40)
        ## Assumed Role
        assume_role_response["AssumedRoleUser"]["Arn"].should.equal(
            f"arn:aws:sts::{ACCOUNT_ID}:assumed-role/{role_name}/{sessionName}"
        )
        ## AssumedRoleUser
        assert assume_role_response["AssumedRoleUser"]["AssumedRoleId"].startswith(
            "AROA"
        )
        assert assume_role_response["AssumedRoleUser"]["AssumedRoleId"].endswith(
            ":" + sessionName
        )
        assume_role_response["AssumedRoleUser"]["AssumedRoleId"].should.have.length_of(
            21 + 1 + len(sessionName)
        )

    @mock_organizations
    @mock_sts
    @mock_iam
    def test_organizations(self):
        client = boto3.client("organizations", region_name="us-east-1")
        iam_client = boto3.client("iam", region_name="us-east-1")
        sts_client = boto3.client("sts", region_name="us-east-1")

        mockname = "mock-account"
        mockdomain = "moto-example.org"
        mockemail = "@".join([mockname, mockdomain])

        org_id = client.create_organization(FeatureSet="ALL")["Organization"]["Id"]
        account_id = client.create_account(AccountName=mockname, Email=mockemail)[
            "CreateAccountStatus"
        ]["AccountId"]

        client.tag_resource(
            ResourceId=account_id, Tags=[{"Key": "key", "Value": "value"}]
        )

        trust_policy_document = {
            "Version": "2012-10-17",
            "Statement": {
                "Effect": "Allow",
                "Principal": {
                    "AWS": "arn:aws:iam::{account_id}:root".format(
                        account_id=ACCOUNT_ID
                    )
                },
                "Action": "sts:AssumeRole",
            },
        }
        iam_role_arn = iam_client.role_arn = iam_client.create_role(
            RoleName="test-role",
            AssumeRolePolicyDocument=json.dumps(trust_policy_document),
        )["Role"]["Arn"]
        session_name = "new-session"
        assumed_role = sts_client.assume_role(
            RoleArn=iam_role_arn, RoleSessionName=session_name
        )

        org = get_organizations_metadata(account_id, assumed_role)

        org.account_details_email.should.equal(mockemail)
        org.account_details_name.should.equal(mockname)
        org.account_details_arn.should.equal(
            "arn:aws:organizations::{0}:account/{1}/{2}".format(
                ACCOUNT_ID, org_id, account_id
            )
        )
        org.account_details_org.should.equal(org_id)
        org.account_details_tags.should.equal("key:value,")

providers/aws/aws_provider.py METASEP
import sys

from arnparse import arnparse
from boto3 import client, session
from botocore.credentials import RefreshableCredentials
from botocore.session import get_session
from colorama import Fore, Style

from config.config import aws_services_json_file
from lib.logger import logger
from lib.utils.utils import open_file, parse_json_file
from providers.aws.lib.arn.arn import arn_parsing
from providers.aws.models import (
    AWS_Assume_Role,
    AWS_Audit_Info,
    AWS_Credentials,
    AWS_Organizations_Info,
)


################## AWS PROVIDER
class AWS_Provider:
    def __init__(self, audit_info):
        logger.info("Instantiating aws provider ...")
        self.aws_session = self.set_session(audit_info)
        self.role_info = audit_info.assumed_role_info

    def get_session(self):
        return self.aws_session

    def set_session(self, audit_info):
        try:
            if audit_info.credentials:
                # If we receive a credentials object filled is coming form an assumed role, so renewal is needed
                logger.info("Creating session for assumed role ...")
                # From botocore we can use RefreshableCredentials class, which has an attribute (refresh_using)
                # that needs to be a method without arguments that retrieves a new set of fresh credentials
                # asuming the role again. -> https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L395
                assumed_refreshable_credentials = RefreshableCredentials(
                    access_key=audit_info.credentials.aws_access_key_id,
                    secret_key=audit_info.credentials.aws_secret_access_key,
                    token=audit_info.credentials.aws_session_token,
                    expiry_time=audit_info.credentials.expiration,
                    refresh_using=self.refresh,
                    method="sts-assume-role",
                )
                # Here we need the botocore session since it needs to use refreshable credentials
                assumed_botocore_session = get_session()
                assumed_botocore_session._credentials = assumed_refreshable_credentials
                assumed_botocore_session.set_config_variable(
                    "region", audit_info.profile_region
                )

                return session.Session(
                    profile_name=audit_info.profile,
                    botocore_session=assumed_botocore_session,
                )
            # If we do not receive credentials start the session using the profile
            else:
                logger.info("Creating session for not assumed identity ...")
                return session.Session(profile_name=audit_info.profile)
        except Exception as error:
            logger.critical(f"{error.__class__.__name__} -- {error}")
            sys.exit()

    # Refresh credentials method using assume role
    # This method is called "adding ()" to the name, so it cannot accept arguments
    # https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L570
    def refresh(self):
        logger.info("Refreshing assumed credentials...")

        response = assume_role(self.role_info)
        refreshed_credentials = dict(
            # Keys of the dict has to be the same as those that are being searched in the parent class
            # https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L609
            access_key=response["Credentials"]["AccessKeyId"],
            secret_key=response["Credentials"]["SecretAccessKey"],
            token=response["Credentials"]["SessionToken"],
            expiry_time=response["Credentials"]["Expiration"].isoformat(),
        )
        logger.info("Refreshed Credentials:")
        logger.info(refreshed_credentials)
        return refreshed_credentials


def provider_set_session(
    input_profile,
    input_role,
    input_session_duration,
    input_external_id,
    input_regions,
    organizations_role_arn,
):

    # Mark variable that stores all the info about the audit as global
    global current_audit_info

    assumed_session = None

    # Setting session
    current_audit_info = AWS_Audit_Info(
        original_session=None,
        audit_session=None,
        audited_account=None,
        audited_user_id=None,
        audited_partition=None,
        audited_identity_arn=None,
        profile=input_profile,
        profile_region=None,
        credentials=None,
        assumed_role_info=AWS_Assume_Role(
            role_arn=None,
            session_duration=None,
            external_id=None,
        ),
        audited_regions=input_regions,
        organizations_metadata=None,
    )

    logger.info("Generating original session ...")
    # Create an global original session using only profile/basic credentials info
    current_audit_info.original_session = AWS_Provider(current_audit_info).get_session()
    logger.info("Validating credentials ...")
    # Verificate if we have valid credentials
    caller_identity = validate_credentials(current_audit_info.original_session)

    logger.info("Credentials validated")
    logger.info(f"Original caller identity UserId : {caller_identity['UserId']}")
    logger.info(f"Original caller identity ARN : {caller_identity['Arn']}")

    current_audit_info.audited_account = caller_identity["Account"]
    current_audit_info.audited_identity_arn = caller_identity["Arn"]
    current_audit_info.audited_user_id = caller_identity["UserId"]
    current_audit_info.audited_partition = arnparse(caller_identity["Arn"]).partition

    logger.info("Checking if organizations role assumption is needed ...")
    if organizations_role_arn:
        current_audit_info.assumed_role_info.role_arn = organizations_role_arn
        current_audit_info.assumed_role_info.session_duration = input_session_duration

        # Check if role arn is valid
        try:
            # this returns the arn already parsed, calls arnparse, into a dict to be used when it is needed to access its fields
            role_arn_parsed = arn_parsing(current_audit_info.assumed_role_info.role_arn)

        except Exception as error:
            logger.critical(f"{error.__class__.__name__} -- {error}")
            sys.exit()

        else:
            logger.info(
                f"Getting organizations metadata for account {organizations_role_arn}"
            )
            assumed_credentials = assume_role(current_audit_info)
            current_audit_info.organizations_metadata = get_organizations_metadata(
                current_audit_info.audited_account, assumed_credentials
            )
            logger.info(f"Organizations metadata retrieved")

    logger.info("Checking if role assumption is needed ...")
    if input_role:
        current_audit_info.assumed_role_info.role_arn = input_role
        current_audit_info.assumed_role_info.session_duration = input_session_duration
        current_audit_info.assumed_role_info.external_id = input_external_id

        # Check if role arn is valid
        try:
            # this returns the arn already parsed, calls arnparse, into a dict to be used when it is needed to access its fields
            role_arn_parsed = arn_parsing(current_audit_info.assumed_role_info.role_arn)

        except Exception as error:
            logger.critical(f"{error.__class__.__name__} -- {error}")
            sys.exit()

        else:
            logger.info(
                f"Assuming role {current_audit_info.assumed_role_info.role_arn}"
            )
            # Assume the role
            assumed_role_response = assume_role(current_audit_info)
            logger.info("Role assumed")
            # Set the info needed to create a session with an assumed role
            current_audit_info.credentials = AWS_Credentials(
                aws_access_key_id=assumed_role_response["Credentials"]["AccessKeyId"],
                aws_session_token=assumed_role_response["Credentials"]["SessionToken"],
                aws_secret_access_key=assumed_role_response["Credentials"][
                    "SecretAccessKey"
                ],
                expiration=assumed_role_response["Credentials"]["Expiration"],
            )
            assumed_session = AWS_Provider(current_audit_info).get_session()

    if assumed_session:
        logger.info("Audit session is the new session created assuming role")
        current_audit_info.audit_session = assumed_session
        current_audit_info.audited_account = role_arn_parsed.account_id
        current_audit_info.audited_partition = role_arn_parsed.partition
    else:
        logger.info("Audit session is the original one")
        current_audit_info.audit_session = current_audit_info.original_session

    # Setting default region of session
    if current_audit_info.audit_session.region_name:
        current_audit_info.profile_region = current_audit_info.audit_session.region_name
    else:
        current_audit_info.profile_region = "us-east-1"

    print_audit_credentials(current_audit_info)
    return current_audit_info


def print_audit_credentials(audit_info: AWS_Audit_Info):
    # Beautify audited regions, set "all" if there is no filter region
    regions = (
        ", ".join(audit_info.audited_regions)
        if audit_info.audited_regions != None
        else "all"
    )
    # Beautify audited profile, set "default" if there is no profile set
    profile = audit_info.profile if audit_info.profile != None else "default"

    report = f"""
This report is being generated using credentials below:

AWS-CLI Profile: {Fore.YELLOW}[{profile}]{Style.RESET_ALL} AWS API Region: {Fore.YELLOW}[{audit_info.profile_region}]{Style.RESET_ALL} AWS Filter Region: {Fore.YELLOW}[{regions}]{Style.RESET_ALL}
AWS Account: {Fore.YELLOW}[{audit_info.audited_account}]{Style.RESET_ALL} UserId: {Fore.YELLOW}[{audit_info.audited_user_id}]{Style.RESET_ALL}
Caller Identity ARN: {Fore.YELLOW}[{audit_info.audited_identity_arn}]{Style.RESET_ALL}
"""
    # If -A is set, print Assumed Role ARN
    if audit_info.assumed_role_info.role_arn != None:
        report += f"Assumed Role ARN: {Fore.YELLOW}[{audit_info.assumed_role_info.role_arn}]{Style.RESET_ALL}"
    print(report)


def validate_credentials(validate_session: session) -> dict:
    try:
        validate_credentials_client = validate_session.client("sts")
        caller_identity = validate_credentials_client.get_caller_identity()
    except Exception as error:
        logger.critical(f"{error.__class__.__name__} -- {error}")
        sys.exit()
    else:
        return caller_identity


def assume_role(audit_info: AWS_Audit_Info) -> dict:
    try:
        # set the info to assume the role from the partition, account and role name
        sts_client = audit_info.original_session.client("sts")
        # If external id, set it to the assume role api call
        if audit_info.assumed_role_info.external_id:
            assumed_credentials = sts_client.assume_role(
                RoleArn=audit_info.assumed_role_info.role_arn,
                RoleSessionName="ProwlerProAsessmentSession",
                DurationSeconds=audit_info.assumed_role_info.session_duration,
                ExternalId=audit_info.assumed_role_info.external_id,
            )
        # else assume the role without the external id
        else:
            assumed_credentials = sts_client.assume_role(
                RoleArn=audit_info.assumed_role_info.role_arn,
                RoleSessionName="ProwlerProAsessmentSession",
                DurationSeconds=audit_info.assumed_role_info.session_duration,
            )
    except Exception as error:
        logger.critical(f"{error.__class__.__name__} -- {error}")
        sys.exit()

    else:
        return assumed_credentials


def get_organizations_metadata(
    metadata_account: str, assumed_credentials: dict
) -> AWS_Organizations_Info:
    try:
        organizations_client = client(
            "organizations",
            aws_access_key_id=assumed_credentials["Credentials"]["AccessKeyId"],
            aws_secret_access_key=assumed_credentials["Credentials"]["SecretAccessKey"],
            aws_session_token=assumed_credentials["Credentials"]["SessionToken"],
        )
        organizations_metadata = organizations_client.describe_account(
            AccountId=metadata_account
        )
        list_tags_for_resource = organizations_client.list_tags_for_resource(
            ResourceId=metadata_account
        )
    except Exception as error:
        logger.critical(f"{error.__class__.__name__} -- {error}")
        sys.exit()
    else:
        # Convert Tags dictionary to String
        account_details_tags = ""
        for tag in list_tags_for_resource["Tags"]:
            account_details_tags += tag["Key"] + ":" + tag["Value"] + ","
        organizations_info = AWS_Organizations_Info(
            account_details_email=organizations_metadata["Account"]["Email"],
            account_details_name=organizations_metadata["Account"]["Name"],
            account_details_arn=organizations_metadata["Account"]["Arn"],
            account_details_org=organizations_metadata["Account"]["Arn"].split("/")[1],
            account_details_tags=account_details_tags,
        )
        return organizations_info


def generate_regional_clients(service, audit_info):
    regional_clients = {}
    # Get json locally
    f = open_file(aws_services_json_file)
    data = parse_json_file(f)
    json_regions = data["services"][service]["regions"][audit_info.audited_partition]
    if audit_info.audited_regions:  # Check for input aws audit_info.audited_regions
        regions = list(
            set(json_regions).intersection(audit_info.audited_regions)
        )  # Get common regions between input and json
    else:  # Get all regions from json of the service and partition
        regions = json_regions
    for region in regions:
        regional_client = audit_info.audit_session.client(service, region_name=region)
        regional_client.region = region
        regional_clients[region] = regional_client
        # regional_clients.append(regional_client)
    return regional_clients

providers/aws/__init__.py METASEP

contrib/wazuh/prowler-wrapper.py METASEP
#!/usr/bin/env python
#
# Authored by Jeremy Phillips <jeremy@uranusbytes.com>
# Copyright: Apache License 2.0
#
# Wrapper around prowler script to parse results and forward to Wazuh
# Prowler - https://github.com/toniblyx/prowler
#
# TODO: Add ability to disable different groups (EXTRA, etc...
# TODO: Allow to disable individual checks
# TODO: Remove all the commented out stuff
#
# Error Codes:
#   1 - Unknown
#   2 - SIGINT
#   3 - Error output from execution of Prowler
#   4 - Output row is invalid json
#   5 - Wazuh must be running
#   6 - Error sending to socket


import signal
import sys
import socket
import argparse
import subprocess
import json
from datetime import datetime
import os
import re

################################################################################
# Constants
################################################################################
WAZUH_PATH = open('/etc/ossec-init.conf').readline().split('"')[1]
DEBUG_LEVEL = 0  # Enable/disable debug mode
PATH_TO_PROWLER = '{0}/integrations/prowler'.format(WAZUH_PATH)  # No trailing slash
TEMPLATE_CHECK = '''
{{
  "integration": "prowler",
  "prowler": {0}
}}
'''
TEMPLATE_MSG = '1:Wazuh-Prowler:{0}'
TEMPLATE_ERROR = '''{{
  "aws_account_id": {aws_account_id},
  "aws_profile": "{aws_profile}",
  "prowler_error": "{prowler_error}",
  "prowler_version": "{prowler_version}",
  "timestamp": "{timestamp}",
  "status": "Error"
}}
'''
WAZUH_QUEUE = '{0}/queue/ossec/queue'.format(WAZUH_PATH)
FIELD_REMAP = {
  "Profile": "aws_profile",
  "Control": "control",
  "Account Number": "aws_account_id",
  "Level": "level",
  "Account Alias": "aws_account_alias",
  "Timestamp": "timestamp",
  "Region": "region",
  "Control ID": "control_id",
  "Status": "status",
  "Scored": "scored",
  "Message": "message"
}
CHECKS_FILES_TO_IGNORE = [
  'check_sample'
]


################################################################################
# Functions
################################################################################
def _send_msg(msg):
  try:
    _json_msg = json.dumps(_reformat_msg(msg))
    _debug("Sending Msg: {0}".format(_json_msg), 3)
    _socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
    _socket.connect(WAZUH_QUEUE)
    _socket.send(TEMPLATE_MSG.format(_json_msg).encode())
    _socket.close()
  except socket.error as e:
    if e.errno == 111:
      print('ERROR: Wazuh must be running.')
      sys.exit(5)
    else:
      print("ERROR: Error sending message to wazuh: {}".format(e))
      sys.exit(6)
  except Exception as e:
    print("ERROR: Error sending message to wazuh: {}".format(e))
    sys.exit(6)
  return


def _handler(signal, frame):
  print("ERROR: SIGINT received.")
  sys.exit(12)


def _debug(msg, msg_level):
  if DEBUG_LEVEL >= msg_level:
    print('DEBUG-{level}: {debug_msg}'.format(level=msg_level, debug_msg=msg))


def _get_script_arguments():
  _parser = argparse.ArgumentParser(usage="usage: %(prog)s [options]",
                                    description="Wazuh wodle for evaluating AWS security configuration",
                                    formatter_class=argparse.RawTextHelpFormatter)
  _parser.add_argument('-c', '--aws_account_id', dest='aws_account_id',
                       help='AWS Account ID for logs',
                       required=False)
  _parser.add_argument('-d', '--debug', action='store', dest='debug', default=0, help='Enable debug')
  _parser.add_argument('-p', '--aws_profile', dest='aws_profile', help='The name of credential profile to use',
                       default=None)
  _parser.add_argument('-n', '--aws_account_alias', dest='aws_account_alias',
                       help='AWS Account ID Alias', default='')
  _parser.add_argument('-e', '--skip_on_error', action='store_false', dest='skip_on_error',
                       help='If check output is invalid json, error out instead of skipping the check', default=True)
  return _parser.parse_args()


def _run_prowler(prowler_args):
  _debug('Running prowler with args: {0}'.format(prowler_args), 1)
  _prowler_command = '{prowler}/prowler {args}'.format(prowler=PATH_TO_PROWLER, args=prowler_args)
  _debug('Running command: {0}'.format(_prowler_command), 2)
  _process = subprocess.Popen(_prowler_command, stdout=subprocess.PIPE, shell=True)
  _output, _error = _process.communicate()
  _debug('Raw prowler output: {0}'.format(_output), 3)
  _debug('Raw prowler error: {0}'.format(_error), 3)
  if _error is not None:
    _debug('PROWLER ERROR: {0}'.format(_error), 1)
    exit(3)
  return _output


def _get_prowler_version(options):
  _debug('+++ Get Prowler Version', 1)
  # Execute prowler, but only display the version and immediately exit
  return _run_prowler('-p {0} -V'.format(options.aws_profile)).rstrip()


def _get_prowler_results(options, prowler_check):
  _debug('+++ Get Prowler Results - {check}'.format(check=prowler_check), 1)
  # Execute prowler with all checks
  # -b = disable banner
  # -p = credential profile
  # -M = output json

  return _run_prowler('-b -c {check} -p {aws_profile} -M json'.format(check=prowler_check,
                                                                      aws_profile=options.aws_profile))

def _get_prowler_checks():
  _prowler_checks = []
  for _directory_path, _directories, _files in os.walk('{path}/checks'.format(path=PATH_TO_PROWLER)):
    _debug('Checking in : {}'.format(_directory_path), 3)
    for _file in _files:
      if _file in CHECKS_FILES_TO_IGNORE:
        _debug('Ignoring check - {}'.format(_directory_path, _file), 3)
      elif re.match("check\d+", _file):
        _prowler_checks.append(_file)
      elif re.match("check_extra(\d+)", _file):
        _prowler_checks.append(_file[6:])
      else:
        _debug('Unknown check file type- {}'.format(_directory_path, _file), 3)
  return _prowler_checks


def _send_prowler_results(prowler_results, _prowler_version, options):
  _debug('+++ Send Prowler Results', 1)
  for _check_result in prowler_results.splitlines():
    # Empty row
    if len(_check_result) < 1:
      continue
    # Something failed during prowler check
    elif _check_result[:17] == 'An error occurred':
      _debug('ERROR MSG --- {0}'.format(_check_result), 2)
      _temp_msg = TEMPLATE_ERROR.format(
        aws_account_id=options.aws_account_id,
        aws_profile=options.aws_profile,
        prowler_error=_check_result.replace('"', '\"'),
        prowler_version=_prowler_version,
        timestamp=datetime.now().isoformat()
      )
      _error_msg = json.loads(TEMPLATE_CHECK.format(_temp_msg))
      _send_msg(_error_msg)
      continue
    try:
      _debug('RESULT MSG --- {0}'.format(_check_result), 2)
      _check_result = json.loads(TEMPLATE_CHECK.format(_check_result))
    except:
      _debug('INVALID JSON --- {0}'.format(TEMPLATE_CHECK.format(_check_result)), 1)
      if not options.skip_on_error:
        exit(4)
    _check_result['prowler']['prowler_version'] = _prowler_version
    _check_result['prowler']['aws_account_alias'] = options.aws_account_alias
    _send_msg(_check_result)

  return True


def _reformat_msg(msg):
  for field in FIELD_REMAP:
    if field in msg['prowler']:
      msg['prowler'][FIELD_REMAP[field]] = msg['prowler'][field]
      del msg['prowler'][field]
  return msg


# Main
###############################################################################
def main(argv):
  _debug('+++ Begin script', 1)
  # Parse arguments
  _options = _get_script_arguments()

  if int(_options.debug) > 0:
    global DEBUG_LEVEL
    DEBUG_LEVEL = int(_options.debug)
    _debug('+++ Debug mode on - Level: {debug}'.format(debug=_options.debug), 1)

  _prowler_version = _get_prowler_version(_options)
  _prowler_checks = _get_prowler_checks()
  for _check in _prowler_checks:
    _prowler_results = _get_prowler_results(_options, _check)
    _send_prowler_results(_prowler_results, _prowler_version, _options)
  _debug('+++ Finished script', 1)
  return


if __name__ == '__main__':
  try:
    _debug('Args: {args}'.format(args=str(sys.argv)), 2)
    signal.signal(signal.SIGINT, _handler)
    main(sys.argv[1:])
    sys.exit(0)
  except Exception as e:
    print("Unknown error: {}".format(e))
    if DEBUG_LEVEL > 0:
      raise
    sys.exit(1)

lib/check/fixtures/metadata.json METASEP
{
  "Categories": [
    "cat1",
    "cat2"
  ],
  "CheckID": "iam_disable_30_days_credentials",
  "CheckTitle": "Ensure credentials unused for 30 days or greater are disabled",
  "CheckType": "Software and Configuration Checks",
  "Compliance": [
    {
      "Control": [
        "4.4"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level1",
        "level2"
      ],
      "Version": "1.4"
    }
  ],
  "DependsOn": [
    "othercheck1",
    "othercheck2"
  ],
  "Description": "Ensure credentials unused for 30 days or greater are disabled",
  "Notes": "additional information",
  "Provider": "aws",
  "RelatedTo": [
    "othercheck3",
    "othercheck4"
  ],
  "RelatedUrl": "https://serviceofficialsiteorpageforthissubject",
  "Remediation": {
    "Code": {
      "CLI": "cli command or URL to the cli command location.",
      "NativeIaC": "code or URL to the code location.",
      "Other": "cli command or URL to the cli command location.",
      "Terraform": "code or URL to the code location."
    },
    "Recommendation": {
      "Text": "Run sudo yum update and cross your fingers and toes.",
      "Url": "https://myfp.com/recommendations/dangerous_things_and_how_to_fix_them.html"
    }
  },
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "ResourceType": "AwsIamAccessAnalyzer",
  "Risk": "Risk associated.",
  "ServiceName": "iam",
  "Severity": "low",
  "SubServiceName": "accessanalyzer",
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  }
}

lib/check/fixtures/groupsA.json METASEP
{
  "aws": {
    "gdpr": {
      "checks": [
        "check11",
        "check12"
      ],
      "description": "GDPR Readiness"
    },
    "iam": {
      "checks": [
        "iam_disable_30_days_credentials",
        "iam_disable_90_days_credentials"
      ],
      "description": "Identity and Access Management"
    }
  }
}

lib/check/fixtures/checklistA.json METASEP
{
  "aws": [
    "check11",
    "check12",
    "check7777"
  ]
}

util/cloud9-installation.sh METASEP
#!/bin/bash

# Upgrade AWS CLI to v2
sudo yum update -y
sudo yum remove -y awscli
cd /opt || exit
sudo curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
sudo  unzip awscliv2.zip
sudo ./aws/install
# shellcheck disable=SC1090
. ~/.profile # to load the new path for AWS CLI v2
sudo rm -fr /opt/aws/
cd ~/environment/ || exit
# Prepare Prowler 3.0
git clone https://github.com/prowler-cloud/prowler
cd prowler || exit
git checkout prowler-3.0-dev
sudo pip3 install pipenv detect-secrets==1.0.3
pipenv install && pipenv shell

util/checklist_example.json METASEP
{
  "aws": [
    "check11",
    "check12"
  ]
}

util/allowlist_example.txt METASEP
# Each line is a (checkid:item) tuple

# Example: Will not consider a myignoredbucket failures as full failure. (Still printed as a warning)
check26:myignoredbucket

# Note that by default, this searches for the string appearing *anywhere* in the resource name.
# For example:
#   extra718:ci-logs  # Will block bucket "ci-logs" AND ALSO bucket "ci-logs-replica"
#   extra718:logs     # Will block EVERY BUCKET containing the string "logs"

# line starting with # are ignored as comments
# add a line per resource as here:
#<checkid1>:<resource to ignore 1>
#<checkid1>:<resource to ignore 2>
# checkid2
#<checkid2>:<resource to ignore 1>

# REGEXES
# This allowlist works with regexes (ERE, the same style of regex as grep -E and bash's =~ use)
# therefore:
# extra718:[[:alnum:]]+-logs # will ignore all buckets containing the terms ci-logs, qa-logs, etc.

# EXAMPLE: CONTROL TOWER
# When using Control Tower, guardrails prevent access to certain protected resources. The allowlist
# below ensures that warnings instead of errors are reported for the affected resources.
#extra734:aws-controltower-logs-[[:digit:]]+-[[:alpha:]\-]+
#extra734:aws-controltower-s3-access-logs-[[:digit:]]+-[[:alpha:]\-]+
#extra764:aws-controltower-logs-[[:digit:]]+-[[:alpha:]\-]+
#extra764:aws-controltower-s3-access-logs-[[:digit:]]+-[[:alpha:]\-]+
util/Dockerfile METASEP
# Build command
# docker build --platform=linux/amd64  --no-cache -t prowler:latest -f util/Dockerfile .

FROM public.ecr.aws/amazonlinux/amazonlinux:latest

LABEL maintainer="https://github.com/prowler-cloud/prowler"

ARG USERNAME=prowler
ARG USERID=34000

RUN yum install -y shadow-utils && \
  useradd -s /bin/sh -U -u ${USERID} ${USERNAME} && \
  yum install -y python3 bash curl jq coreutils py3-pip which unzip && \
  yum upgrade -y && \
  yum clean all && \
  pip3 install --upgrade pip && \
  pip3 install --no-cache-dir boto3 detect-secrets==1.0.3 && \
  pip3 cache purge && \
  curl https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o awscliv2.zip && \
  unzip -q awscliv2.zip && \
  aws/install && \
  rm -rf aws awscliv2.zip /var/cache/yum && \
  rm /usr/bin/python && \
  ln -s /usr/bin/python3 /usr/bin/python

WORKDIR /prowler

COPY . ./

RUN chown -R prowler .

USER ${USERNAME}

ENTRYPOINT ["./prowler"]

permissions/prowler-security-hub.json METASEP
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "securityhub:BatchImportFindings",
                "securityhub:GetFindings"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

permissions/prowler-additions-policy.json METASEP
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ds:ListAuthorizedApplications",
                "ec2:GetEbsEncryptionByDefault",
                "ecr:Describe*",
                "elasticfilesystem:DescribeBackupPolicy",
                "glue:GetConnections",
                "glue:GetSecurityConfiguration",
                "glue:SearchTables",
                "lambda:GetFunction",
                "s3:GetAccountPublicAccessBlock",
                "shield:DescribeProtection",
                "shield:GetSubscriptionState",
                "ssm:GetDocument",
                "support:Describe*",
                "tag:GetTagKeys"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "AllowMoreReadForProwler"
        }
    ]
}

permissions/create_role_to_assume_cfn.yaml METASEP
AWSTemplateFormatVersion: '2010-09-09'
#
# You can invoke CloudFormation and pass the principal ARN from a command line like this:
# aws cloudformation create-stack \
#  --capabilities CAPABILITY_IAM --capabilities CAPABILITY_NAMED_IAM \
#  --template-body "file://create_role_to_assume_cfn.yaml" \
#  --stack-name "ProwlerExecRole" \
#  --parameters "ParameterKey=AuthorisedARN,ParameterValue=arn:aws:iam::123456789012:root"
#
Description: | 
  This template creates an AWS IAM Role with an inline policy and two AWS managed policies
  attached. It sets the trust policy on that IAM Role to permit a named ARN in another AWS
  account to assume that role. The role name and the ARN of the trusted user can all be passed
  to the CloudFormation stack as parameters. Then you can run Prowler to perform a security
  assessment with a command like:
  ./prowler -A <THIS_ACCOUNT_ID> -R ProwlerExecRole
Parameters:
  AuthorisedARN:
    Description: |
      ARN of user who is authorised to assume the role that is created by this template.
      E.g., arn:aws:iam::123456789012:root
    Type: String
  ProwlerRoleName:
    Description: |
      Name of the IAM role that will have these policies attached. Default: ProwlerExecRole
    Type: String
    Default: 'ProwlerExecRole'

Resources:
  ProwlerExecRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub ${AuthorisedARN}
            Action: 'sts:AssumeRole'
            ## In case MFA is required uncomment lines below and read https://github.com/prowler-cloud/prowler#run-prowler-with-mfa-protected-credentials
            # Condition:
            #   Bool:
            #     'aws:MultiFactorAuthPresent': true
      # This is 12h that is maximum allowed, Minimum is 3600 = 1h
      # to take advantage of this use -T like in './prowler -A <ACCOUNT_ID_TO_ASSUME> -R ProwlerExecRole -T 43200 -M text,html'
      MaxSessionDuration: 43200
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/SecurityAudit'
        - 'arn:aws:iam::aws:policy/job-function/ViewOnlyAccess'
      RoleName: !Sub ${ProwlerRoleName}
      Policies: 
        - PolicyName: ProwlerExecRoleAdditionalViewPrivileges
          PolicyDocument:
            Version : '2012-10-17'
            Statement:
            - Effect: Allow
              Action:
                - 'ds:ListAuthorizedApplications'
                - 'ec2:GetEbsEncryptionByDefault'
                - 'ecr:Describe*'
                - 'elasticfilesystem:DescribeBackupPolicy'
                - 'glue:GetConnections'
                - 'glue:GetSecurityConfiguration'
                - 'glue:SearchTables'
                - 'lambda:GetFunction'
                - 's3:GetAccountPublicAccessBlock'
                - 'shield:DescribeProtection'
                - 'shield:GetSubscriptionState'
                - 'ssm:GetDocument'
                - 'support:Describe*'
                - 'tag:GetTagKeys'
              Resource: '*'

groups/groupN_sample METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[9]='my-custom-group'
GROUP_NUMBER[9]='9.0'
GROUP_TITLE[9]='My Custom Group - [my-custom-group] ****************************'
GROUP_RUN_BY_DEFAULT[9]='N' # run it when execute_all is called
GROUP_CHECKS[9]='checkNN,checkMM'

groups/group9_gdpr METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[9]='gdpr'
GROUP_NUMBER[9]='9.0'
GROUP_TITLE[9]='GDPR Readiness - ONLY AS REFERENCE - [gdpr] ********************'
GROUP_RUN_BY_DEFAULT[9]='N' # run it when execute_all is called
GROUP_CHECKS[9]='extra718,extra725,extra727,check12,check113,check114,extra71,extra731,extra732,extra733,check25,check39,check21,check22,check23,check24,check26,check27,check35,extra726,extra714,extra715,extra717,extra719,extra720,extra721,extra722,check43,check25,extra714,extra729,extra734,extra735,extra736,extra738,extra740,extra761,check11,check110,check111,check112,check116,check120,check122,check13,check14,check15,check16,check17,check18,check19,check28,check29,check31,check310,check311,check312,check313,check314,check32,check33,check34,check36,check37,check38,check41,check42,extra711,extra72,extra723,extra730,extra739,extra76,extra763,extra778,extra78,extra792,extra798'

# Resources:
# https://d1.awsstatic.com/whitepapers/compliance/GDPR_Compliance_on_AWS.pdf
# https://www.slideshare.net/AmazonWebServices/sid303-navigating-gdpr-compliance-on-aws
# https://aws.amazon.com/compliance/gdpr-center/
# https://eur-lex.europa.eu/eli/reg/2016/679/2016-05-04

groups/group8_forensics METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[8]='forensics-ready'
GROUP_NUMBER[8]='8.0'
GROUP_TITLE[8]='Forensics Readiness - [forensics-ready] ************************'
GROUP_RUN_BY_DEFAULT[8]='N' # run it when execute_all is called
GROUP_CHECKS[8]='check21,check22,check23,check24,check25,check26,check27,check29,extra712,extra713,extra714,extra715,extra717,extra718,extra719,extra720,extra721,extra722,extra725,extra7101,extra794'

groups/group7_extras METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[7]='extras'
GROUP_NUMBER[7]='7.0'
GROUP_TITLE[7]='Extras - all non CIS specific checks - [extras] ****************'
GROUP_RUN_BY_DEFAULT[7]='Y' # run it when execute_all is called
GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra767,extra768,extra769,extra770,extra771,extra772,extra773,extra774,extra775,extra776,extra777,extra778,extra779,extra780,extra781,extra782,extra783,extra784,extra785,extra786,extra787,extra788,extra791,extra792,extra793,extra794,extra795,extra796,extra797,extra798,extra799,extra7100,extra7101,extra7102,extra7103,extra7104,extra7105,extra7106,extra7107,extra7108,extra7109,extra7110,extra7111,extra7112,extra7113,extra7114,extra7115,extra7116,extra7117,extra7118,extra7119,extra7120,extra7121,extra7122,extra7123,extra7124,extra7125,extra7126,extra7127,extra7128,extra7129,extra7130,extra7131,extra7132,extra7133,extra7134,extra7135,extra7136,extra7137,extra7138,extra7139,extra7140,extra7141,extra7142,extra7143,extra7144,extra7145,extra7146,extra7147,extra7148,extra7149,extra7150,extra7151,extra7152,extra7153,extra7154,extra7155,extra7156,extra7157,extra7158,extra7159,extra7160,extra7161,extra7162,extra7163,extra7164,extra7165,extra7166,extra7167,extra7168,extra7169,extra7170,extra7171,extra7172,extra7173,extra7174,extra7175,extra7176,extra7177,extra7178,extra7179,extra7180'

# Extras 759 and 760 (lambda variables and code secrets finder are not included)
# to run detect-secrets use `./prowler -g secrets`

# Extras 789 and 790 VPC trust boundaries are not included by default in Extras
# to run trust-boundaries use `./prowler -g trustboundaries`
# read more in https://github.com/toniblyx/prowler/#trust-boundaries-checks

groups/group6_cislevel2 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (c) by Toni de la Fuente
#
# This Prowler check is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/4.0/>.

GROUP_ID[6]='cislevel2'
GROUP_NUMBER[6]='6.0'
GROUP_TITLE[6]='CIS Level 2 - CIS only - [cislevel2] ***************************'
GROUP_RUN_BY_DEFAULT[6]='N' # run it when execute_all is called
GROUP_CHECKS[6]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check111,check112,check113,check114,check115,check116,check117,check118,check119,check120,check121,check122,check21,check22,check23,check24,check25,check26,check27,check28,check29,check31,check32,check33,check34,check35,check36,check37,check38,check39,check310,check311,check312,check313,check314,check41,check42,check43,check44'

groups/group5_cislevel1 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (c) by Toni de la Fuente
#
# This Prowler check is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/4.0/>.

GROUP_ID[5]='cislevel1'
GROUP_NUMBER[5]='5.0'
GROUP_TITLE[5]='CIS Level 1 - CIS only - [cislevel1] ***************************'
GROUP_RUN_BY_DEFAULT[5]='N' # run it when execute_all is called
GROUP_CHECKS[5]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check111,check112,check113,check115,check116,check117,check118,check119,check120,check122,check21,check23,check24,check25,check26,check31,check32,check33,check34,check35,check38,check312,check313,check314,check41,check42'

groups/group4_networking METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (c) by Toni de la Fuente
#
# This Prowler check is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/4.0/>.

GROUP_ID[4]='group4'
GROUP_NUMBER[4]='4.0'
GROUP_TITLE[4]='Networking - CIS only - [group4] *******************************'
GROUP_RUN_BY_DEFAULT[4]='Y' # run it when execute_all is called
GROUP_CHECKS[4]='check41,check42,check43,check44,check45,check46'

groups/group3_monitoring METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (c) by Toni de la Fuente
#
# This Prowler check is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/4.0/>.

GROUP_ID[3]='group3'
GROUP_NUMBER[3]='3.0'
GROUP_TITLE[3]='Monitoring - CIS only - [group3] *******************************'
GROUP_RUN_BY_DEFAULT[3]='Y' # run it when execute_all is called
GROUP_CHECKS[3]='check31,check32,check33,check34,check35,check36,check37,check38,check39,check310,check311,check312,check313,check314'

groups/group2_logging METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (c) by Toni de la Fuente
#
# This Prowler check is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/4.0/>.

GROUP_ID[2]='group2'
GROUP_NUMBER[2]='2.0'
GROUP_TITLE[2]='Logging - CIS only - [group2] **********************************'
GROUP_RUN_BY_DEFAULT[2]='Y' # run it when execute_all is called
GROUP_CHECKS[2]='check21,check22,check23,check24,check25,check26,check27,check28,check29'

groups/group25_FTR METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[25]='ftr'
GROUP_NUMBER[25]='25.0'
GROUP_TITLE[25]='Amazon FTR related security checks - [ftr] ********************'
GROUP_RUN_BY_DEFAULT[25]='N' # run it when execute_all is called
GROUP_CHECKS[25]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check111,check111,check112,check113,check117,check118,check122,check21,check22,extra759,extra760,extra768,extra775,extra797,extra7141,extra73'

# Checks from AWS FTR https://apn-checklists.s3.amazonaws.com/foundational/partner-hosted/partner-hosted/CVLHEC5X7.html
# 1.1 [check11] Avoid the use of the root account - iam [High]
# 1.2 [check12] Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password - iam [High]
# 1.3 [check13] Ensure credentials unused for 90 days or greater are disabled - iam [Medium]
# 1.4 [check14] Ensure access keys are rotated every 90 days or less - iam [Medium]
# 1.5 [check15] Ensure IAM password policy requires at least one uppercase letter - iam [Medium]
# 1.6 [check16] Ensure IAM password policy require at least one lowercase letter - iam [Medium]
# 1.7 [check17] Ensure IAM password policy require at least one symbol - iam [Medium]
# 1.8 [check18] Ensure IAM password policy require at least one number - iam [Medium]
# 1.9 [check19] Ensure IAM password policy requires minimum length of 14 or greater - iam [Medium]
# 1.10 [check110] Ensure IAM password policy prevents password reuse: 24 or greater - iam [Medium]
# 1.11 [check111] Ensure IAM password policy expires passwords within 90 days or less - iam [Medium]
# 1.12 [check112] Ensure no root account access key exists - iam [Critical]
# 1.13 [check113] Ensure MFA is enabled for the root account - iam [Critical]
# 1.17 [check117] Maintain current contact details - support [Medium]
# 1.18 [check118] Ensure security contact information is registered - support [Medium]
# 1.22 [check122] Ensure IAM policies that allow full "*:*" administrative privileges are not created - iam [Medium]
# 2.1 [check21] Ensure CloudTrail is enabled in all regions - cloudtrail [High]
# 2.2 [check22] Ensure CloudTrail log file validation is enabled - cloudtrail [Medium]
# 7.59 [extra759] Find secrets in Lambda functions variables  - lambda [Critical]
# 7.60 [extra760] Find secrets in Lambda functions code  - lambda [Critical]
# 7.68 [extra768] Find secrets in ECS task definitions variables  - ecs [Critical]
# 7.75 [extra775] Find secrets in EC2 Auto Scaling Launch Configuration  - autoscaling [Critical]
# 7.97 [extra797] Ensure Kubernetes Secrets are encrypted using Customer Master Keys (CMKs) - eks [Medium]
# 7.141 [extra7141] Find secrets in SSM Documents - ssm [Critical]
# 7.3 [extra73] Ensure there are no S3 buckets open to Everyone or Any AWS user - s3 [Critical]


groups/group24_glue METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[24]='glue'
GROUP_NUMBER[24]='24.0'
GROUP_TITLE[24]='Amazon Glue related security checks - [glue] ******************'
GROUP_RUN_BY_DEFAULT[24]='N' # run it when execute_all is called
GROUP_CHECKS[24]='extra7114,extra7115,extra7116,extra7117,extra7118,extra7119,extra7120,extra7121,extra7122'
groups/group23_ens METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[23]='ens'
GROUP_NUMBER[23]='23.0'
GROUP_TITLE[23]='ENS Esquema Nacional de Seguridad security checks - [ens] *****'
GROUP_RUN_BY_DEFAULT[23]='N' # run it when execute_all is called
GROUP_CHECKS[23]='extra733,extra7123,check13,check14,check121,extra7100,check120,check116,extra7124,check12,extra7125,check14,check13,check21,check25,extra7127,check35,check24,check31,check36,check32,check33,check34,check22,extra71,check23,check23,check27,check37,extra736,check28,extra713,check21,check29,extra793,extra792,extra764,extra738,check43,extra74,extra710,extra75,check41,check42,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra7128,extra729,extra761,extra740,extra735,extra734,extra728,extra781,extra773,extra744,extra7126,extra7129'

# ENS Control ID for AWS;Prowler checks that apply
# ens-op.acc.1.aws.iam.1;extra733
# ens-op.acc.1.aws.iam.2;extra7123
# ens-op.acc.1.aws.iam.3;check13
# ens-op.acc.1.aws.iam.4;check14
# ens-op.acc.1.aws.iam.5;check121
# ens-op.acc.2.aws.iam.1;extra7100
# ens-op.acc.1.aws.iam.4;check120
# ens-op.acc.3.aws.iam.1;check116
# ens-op.acc.4.aws.sys.1;extra7124
# ens-op.acc.5.aws.iam.1;check12
# ens-op.acc.5.aws.iam.2;extra7125
# ens-op.acc.5.aws.iam.3;check14
# ens-op.acc.5.aws.iam.4;check13
# ens-op.acc.7.aws.iam.1;check21
# ens-op.exp.1.aws.cfg.1;check25
# ens-op.exp.1.aws.sys.1;extra7127
# ens-op.exp.4.aws.sys.1;extra7127
# ens-op.exp.8.aws.trail.1;check35
# ens-op.exp.8.aws.cw.1;check24
# ens-op.exp.8.aws.trail.2;check31
# ens-op.exp.8.aws.trail.3;check36
# ens-op.exp.8.aws.trail.4;check32
# ens-op.exp.8.aws.trail.5;check33
# ens-op.exp.8.aws.trail.6;check34
# ens-op.exp.10.aws.trail.1;check22
# ens-op.exp.10.aws.trail.2;extra71
# ens-op.exp.10.aws.trail.3;check23
# ens-op.exp.10.aws.trail.4;check23
# ens-op.exp.10.aws.trail.5;check27
# ens-op.exp.11.aws.kms.1;check37
# ens-op.exp.11.aws.kms.2;extra736*
# ens-op.exp.11.aws.kms.3;check28
# ens-op.mon.1.aws.duty.1;extra713
# ens-op.mon.1.aws.trail.1;check21
# ens-op.mon.1.aws.flow.1;check29
# ens-mp.com.2.aws.elb.1;extra793
# ens-mp.com.2.aws.elb.2;extra792
# ens-mp.com.2.aws.s3.1;extra764
# ens-mp.com.2.aws.front.1;extra738
# ens-mp.com.4.aws.sg.1;check43
# ens-mp.com.4.aws.sg.2;extra74
# ens-mp.com.4.aws.vpc.1;extra710
# ens-mp.com.4.aws.sg.3;extra75
# ens-mp.com.4.aws.sg.4;check41
# ens-mp.com.4.aws.sg.5;check42
# ens-mp.com.4.aws.sg.6;extra749
# ens-mp.com.4.aws.sg.7;extra750
# ens-mp.com.4.aws.sg.8;extra751
# ens-mp.com.4.aws.sg.9;extra752
# ens-mp.com.4.aws.sg.10;extra753
# ens-mp.com.4.aws.sg.11;extra754
# ens-mp.com.4.aws.sg.12;extra755
# ens-mp.info.3.aws.dyndb.1;extra7128
# ens-mp.info.3.aws.ebs.1;extra729
# ens-mp.info.3.aws.ebs.2;extra761
# ens-mp.info.3.aws.ebs.3;extra740
# ens-mp.info.3.aws.rds.1;extra735
# ens-mp.info.3.s3.1;extra734
# ens-mp.info.3.sns.1;extra728
# ens-mp.info.3.aws.au.1;extra781
# ens-mp.s.2.aws.waf.1;extra773
# ens-mp.s.2.aws.waf.2;extra744
# ens-mp.s.2.aws.waf.3;extra7129

groups/group22_sagemaker METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2222) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[22]='sagemaker'
GROUP_NUMBER[22]='22.0'
GROUP_TITLE[22]='Amazon SageMaker related security checks - [sagemaker] ********'
GROUP_RUN_BY_DEFAULT[22]='N' # run it when execute_all is called
GROUP_CHECKS[22]='extra7103,extra7104,extra7111,extra7112,extra7105,extra7106,extra7107,extra7108,extra7109,extra7110'


groups/group21_soc2 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[21]='soc2'
GROUP_NUMBER[21]='21.0'
GROUP_TITLE[21]='SOC2 Readiness - ONLY AS REFERENCE - [soc2] *******************'
GROUP_RUN_BY_DEFAULT[21]='N' # run it when execute_all is called
GROUP_CHECKS[21]='check110,check111,check113,check12,check122,check13,check15,check16,check17,check18,check19,check21,check31,check310,check32,check33,check34,check35,check36,check37,check38,check39,check41,check42,check43,extra711,extra72,extra723,extra729,extra731,extra734,extra735,extra739,extra76,extra78,extra792'

# References:
# 1. https://www.aicpa.org/content/dam/aicpa/interestareas/frc/assuranceadvisoryservices/downloadabledocuments/trust-services-criteria.pdf
# 2. https://www.aicpa.org/interestareas/frc/assuranceadvisoryservices/mappingsrelevanttothesocsuiteofservices.html
# 3. https://www.aicpa.org/content/dam/aicpa/interestareas/frc/assuranceadvisoryservices/downloadabledocuments/othermapping/mapping-final-2017-tsc-to-extant-2016-tspc.xlsx

groups/group20_ffiec METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[20]='ffiec'
GROUP_NUMBER[20]='20.0'
GROUP_TITLE[20]='FFIEC Cybersecurity Readiness - ONLY AS REFERENCE - [ffiec] ***'
GROUP_RUN_BY_DEFAULT[20]='N' # run it when execute_all is called
GROUP_CHECKS[20]='check11,check12,check13,check14,check16,check18,check19,check21,check23,check25,check29,check29,check31,check32,check33,check34,check35,check36,check37,check37,check38,check39,check41,check42,check43,check110,check112,check113,check116,check310,check311,check312,check313,check314,extra72,extra76,extra78,extra711,extra723,extra729,extra731,extra734,extra735,extra763,extra792'

# References:
# 1. https://www.ffiec.gov/pdf/cybersecurity/FFIEC_CAT_May_2017.pdf
# 2. https://ithandbook.ffiec.gov/media/274793/ffiec_itbooklet_informationsecurity.pdf

groups/group1_iam METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (c) by Toni de la Fuente
#
# This Prowler check is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/4.0/>.

GROUP_ID[1]='group1'
GROUP_NUMBER[1]='1.0'
GROUP_TITLE[1]='Identity and Access Management - CIS only - [group1] ***********'
GROUP_RUN_BY_DEFAULT[1]='Y' # run it when execute_all is called
GROUP_CHECKS[1]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check111,check112,check113,check114,check115,check116,check117,check118,check119,check120,check121,check122'

groups/group19_eks-cis METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (c) by Toni de la Fuente
#
# This Prowler check is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/4.0/>.

GROUP_ID[19]='eks-cis'
GROUP_NUMBER[19]='19.0'
GROUP_TITLE[19]='CIS EKS Benchmark - [eks-cis] *********************************'
GROUP_RUN_BY_DEFAULT[19]='N' # run it when execute_all is called
GROUP_CHECKS[19]='extra765,extra794,extra795,extra796,extra797'

groups/group18_iso27001 METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[18]='iso27001'
GROUP_NUMBER[18]='18.0'
GROUP_TITLE[18]='ISO 27001:2013 Readiness - ONLY AS REFERENCE - [iso27001] *****'
GROUP_RUN_BY_DEFAULT[18]='N' # run it when execute_all is called
GROUP_CHECKS[18]='check11,check110,check111,check112,check113,check114,check115,check116,check119,check12,check122,check13,check14,check15,check16,check17,check18,check19,check21,check22,check23,check24,check25,check26,check27,check28,check29,check31,check310,check311,check312,check313,check314,check32,check33,check34,check35,check36,check37,check38,check39,check41,check42,check43,check44,extra71,extra710,extra7100,extra711,extra7113,extra7123,extra7125,extra7126,extra7128,extra7129,extra713,extra714,extra7130,extra718,extra719,extra72,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra731,extra73,extra731,extra735,extra739,extra74,extra741,extra747,extra748,extra75,extra757,extra758,extra759,extra76,extra760,extra761,extra762,extra763,extra764,extra765,extra767,extra768,extra769,extra77,extra771,extra772,extra774,extra776,extra777,extra778,extra78,extra789,extra79,extra790,extra792,extra793,extra794,extra795,extra796,extra798'

# #	Category	Objective ID	Objective Name	Prowler check ID	Check Summary
# 1		A.9 Access Control	A.9.2	User Access Management	check122	Ensure IAM policies that allow full "*:*" administrative privileges are not created.
# 2		A.9 Access Control	A.9.2	User Access Management	check111	Ensure IAM password policy expires passwords within 90 days or less
# 3		A.9 Access Control	A.9.2	User Access Management	check110	Ensure IAM password policy prevents password reuse
# 4		A.9 Access Control	A.9.2	User Access Management	check19	Ensure IAM password policy requires minimum length of 14 or greater
# 5		A.9 Access Control	A.9.2	User Access Management	check18	Ensure IAM password policy require at least one number
# 6		A.9 Access Control	A.9.2	User Access Management	check17	Ensure IAM password policy require at least one symbol
# 7		A.9 Access Control	A.9.2	User Access Management	check16	Ensure IAM password policy require at least one lowercase letter
# 8		A.9 Access Control	A.9.2	User Access Management	check15	Ensure IAM password policy requires at least one uppercase letter
# 9		A.9 Access Control	A.9.2	User Access Management	check11	Avoid the use of the 'root' account
# 10	A.9 Access Control	A.9.2	User Access Management	check116	Ensure IAM policies are attached only to groups or roles
# 11	A.9 Access Control	A.9.2	User Access Management	check12	Ensure multi-factor authentication (MFA) is enabled for all IAM users that have console access
# 12	A.9 Access Control	A.9.2	User Access Management	check114	Ensure MFA is enabled for the 'root' account
# 13	A.9 Access Control	A.9.2	User Access Management	check115	 Ensure security questions are registered in the AWS account
# 14	A.9 Access Control	A.9.2	User Access Management	check14	Ensure access keys are rotated every 90 days or less
# 15	A.9 Access Control	A.9.2	User Access Management	check13	Ensure credentials unused for 90 days or greater are disabled
# 16	A.9 Access Control	A.9.2	User Access Management	check112	Ensure no root account access key exists
# 17	A.9 Access Control	A.9.2	User Access Management	check119	Ensure IAM instance roles are used for AWS resource access from instances
# 18	A.9 Access Control	A.9.2	User Access Management	extra71	Ensure users of groups with AdministratorAccess policy have MFA tokens enabled
# 19	A.9 Access Control	A.9.2	User Access Management	extra7100	Ensure that no custom policies exist which allow permissive role assumption (e.g. sts:AssumeRole on *)
# 20	A.9 Access Control	A.9.2	User Access Management	extra7123	Check if IAM users have two active access keys
# 21	A.9 Access Control	A.9.2	User Access Management	extra7125	Check if IAM users have Hardware MFA enabled.
# 22	A.9 Access Control	A.9.2	User Access Management	extra769	Check if IAM Access Analyzer is enabled and its findings
# 23	A.9 Access Control	A.9.2	User Access Management	extra774	Ensure credentials unused for 30 days or greater are disabled.
# 24	A.9 Access Control	A.9.3	User Responsibilities	check111	Ensure IAM password policy expires passwords within 90 days or less
# 25	A.9 Access Control	A.9.3	User Responsibilities	check110	Ensure IAM password policy prevents password reuse
# 26	A.9 Access Control	A.9.3	User Responsibilities	check19	Ensure IAM password policy requires minimum length of 14 or greater
# 27	A.9 Access Control	A.9.3	User Responsibilities	check18	Ensure IAM password policy require at least one number
# 28	A.9 Access Control	A.9.3	User Responsibilities	check17	Ensure IAM password policy require at least one symbol
# 29	A.9 Access Control	A.9.3	User Responsibilities	check16	Ensure IAM password policy require at least one lowercase letter
# 30	A.9 Access Control	A.9.3	User Responsibilities	check15	Ensure IAM password policy requires at least one uppercase letter
# 31	A.9 Access Control	A.9.3	User Responsibilities	check12	Ensure multi-factor authentication (MFA) is enabled for all IAM users that have console access
# 32	A.9 Access Control	A.9.3	User Responsibilities	check14	Ensure access keys are rotated every 90 days or less
# 33	A.9 Access Control	A.9.3	User Responsibilities	check13	Ensure credentials unused for 90 days or greater are disabled
# 34	A.9 Access Control	A.9.4	System and Application Access Control	check122	Ensure IAM policies that allow full "*:*" administrative privileges are not created.
# 35	A.9 Access Control	A.9.4	System and Application Access Control	check111	Ensure IAM password policy expires passwords within 90 days or less
# 36	A.9 Access Control	A.9.4	System and Application Access Control	check110	Ensure IAM password policy prevents password reuse
# 37	A.9 Access Control	A.9.4	System and Application Access Control	check19	Ensure IAM password policy requires minimum length of 14 or greater
# 38	A.9 Access Control	A.9.4	System and Application Access Control	check18	Ensure IAM password policy require at least one number
# 39	A.9 Access Control	A.9.4	System and Application Access Control	check17	Ensure IAM password policy require at least one symbol
# 40	A.9 Access Control	A.9.4	System and Application Access Control	check16	Ensure IAM password policy require at least one lowercase letter
# 41	A.9 Access Control	A.9.4	System and Application Access Control	check15	Ensure IAM password policy requires at least one uppercase letter
# 42	A.9 Access Control	A.9.4	System and Application Access Control	check11	Avoid the use of the 'root' account
# 43	A.9 Access Control	A.9.4	System and Application Access Control	check116	Ensure IAM policies are attached only to groups or roles
# 44	A.9 Access Control	A.9.4	System and Application Access Control	check12	Ensure multi-factor authentication (MFA) is enabled for all IAM users that have console access
# 45	A.9 Access Control	A.9.4	System and Application Access Control	check113	Ensure MFA is enabled for the 'root' account
# 46	A.9 Access Control	A.9.4	System and Application Access Control	check14	Ensure access keys are rotated every 90 days or less
# 47	A.9 Access Control	A.9.4	System and Application Access Control	check13	Ensure credentials unused for 90 days or greater are disabled
# 48	A.9 Access Control	A.9.4	System and Application Access Control	check112	Ensure no root account access key exists
# 55    A.9 Access Control	A.9.4	System and Application Access Control	extra711	Check if Redshift cluster is Public Accessible
# 49	A.9 Access Control	A.9.4	System and Application Access Control	extra7113	Check if RDS instances have deletion protection enabled
# 50	A.9 Access Control	A.9.4	System and Application Access Control	extra72	Ensure there are no EBS Snapshots set as Public
# 51	A.9 Access Control	A.9.4	System and Application Access Control	extra723	Check if RDS Snapshots and Cluster Snapshots are public
# 52    A.9 Access Control	A.9.4	System and Application Access Control	extra727	Check if SQS queues have policy set as Public
# 53    A.9 Access Control	A.9.4	System and Application Access Control	extra73	Ensure there are no S3 buckets open to Everyone or Any AWS user
# 54    A.9 Access Control	A.9.4	System and Application Access Control	extra731	Check if SNS topics have policy set as Public
# 56    A.9 Access Control	A.9.4	System and Application Access Control	extra76	Ensure there are no EC2 AMIs set as Public
# 57    A.9 Access Control	A.9.4	System and Application Access Control	extra77 Ensure there are no ECR repositories set as Public
# 58    A.9 Access Control	A.9.4	System and Application Access Control	extra771 Check if S3 buckets have policies which allow WRITE access
# 59    A.9 Access Control	A.9.4	System and Application Access Control	extra795 Ensure EKS Clusters are created with Private Endpoint Enabled and Public Access Disabled
# 60    A.9 Access Control	A.9.4	System and Application Access Control	extra796 Restrict Access to the EKS Control Plane Endpoint
# 61	A.10 Cryptography	A.10.1	Cryptographic Controls	extra735	Setup Encryption at rest for RDS instances
# 62	A.10 Cryptography	A.10.1	Cryptographic Controls	extra792	Check if Elastic Load Balancers have insecure SSL ciphers
# 63	A.10 Cryptography	A.10.1	Cryptographic Controls	check37	Detect Customer Master Keys (CMKs) scheduled for deletion
# 64	A.10 Cryptography	A.10.1	Cryptographic Controls	check27	Ensure CloudTrail logs are encrypted at rest using KMS CMKs
# 65	A.10 Cryptography	A.10.1	Cryptographic Controls	check28	Ensure rotation for customer created KMS CMKs is enabled
# 66	A.10 Cryptography	A.10.1	Cryptographic Controls	extra7126	Check if there are CMK KMS keys not used
# 67	A.10 Cryptography	A.10.1	Cryptographic Controls	extra7128	Check if DynamoDB table has encryption at rest enabled using CMK KMS
# 68	A.10 Cryptography	A.10.1	Cryptographic Controls	extra7130	Ensure there are no SNS Topics unencrypted
# 69	A.10 Cryptography	A.10.1	Cryptographic Controls	extra724	Check if ACM certificates have Certificate Transparency logging enabled
# 70	A.10 Cryptography	A.10.1	Cryptographic Controls	extra728	Check if SQS queues have Server Side Encryption enabled
# 71	A.10 Cryptography	A.10.1	Cryptographic Controls	extra729	Ensure there are no EBS Volumes unencrypted
# 72	A.10 Cryptography	A.10.1	Cryptographic Controls	extra761	Check if EBS Default Encryption is activated
# 73	A.10 Cryptography	A.10.1	Cryptographic Controls	extra764	Check if S3 buckets have secure transport policy
# 74	A.10 Cryptography	A.10.1	Cryptographic Controls	extra767	Check if CloudFront distributions have Field Level Encryption enabled
# 75	A.10 Cryptography	A.10.1	Cryptographic Controls	extra791	Check if CloudFront distributions are using deprecated SSL protocols
# 76	A.10 Cryptography	A.10.1	Cryptographic Controls	extra793	Check if Elastic Load Balancers have SSL listeners
# 77	A.12 Operations Security	A.12.3	Information Backup	extra739	Check if RDS instances have backup enabled
# 78	A.12 Operations Security	A.12.4	Logging and Monitoring	check314	Ensure a log metric filter and alarm exist for VPC changes
# 79	A.12 Operations Security	A.12.4	Logging and Monitoring	check313	Ensure a log metric filter and alarm exist for route table changes
# 80	A.12 Operations Security	A.12.4	Logging and Monitoring	check312	Ensure a log metric filter and alarm exist for changes to network gateways
# 81	A.12 Operations Security	A.12.4	Logging and Monitoring	check311	Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL)
# 82	A.12 Operations Security	A.12.4	Logging and Monitoring	check310	Ensure a log metric filter and alarm exist for security group changes
# 83	A.12 Operations Security	A.12.4	Logging and Monitoring	check39	Ensure a log metric filter and alarm exist for AWS Config configuration changes
# 84	A.12 Operations Security	A.12.4	Logging and Monitoring	check39	Check if CloudFront distributions have logging enabled
# 85	A.12 Operations Security	A.12.4	Logging and Monitoring	extra719	Check if Route53 public hosted zones are logging queries to CloudWatch Logs
# 86	A.12 Operations Security	A.12.4	Logging and Monitoring	extra720	 Check if Lambda functions invoke API operations are being recorded by CloudTrail 
# 87	A.12 Operations Security	A.12.4	Logging and Monitoring	extra722	 Check if API Gateway has logging enabled
# 88	A.12 Operations Security	A.12.4	Logging and Monitoring	check38	Ensure a log metric filter and alarm exist for S3 bucket policy changes
# 89	A.12 Operations Security	A.12.4	Logging and Monitoring	check37	Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs
# 90	A.12 Operations Security	A.12.4	Logging and Monitoring	check36	Ensure a log metric filter and alarm exist for AWS Management Console authentication failures
# 91	A.12 Operations Security	A.12.4	Logging and Monitoring	check35	Ensure a log metric filter and alarm exist for CloudTrail configuration changes
# 92	A.12 Operations Security	A.12.4	Logging and Monitoring	check34	Ensure a log metric filter and alarm exist for IAM policy changes
# 93	A.12 Operations Security	A.12.4	Logging and Monitoring	check33	Ensure a log metric filter and alarm exist for usage of "root" account
# 94	A.12 Operations Security	A.12.4	Logging and Monitoring	check32	Ensure a log metric filter and alarm exist for Management Console sign-in without MFA
# 95	A.12 Operations Security	A.12.4	Logging and Monitoring	check31	Ensure a log metric filter and alarm exist for unauthorized API calls
# 96	A.12 Operations Security	A.12.4	Logging and Monitoring	check26	Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket
# 97	A.12 Operations Security	A.12.4	Logging and Monitoring	check25	Ensure AWS Config is enabled in all regions
# 98	A.12 Operations Security	A.12.4	Logging and Monitoring	check24	Ensure CloudTrail trails are integrated with CloudWatch Logs
# 99	A.12 Operations Security	A.12.4	Logging and Monitoring	check29	Ensure VPC flow logging is enabled in all VPCs
#100 	A.12 Operations Security	A.12.4	Logging and Monitoring	check23	Ensure the S3 bucket CloudTrail logs to is not publicly accessible
#101	A.12 Operations Security	A.12.4	Logging and Monitoring	check21	Ensure CloudTrail is enabled in all regions
#102	A.12 Operations Security	A.12.4	Logging and Monitoring	check21	Ensure CloudTrail is enabled in all regions
#103	A.12 Operations Security	A.12.4	Logging and Monitoring	extra725	Check if S3 buckets have Object-level logging enabled in CloudTrail
#104	A.12 Operations Security	A.12.4	Logging and Monitoring	extra794	Ensure EKS Control Plane Audit Logging is enabled for all log types
#105	A.12 Operations Security	A.12.4	Logging and Monitoring	extra747	Check if RDS instances is integrated with CloudWatch Logs
#106	A.12 Operations Security	A.12.4	Logging and Monitoring	extra718	Check if S3 buckets have server access logging enabled
#107	A.12 Operations Security	A.12.6	Technical Vulnerability Management	check43	Ensure the default security group of every VPC restricts all traffic
#108	A.12 Operations Security	A.12.6	Technical Vulnerability Management	check42	Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389
#109	A.12 Operations Security	A.12.6	Technical Vulnerability Management	check41	Ensure no security groups allow ingress from 0.0.0.0/0 to port 22
#110	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra76	Check for publicly shared AMIs
#111	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra72	Ensure EBS snapshots are not publicly accessible
#112	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra731	Ensure SNS topics do not allow global send or subscribe
#113	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra711	Ensure Redshift clusters do not have a public endpoint
#114	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra723	Ensure RDS snapshots are not publicly accessible
#115	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra78	Ensure RDS instances are not accessible to the world.
#116	A.12 Operations Security	A.12.6	Technical Vulnerability Management	check23	Ensure the S3 bucket CloudTrail logs to is not publicly accessible
#117	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra713	Check if GuardDuty is enabled
#118	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra726	Check Trusted Advisor for errors and warnings
#119	A.12 Operations Security	A.12.6	Technical Vulnerability Management	extra776	Check if ECR image scan found vulnerabilities in the newest image version 
#120	A.13 Communications Security	A.13.1	Network Security Management	check43	Ensure the default security group of every VPC restricts all traffic
#121	A.13 Communications Security	A.13.1	Network Security Management	check42	Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389
#122	A.13 Communications Security	A.13.1	Network Security Management	check41	Ensure no security groups allow ingress from 0.0.0.0/0 to port 22
#123	A.13 Communications Security	A.13.1	Network Security Management	extra72	Ensure EBS snapshots are not publicly accessible
#124	A.13 Communications Security	A.13.1	Network Security Management	extra731	Ensure SNS topics do not allow global send or subscribe
#125	A.13 Communications Security	A.13.1	Network Security Management	extra711	Ensure Redshift clusters do not have a public endpoint
#126	A.13 Communications Security	A.13.1	Network Security Management	extra723	Ensure RDS snapshots are not publicly accessible
#127	A.13 Communications Security	A.13.1	Network Security Management	extra78	Ensure RDS instances are not accessible to the world.
#128	A.13 Communications Security	A.13.1	Network Security Management	extra798	Ensure Lambda Functions are not publicly accessible
#129	A.13 Communications Security	A.13.1	Network Security Management	check44	Ensure routing tables for VPC peering are \"least access\"
#130	A.13 Communications Security	A.13.1	Network Security Management	extra710	Check for internet facing EC2 Instances
#131	A.13 Communications Security	A.13.1	Network Security Management	extra711	Check for Publicly Accessible Redshift Clusters
#132	A.13 Communications Security	A.13.1	Network Security Management	extra748	Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to any port
#133	A.13 Communications Security	A.13.1	Network Security Management	extra7129	Check if Application Load Balancer has a WAF ACL attached
#134	A.13 Communications Security	A.13.1	Network Security Management	extra74	Ensure there are no Security Groups without ingress filtering being used
#135	A.13 Communications Security	A.13.1	Network Security Management	extra777	Find VPC security groups with many ingress or egress rules
#136	A.13 Communications Security	A.13.1	Network Security Management	extra778	Find VPC security groups with wide-open public IPv4 CIDR ranges (non-RFC1918)
#137	A.13 Communications Security	A.13.1	Network Security Management	extra789	Find trust boundaries in VPC endpoint services connections
#138	A.13 Communications Security	A.13.1	Network Security Management	extra79	Check for internet facing Elastic Load Balancers
#139	A.13 Communications Security	A.13.1	Network Security Management	extra790	Find trust boundaries in VPC endpoint services allowlisted principles
#140	A.13 Communications Security	A.13.1	Network Security Management	extra78	Ensure there are no Public Accessible RDS instances
#141    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra731 Check if SNS topics have policy set as Public
#142    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra741 Find secrets in EC2 User Data
#143    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra75 Ensure there are no Security Groups not being used
#144    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra757 Check EC2 Instances older than 6 months
#145    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra758 Check EC2 Instances older than 12 months
#146    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra759 Find secrets in Lambda functions variables
#147    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra760 Find secrets in Lambda functions code
#148    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra762 Find obsolete Lambda runtimes
#149    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra765 Check if ECR image scan on push is enabled
#150    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra768 Find secrets in ECS task definitions variables
#151    A.14 System acquisition, dev & maintenance A.14.2 Security in Dev & Support	extra772 Check if elastic IPs are unused
#152    A.18 Compliance	A.18.1	Compliance with Legal and Regulatory Reqs	check22 Ensure CloudTrail log file validation is enabled
#153    A.18 Compliance	A.18.1	Compliance with Legal and Regulatory Reqs	extra721 Check if Redshift cluster has audit logging enabled
#154    A.18 Compliance	A.18.1	Compliance with Legal and Regulatory Reqs	extra763 Check if S3 buckets have object versioning enabled

groups/group17_internetexposed METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[17]='internet-exposed'
GROUP_NUMBER[17]='17.0'
GROUP_TITLE[17]='Find resources exposed to the internet - [internet-exposed] ***'
GROUP_RUN_BY_DEFAULT[17]='N' # run it when execute_all is called
GROUP_CHECKS[17]='check41,check42,check45,check46,extra72,extra73,extra74,extra76,extra77,extra78,extra79,extra710,extra711,extra716,extra723,extra727,extra731,extra736,extra738,extra745,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra770,extra771,extra778,extra779,extra787,extra788,extra795,extra796,extra798,extra7102,extra7134,extra7135,extra7136,extra7137,extra7138'

# 4.1 [check41] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 22 (Scored)  [group4, cislevel1, cislevel2]
# 4.2 [check42] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 3389 (Scored)  [group4, cislevel1, cislevel2]
# 7.2 [extra72] Ensure there are no EBS Snapshots set as Public    [extras, forensics-ready, gdpr, hipaa, apigateway, rds]
# 7.3 [extra73] Ensure there are no S3 buckets open to the Everyone or Any AWS user    [extras, gdpr, hipaa, rds]
# 7.4 [extra74] Ensure there are no Security Groups without ingress filtering being used    [extras, gdpr, hipaa, secrets, apigateway, rds]
# 7.6 [extra76] Ensure there are no EC2 AMIs set as Public    [extras, gdpr, secrets]
# 7.7 [extra77] Ensure there are no ECR repositories set as Public    [group1, extras, secrets, elasticsearch]
# 7.8 [extra78] Ensure there are no Public Accessible RDS instances    [extras, rds, elasticsearch, trustboundaries]
# 7.9 [extra79] Check for internet facing Elastic Load Balancers    [extras, trustboundaries]
# 7.10 [extra710] Check for internet facing EC2 Instances    [extras]
# 7.11 [extra711] Check for Publicly Accessible Redshift Clusters    [extras]
# 7.16 [extra716] Check if Amazon Elasticsearch Service (ES) domains are set as Public or if it has open policy access   [extras, elasticsearch]
# 7.23 [extra723] Check if RDS Snapshots and Cluster Snapshots are public    [extras, rds]
# 7.27 [extra727] Check if SQS queues have policy set as Public    [extras, gdpr]
# 7.31 [extra731] Check if SNS topics have policy set as Public    [extras, gdpr]
# 7.38 [extra738] Check if CloudFront distributions are set to HTTPS    [extras, gdpr]
# 7.45 [extra745] Check if API Gateway endpoint is public or private    [extras, apigateway]
# 7.48 [extra748] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to any port    [extras]
# 7.49 [extra749] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Oracle ports 1521 or 2483    [extras]
# 7.50 [extra750] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MySQL port 3306    [extras]
# 7.51 [extra751] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Postgres port 5432    [extras]
# 7.52 [extra752] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Redis port 6379    [extras]
# 7.53 [extra753] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MongoDB ports 27017 and 27018    [extras]
# 7.54 [extra754] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Cassandra ports 7199 or 9160 or 8888    [extras]
# 7.55 [extra755] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Memcached port 11211    [extras]
# 7.70 [extra770] Check for internet facing EC2 instances with Instance Profiles attached    [extras]
# 7.78 [extra778] Find VPC security groups with wide-open public IPv4 CIDR ranges (non-RFC1918)    [extras]
# 7.79 [extra779] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Elasticsearch/Kibana ports   [extras, elasticsearch]
# 7.87 [extra787] Check connection and authentication for Internet exposed Elasticsearch/Kibana ports   [extras, elasticsearch]
# 7.88 [extra788] Check connection and authentication for Internet exposed Amazon Elasticsearch Service (ES) domains   [extras, elasticsearch]
# 7.71 [extra771] Check if S3 buckets have policies which allow WRITE access    [extras]

groups/group16_trustboundaries METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[16]='trustboundaries'
GROUP_NUMBER[16]='16.0'
GROUP_TITLE[16]='Find cross-account trust boundaries - [trustboundaries] *******'
GROUP_RUN_BY_DEFAULT[16]='N' # run it when execute_all is called
GROUP_CHECKS[16]='extra789,extra790'

# Single account environment: No action required. The AWS account number will be automatically added by the checks.
# Multi account environment: Any additional trusted account number should be added as a space separated list, e.g.
# GROUP_TRUSTBOUNDARIES_TRUSTED_ACCOUNT_IDS="1234567890 0987654321 6789012345"
GROUP_TRUSTBOUNDARIES_TRUSTED_ACCOUNT_IDS=''

groups/group15_pci METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[15]='pci'
GROUP_NUMBER[15]='15.0'
GROUP_TITLE[15]='PCI-DSS v3.2.1 Readiness - ONLY AS REFERENCE - [pci] **********'
GROUP_RUN_BY_DEFAULT[15]='N' # run it when execute_all is called
GROUP_CHECKS[15]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check112,check113,check114,check116,check21,check23,check25,check26,check27,check28,check29,check314,check36,check38,check43,extra711,extra713,extra717,extra718,extra72,extra729,extra735,extra738,extra740,extra744,extra748,extra75,extra750,extra751,extra753,extra754,extra755,extra773,extra78,extra780,extra781,extra782,extra783,extra784,extra785,extra787,extra788,extra798'

# Resources:
# https://github.com/toniblyx/prowler/issues/296

# List of checks based on PCI v3.2.1
# 3.1 Requirement: Install and Maintain a Firewall Configuration to Protect Cardholder Data

#     Ensure no security groups allow ingress from 0.0.0.0/0 to all ports and protocols extra748
#     Ensure no security groups allow ingress from 0.0.0.0/0 to RDP (TCP 3389) check42
#     Ensure no security groups allow ingress from 0.0.0.0/0 to SSH (TCP 22) check41
#     Ensure no security groups allow ingress from 0.0.0.0/0 to Oracle (TCP 1521) extra749
#     Ensure no security groups allow ingress from 0.0.0.0/0 to Oracle (TCP and UDP 2483) extra749
#     Ensure no security groups allow ingress from 0.0.0.0/0 to Oracle (UDP 2483) extra749
#     Ensure no security groups allow ingress from 0.0.0.0/0 to MySQL (TCP 3306) extra750
#     Ensure no security groups allow ingress from 0.0.0.0/0 to Postgres (TCP 5432) extra751
#     Ensure no security groups allow ingress from 0.0.0.0/0 to Redis (TCP 6379)extra752
#     Ensure no security groups allow ingress from 0.0.0.0/0 to MongoDB (TCP 27017 and 27018) extra753
#     Ensure no security groups allow ingress from 0.0.0.0/0 to Cassandra (TCP 7199, 9160 and 8888) extra754
#     Ensure no security groups allow ingress from 0.0.0.0/0 to Memcached (TCP and UDP 11211) extra755
#     Ensure no security groups allow ingress from 0.0.0.0/0 to Elasticsearch ports (TCP 9200/9300/5601) extra779
#     Ensure the default security group restricts all traffic check43
#     Remove unused security groups extra75
#     RDS should not have Public interface open to a public scope extra78
#     Check for Publicly Accessible Redshift Clusters extra711
#     Ensure Lambda Functions are not publicly accessible   extra798

# 3.2 Requirement 2: Do Not Use Vendor-Supplied Defaults for System Passwords and Other Security Parameters

#     Instance with administrative service: SSH (TCP:22) is exposed to the public Internet check41
#     Instance with administrative service: RDP (TCP:3389) is exposed to the public Internet check42
#     Instance with unencrypted Redis: (TCP:6379) is exposed to the public Internet extra752

# 3.3 Requirement 3: Protect Stored Cardholder Data

#     S3 Buckets Server Side encryption at rest extra734
#     Ensure ECS Cluster At-rest encryption TODO
#     Ensure DynamoDB -Server Side Encryption DONE (default behavior)
#     Check if RDS instances storage is encrypted extra735
#     Ensure there are no EBS Volumes unencrypted extra729
#     Ensure CloudTrail logs are encrypted at rest using KMS CMKs check27
#     Ensure rotation for customer created CMKs is enabled check28
#     Check if EBS snapshots are encrypted extra740
#     Ensure there are no EBS Snapshots set as Public extra72
#     Ensure there is no Fargate containers in use (/aws/containers-roadmap/issues/314) TODO

# 3.4 Requirement 4: Encrypt Transmission of Cardholder Data Across Open, Public Networks

#     Use encrypted connections between CloudFront and origin server extra738 and TODO
#     Ensure that S3 Buckets only allow data transfer using SSL/TLS extra734
#     ELB is setup with SSL for secure communications TODO
#     Ensure the access keys are rotated every 90 days or less check14
#     Network Load Balancer with unencrypted service: ElasticSearch (TCP:9200) is exposed to the public Internet TODO? ELB on SSL should be enough extra779 and extra716
#     Network Load Balancer with unencrypted service: ElasticSearch (TCP:9300) is exposed to the public Internet TODO? ELB on SSL should be enough extra779 and extra716
#     Network Load Balancer with unencrypted service: LDAP (UDP:389) is exposed to the public Internet TODO? ELB on SSL should be enough
#     Network Load Balancer with unencrypted service: LDAP (TCP:389) is exposed to the public Internet TODO? ELB on SSL should be enough
#     Instance with unencrypted service: ElasticSearch (TCP:9200) is exposed to the public Internet extra779 and extra716
#     Instance with unencrypted service: ElasticSearch (TCP:9300) is exposed to the public Internet extra779 and extra716
#     Instance with unencrypted service: LDAP (UDP:389) is exposed to the public Internet
#     Instance with unencrypted service: LDAP (TCP:389) is exposed to the public Internet
#     Instance with unencrypted Redis: (TCP:6379) is exposed to the public Internet

# 3.5. Requirement 5: Protect All Systems Against Malware and Regularly Update Anti-Virus Software or Programs

# N/A
# 3.6. Requirement 6: Develop and Maintain Secure Systems and Applications

#     Ensure Inspector has Assessment Targets TODO
#     Ensure Inspector has a Scheduled Assessment Template TODO
#     Check for WAF IPSet TODO
#     Check for WAF Constraint Sets TODO
#     Check for WAF Web ACL extra744,extra773

# 3.7. Requirement 7: Restrict Access to Cardholder Data By Business Need To Know

#     Credentials (access keys) unused for 90 days or more should be disabled check13
#     Credentials (password enabled) unused for 90 days or more should be disabled check111
#     Ensure IAM policies are attached only to groups or roles check116

# 3.8. Requirement 8: Identify and Authenticate Access to System Components

#     Enforce password policy: IAM (check15, check16, check17, check18, check19, check110, check111)
#     Ensure that MFA is enabled for root account check113
#     Ensure MFA is enabled for all IAM users that have a console password check12
#     Ensure no root account access key exist check112
#     Ensure hardware MFA is enabled for the root account check114
#     Avoid the use of root account. check11

# 3.9 Requirement 9: Restrict Physical Access to Cardholder Data

# N/A
# 3.10. Requirement 10: Track and Monitor All Access to Network Resources and Cardholder Data

#     Ensure CloudTrail is enabled in all regions check21
#     Ensure VPC Flow Logging is enabled in all the applicable Regions check29
#     Ensure S3 Buckets access logging is enabled on the CloudTrail S3 bucket check26
#     ELB is created with access logs enabled extra717
#     S3 bucket should have server access logging enabled extra718
#     Ensure AWS Config in all regions check25
#     S3 bucket CloudTrail logs should not have public accessible check23
#     S3 buckets should not be world-listable extra73
#     S3 buckets should not be world-writable extra73
#     S3 buckets should not be world-readable extra73
#     Ensure a log metric filter and alarm exist for S3 bucket policy changes check38
#     Ensure a log metric exist for AWS Management console authentication failures check36
#     Ensure a log metric exist for VPC changes check314

# 3.11. Requirement 11: Regularly Test Security Systems and Processes

#     Ensure AWS GuarDduty is enabled extra713
#     Ensure Inspector has Assessment Targets
#     Ensure Inspector has a Scheduled Assessment Template

# Requirement 12: Maintain a Policy That Addresses Information Security For All Personnel

# N/A

groups/group14_elasticsearch METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[14]='elasticsearch'
GROUP_NUMBER[14]='14.0'
GROUP_TITLE[14]='Elasticsearch related security checks - [elasticsearch] *******'
GROUP_RUN_BY_DEFAULT[14]='N' # run it when execute_all is called
GROUP_CHECKS[14]='extra715,extra716,extra779,extra780,extra781,extra782,extra783,extra784,extra785,extra787,extra788,extra7101'
groups/group13_rds METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[13]='rds'
GROUP_NUMBER[13]='13.0'
GROUP_TITLE[13]='RDS security checks - [rds] ***********************************'
GROUP_RUN_BY_DEFAULT[13]='N' # run it when execute_all is called
GROUP_CHECKS[13]='extra78,extra723,extra735,extra739,extra747,extra7113,extra7131,extra7132,extra7133'

groups/group12_apigateway METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[12]='apigateway'
GROUP_NUMBER[12]='12.0'
GROUP_TITLE[12]='API Gateway security checks - [apigateway] ********************'
GROUP_RUN_BY_DEFAULT[12]='N' # run it when execute_all is called
GROUP_CHECKS[12]='extra722,extra743,extra744,extra745,extra746'


groups/group11_secrets METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[11]='secrets'
GROUP_NUMBER[11]='11.0'
GROUP_TITLE[11]='Look for keys secrets or passwords around resources - [secrets]'
GROUP_RUN_BY_DEFAULT[11]='N' # but it runs when execute_all is called (default)
GROUP_CHECKS[11]='extra741,extra742,extra759,extra760,extra768,extra775,extra7141'

# requires https://github.com/Yelp/detect-secrets
# `pip install detect-secrets` 


groups/group10_hipaa METASEP
#!/usr/bin/env bash

# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

GROUP_ID[10]='hipaa'
GROUP_NUMBER[10]='10.0'
GROUP_TITLE[10]='HIPAA Compliance - ONLY AS REFERENCE - [hipaa] ****************'
GROUP_RUN_BY_DEFAULT[10]='N' # run it when execute_all is called
GROUP_CHECKS[10]='check12,check113,check23,check26,check27,check29,extra718,extra725,extra72,extra75,extra717,extra729,extra734,check38,extra73,extra740,extra735,check112,check13,check15,check16,check17,check18,check19,check21,check24,check28,check31,check310,check311,check312,check313,check314,check32,check33,check34,check35,check36,check37,check39,extra792'

# Resources:
# https://d0.awsstatic.com/whitepapers/compliance/AWS_HIPAA_Compliance_Whitepaper.pdf
# https://www.slideshare.net/AmazonWebServices/wps301navigating-hipaa-and-hitrustquickstart-guide-to-account-gov-stratpdf

util/update_aws_services_regions.py METASEP
import json
import logging
import os
import sys
from urllib import request

aws_services_json_url = (
    "https://api.regional-table.region-services.aws.a2z.com/index.json"
)

# Logging config
logging.basicConfig(
    stream=sys.stdout,
    format="%(asctime)s [File: %(filename)s:%(lineno)d] \t[Module: %(module)s]\t %(levelname)s: %(message)s",
    datefmt="%m/%d/%Y %I:%M:%S %p",
    level=logging.INFO,
)

# JSON files
with request.urlopen(aws_services_json_url) as url:  # Get the AWS regions matrix online
    logging.info(f"Downloading JSON from {aws_services_json_url}")
    original_matrix_regions_aws = json.loads(url.read().decode())
parsed__matrix_regions_aws = f"{os.path.dirname(os.path.realpath(__name__))}/providers/aws/aws_regions_by_service.json"

# JSON objects
regions_by_service = {}
services = {}
old_service = ""

logging.info("Recovering AWS Regions by Service")
# Iterating through the json list
for item in original_matrix_regions_aws["prices"]:
    service = item["id"].split(":")[0]
    region = item["id"].split(":")[1]
    # Init regions for the new service
    if service != old_service:
        regions_dict = {}
        # Store the service
        services[service] = regions_dict
        # Init objects for every new service
        old_service = service
        regions = {}
        regions["aws"] = {}
        regions["cn"] = {}
        regions["gov"] = {}
        regions_dict["regions"] = {}
        regions_aws = []
        regions_cn = []
        regions_gov = []

    # Include the region in their AWS partition
    if "cn-" in region:
        regions_cn.append(region)
        regions["cn"] = regions_cn

    elif "gov-" in region:
        regions_gov.append(region)
        regions["gov"] = regions_gov
    else:
        regions_aws.append(region)
        regions["aws"] = regions_aws

    regions_dict["regions"] = regions

# Store final JSON
logging.info(f"Storing final JSON")
regions_by_service["services"] = services

# Write to file
logging.info(f"Writing {parsed__matrix_regions_aws}")
with open(parsed__matrix_regions_aws, "w") as outfile:
    json.dump(regions_by_service, outfile, indent=2, sort_keys=True)

config/config.py METASEP
from datetime import datetime, timezone
from os import getcwd

timestamp = datetime.today()
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc)
prowler_version = "3.0-beta-08Aug2022"

# Groups
groups_file = "groups.json"

# AWS services-regions matrix json
aws_services_json_file = "providers/aws/aws_regions_by_service.json"

default_output_directory = getcwd() + "/output"

output_file_timestamp = timestamp.strftime("%Y%m%d%H%M%S")
csv_file_suffix = f"{output_file_timestamp}.csv"
json_file_suffix = f"{output_file_timestamp}.json"
json_asff_file_suffix = f"{output_file_timestamp}.asff.json"

config/__init__.py METASEP

prowler METASEP
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import sys
from os import mkdir
from os.path import isdir

from config.config import default_output_directory
from lib.banner import print_banner, print_version
from lib.check.check import (
    bulk_load_checks_metadata,
    exclude_checks_to_run,
    exclude_groups_to_run,
    exclude_services_to_run,
    import_check,
    list_groups,
    list_services,
    print_checks,
    print_services,
    run_check,
    set_output_options,
)
from lib.check.checks_loader import load_checks_to_execute
from lib.logger import logger, set_logging_config
from lib.outputs.outputs import close_json
from providers.aws.aws_provider import provider_set_session
from providers.aws.lib.security_hub import resolve_security_hub_previous_findings

if __name__ == "__main__":
    # CLI Arguments
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "provider",
        choices=["aws"],
        nargs="?",
        default="aws",
        help="Specify Cloud Provider",
    )

    # Arguments to set checks to run
    # The following arguments needs to be set exclusivelly
    group = parser.add_mutually_exclusive_group()
    group.add_argument("-c", "--checks", nargs="+", help="List of checks")
    group.add_argument("-C", "--checks-file", nargs="?", help="List of checks")
    group.add_argument("-s", "--services", nargs="+", help="List of services")
    group.add_argument("-g", "--groups", nargs="+", help="List of groups")
    group.add_argument(
        "--severity",
        nargs="+",
        help="List of severities [informational, low, medium, high, critical]",
        choices=["informational", "low", "medium", "high", "critical"],
    )
    # Exclude checks options
    parser.add_argument("-e", "--excluded-checks", nargs="+", help="Checks to exclude")
    parser.add_argument("-E", "--excluded-groups", nargs="+", help="Groups to exclude")
    parser.add_argument("--excluded-services", nargs="+", help="Services to exclude")
    # List checks options
    list_group = parser.add_mutually_exclusive_group()
    list_group.add_argument(
        "-L", "--list-groups", action="store_true", help="List groups"
    )
    list_group.add_argument(
        "-l", "--list-checks", action="store_true", help="List checks"
    )
    list_group.add_argument(
        "--list-services", action="store_true", help="List services"
    )

    parser.add_argument(
        "-b", "--no-banner", action="store_false", help="Hide Prowler banner"
    )
    parser.add_argument(
        "-V", "-v", "--version", action="store_true", help="Show Prowler version"
    )
    parser.add_argument(
        "-q", "--quiet", action="store_true", help="Show only Prowler failed findings"
    )

    # Both options can be combined to only report to file some log level
    parser.add_argument(
        "--log-level",
        choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
        default="CRITICAL",
        help="Select Log Level",
    )
    parser.add_argument(
        "--log-file",
        nargs="?",
        help="Set log file name",
    )

    parser.add_argument(
        "-p",
        "--profile",
        nargs="?",
        default=None,
        help="AWS profile to launch prowler with",
    )
    parser.add_argument(
        "-R",
        "--role",
        nargs="?",
        default=None,
        help="ARN of the role to be assumed",
    )
    parser.add_argument(
        "-T",
        "--session-duration",
        nargs="?",
        default=3600,
        type=int,
        help="Assumed role session duration in seconds, by default 3600",
    )
    parser.add_argument(
        "-I",
        "--external-id",
        nargs="?",
        default=None,
        help="External ID to be passed when assuming role",
    )
    parser.add_argument(
        "-f",
        "--filter-region",
        nargs="+",
        help="AWS region names to run Prowler against",
    )
    parser.add_argument(
        "-M",
        "--output-modes",
        nargs="+",
        help="Output mode, by default csv",
        choices=["csv", "json", "json-asff"],
    )
    parser.add_argument(
        "-o",
        "--custom-output-directory",
        nargs="?",
        help="Custom output directory, by default the folder where Prowler is stored",
        default=default_output_directory,
    )
    parser.add_argument(
        "-O",
        "--organizations-role",
        nargs="?",
        help="Specify AWS Organizations management role ARN to be assumed, to get Organization metadata",
    )
    parser.add_argument(
        "-S",
        "--security-hub",
        action="store_true",
        help="Send check output to AWS Security Hub",
    )
    # Parse Arguments
    args = parser.parse_args()

    provider = args.provider
    checks = args.checks
    excluded_checks = args.excluded_checks
    excluded_groups = args.excluded_groups
    excluded_services = args.excluded_services
    services = args.services
    groups = args.groups
    checks_file = args.checks_file
    output_directory = args.custom_output_directory
    severities = args.severity
    output_modes = args.output_modes

    # Set Logger configuration
    set_logging_config(args.log_file, args.log_level)

    # Role assumption input options tests
    if args.session_duration not in range(900, 43200):
        logger.critical("Value for -T option must be between 900 and 43200")
        sys.exit()
    if args.session_duration != 3600 or args.external_id:
        if not args.role:
            logger.critical("To use -I/-T options -R option is needed")
            sys.exit()

    if args.version:
        print_version()
        sys.exit()

    if args.no_banner:
        print_banner()

    if args.list_groups:
        list_groups(provider)
        sys.exit()

    if args.list_services:
        print_services(list_services(provider))
        sys.exit()

    # Load checks metadata
    logger.debug("Loading checks metadata from .metadata.json files")
    bulk_checks_metadata = bulk_load_checks_metadata(provider)

    # Load checks to execute
    checks_to_execute = load_checks_to_execute(
        bulk_checks_metadata,
        checks_file,
        checks,
        services,
        groups,
        severities,
        provider,
    )
    # Exclude checks if -e/--excluded-checks
    if excluded_checks:
        checks_to_execute = exclude_checks_to_run(checks_to_execute, excluded_checks)

    # Exclude groups if -g/--excluded-groups
    if excluded_groups:
        checks_to_execute = exclude_groups_to_run(
            checks_to_execute, excluded_groups, provider
        )

    # Exclude services if -s/--excluded-services
    if excluded_services:
        checks_to_execute = exclude_services_to_run(
            checks_to_execute, excluded_services, provider
        )

    # Sort final check list
    checks_to_execute = sorted(checks_to_execute)

    # If -l/--list-checks passed as argument, print checks to execute and quit
    if args.list_checks:
        print_checks(provider, checks_to_execute, bulk_checks_metadata)
        sys.exit()

    # If security hub sending enabled, it is need to create json-asff output
    if args.security_hub:
        if not output_modes:
            output_modes = ["json-asff"]
        else:
            output_modes.append("json-asff")

    # Setting output options
    audit_output_options = set_output_options(
        args.quiet, output_modes, output_directory, args.security_hub
    )

    # Check output directory, if it is default and not created -> create it
    # If is custom and not created -> error
    if output_directory:
        if not isdir(output_directory):
            if output_directory == default_output_directory:
                if output_modes:
                    mkdir(default_output_directory)
            else:
                logger.critical("Output directory does not exist")
                sys.exit()

    # Set global session
    audit_info = provider_set_session(
        args.profile,
        args.role,
        args.session_duration,
        args.external_id,
        args.filter_region,
        args.organizations_role,
    )

    # Execute checks
    if len(checks_to_execute):
        for check_name in checks_to_execute:
            # Recover service from check name
            service = check_name.split("_")[0]
            try:
                # Import check module
                check_module_path = (
                    f"providers.{provider}.services.{service}.{check_name}.{check_name}"
                )
                lib = import_check(check_module_path)
                # Recover functions from check
                check_to_execute = getattr(lib, check_name)
                c = check_to_execute()
                # Run check
                run_check(c, audit_info, audit_output_options)

            # If check does not exists in the provider or is from another provider
            except ModuleNotFoundError:
                logger.error(
                    f"Check '{check_name}' was not found for the {provider.upper()} provider"
                )
    else:
        logger.error(
            "There are no checks to execute. Please, check your input arguments"
        )

    # Close json file if exists
    if output_modes:
        for mode in output_modes:
            if mode == "json" or mode == "json-asff":
                close_json(output_directory, audit_info.audited_account, mode)

    # Resolve previous fails of Security Hub
    if args.security_hub:
        resolve_security_hub_previous_findings(output_directory, audit_info)

groups.json METASEP
{
  "aws": {
    "gdpr": {
      "checks": [
        "s3_bucket_server_access_logging_enabled",
        "s3_bucket_object_versioning",
        "iam_avoid_root_usage",
        "iam_user_mfa_enabled_console_access",
        "iam_disable_90_days_credentials",
        "iam_rotate_access_key_90_days",
        "iam_root_mfa_enabled",
        "iam_root_hardware_mfa_enabled",
        "iam_no_root_access_key",
        "iam_administrator_access_with_mfa",
        "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389",
        "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
        "ec2_ebs_snapshots_encrypted",
        "ec2_ebs_public_snapshot"
      ],
      "description": "GDPR Readiness"
    },
    "pci": {
      "checks": [
        "iam_avoid_root_usage",
        "iam_user_mfa_enabled_console_access",
        "iam_disable_90_days_credentials",
        "iam_rotate_access_key_90_days",
        "iam_root_mfa_enabled",
        "iam_root_hardware_mfa_enabled",
        "iam_no_root_access_key",
        "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
        "ec2_securitygroup_allow_ingress_from_internet_to_any_port",
        "ec2_ebs_snapshots_encrypted",
        "ec2_ebs_public_snapshot",
        "s3_bucket_server_access_logging_enabled"
      ],
      "description": "PCI-DSS v3.2.1 Readiness"
    }
  }
}

check_sample.metadata.json METASEP
{
  "Provider": "aws",
  "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
  "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to SSH port 22.",
  "CheckType": "Data Protection",
  "ServiceName": "ec2",
  "SubServiceName": "securitygroup",
  "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
  "Severity": "low",
  "ResourceType": "AwsEc2SecurityGroup",
  "Description": "Extended Description",
  "Risk": "If Security groups are not properly configured the attack surface is increased.",
  "RelatedUrl": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html",
  "Remediation": {
    "Code": {
      "CLI": "cli command or URL to the cli command location.",
      "NativeIaC": "code or URL to the code location.",
      "Other": "cli command or URL to the cli command location.",
      "Terraform": "code or URL to the code location."
    },
    "Recommendation": {
      "Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
      "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-best-practices.html"
    }
  },
  "Categories": [
    "cat1",
    "cat2"
  ],
  "Tags": {
    "Tag1Key": "value",
    "Tag2Key": "value"
  },
  "DependsOn": [
    "othercheck1",
    "othercheck2"
  ],
  "RelatedTo": [
    "othercheck3",
    "othercheck4"
  ],
  "Notes": "additional information",
  "Compliance": [
    {
      "Control": [
        "4.1"
      ],
      "Framework": "CIS-AWS",
      "Group": [
        "level2"
      ],
      "Version": "1.4"
    }
  ]
}

README.md METASEP
<p align="center">
  <img align="center" src="docs/images/prowler-pro-dark.png#gh-dark-mode-only" width="150" height="36">
  <img align="center" src="docs/images/prowler-pro-light.png#gh-light-mode-only" width="15%" height="15%">
</p>
<p align="center">
  <b><i>&nbsp&nbsp&nbspExplore the Pro version of Prowler at <a href="https://prowler.pro">prowler.pro</a></i></b>
</p>
<hr>
<p align="center">
  <img src="https://user-images.githubusercontent.com/3985464/113734260-7ba06900-96fb-11eb-82bc-d4f68a1e2710.png" />
</p>
<p align="center">
  <a href="https://discord.gg/UjSMCVnxSB"><img alt="Discord Shield" src="https://img.shields.io/discord/807208614288818196"></a>
  <a href="https://hub.docker.com/r/toniblyx/prowler"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/toniblyx/prowler"></a>
  <a href="https://hub.docker.com/r/toniblyx/prowler"><img alt="Docker" src="https://img.shields.io/docker/cloud/build/toniblyx/prowler"></a>
  <a href="https://hub.docker.com/r/toniblyx/prowler"><img alt="Docker" src="https://img.shields.io/docker/image-size/toniblyx/prowler"></a>
  <a href="https://gallery.ecr.aws/o4g1s5r6/prowler"><img width="120" height=19" alt="AWS ECR Gallery" src="https://user-images.githubusercontent.com/3985464/151531396-b6535a68-c907-44eb-95a1-a09508178616.png"></a>
  <a href="https://github.com/prowler-cloud/prowler"><img alt="Repo size" src="https://img.shields.io/github/repo-size/prowler-cloud/prowler"></a>
  <a href="https://github.com/prowler-cloud/prowler"><img alt="Lines" src="https://img.shields.io/tokei/lines/github/prowler-cloud/prowler"></a>
  <a href="https://github.com/prowler-cloud/prowler/issues"><img alt="Issues" src="https://img.shields.io/github/issues/prowler-cloud/prowler"></a>
  <a href="https://github.com/prowler-cloud/prowler/releases"><img alt="Version" src="https://img.shields.io/github/v/release/prowler-cloud/prowler?include_prereleases"></a>
  <a href="https://github.com/prowler-cloud/prowler/releases"><img alt="Version" src="https://img.shields.io/github/release-date/prowler-cloud/prowler"></a>
  <a href="https://github.com/prowler-cloud/prowler"><img alt="Contributors" src="https://img.shields.io/github/contributors-anon/prowler-cloud/prowler"></a>
  <a href="https://github.com/prowler-cloud/prowler"><img alt="License" src="https://img.shields.io/github/license/prowler-cloud/prowler"></a>
  <a href="https://twitter.com/ToniBlyx"><img alt="Twitter" src="https://img.shields.io/twitter/follow/toniblyx?style=social"></a>
</p>

<p align="center">
  <i>Prowler</i> is an Open Source security tool to perform AWS security best practices assessments, audits, incident response, continuous monitoring, hardening and forensics readiness. It contains more than 200 controls covering CIS, PCI-DSS, ISO27001, GDPR, HIPAA, FFIEC, SOC2, AWS FTR, ENS and custome security frameworks.
</p>

## Table of Contents

- [Description](#description)
- [Features](#features)
- [High level architecture](#high-level-architecture)
- [Requirements and Installation](#requirements-and-installation)
- [Usage](#usage)
- [Screenshots](#screenshots)
- [Advanced Usage](#advanced-usage)
- [Security Hub integration](#security-hub-integration)
- [CodeBuild deployment](#codebuild-deployment)
- [Allowlist](#allowlist-or-remove-a-fail-from-resources)
- [Fix](#how-to-fix-every-fail)
- [Troubleshooting](#troubleshooting)
- [Extras](#extras)
- [Forensics Ready Checks](#forensics-ready-checks)
- [GDPR Checks](#gdpr-checks)
- [HIPAA Checks](#hipaa-checks)
- [Trust Boundaries Checks](#trust-boundaries-checks)
- [Multi Account and Continuous Monitoring](util/org-multi-account/README.md)
- [Custom Checks](#custom-checks)
- [Third Party Integrations](#third-party-integrations)
- [Full list of checks and groups](/LIST_OF_CHECKS_AND_GROUPS.md)
- [License](#license)

## Description

Prowler is a command line tool that helps you with AWS security assessment, auditing, hardening and incident response.

It follows guidelines of the CIS Amazon Web Services Foundations Benchmark (49 checks) and has more than 100 additional checks including related to GDPR, HIPAA, PCI-DSS, ISO-27001, FFIEC, SOC2 and others.

Read more about [CIS Amazon Web Services Foundations Benchmark v1.2.0 - 05-23-2018](https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf)

## Features

+200 checks covering security best practices across all AWS regions and most of AWS services and related to the next groups:

- Identity and Access Management [group1]
- Logging  [group2]
- Monitoring [group3]
- Networking [group4]
- CIS Level 1 [cislevel1]
- CIS Level 2 [cislevel2]
- Extras *see Extras section* [extras]
- Forensics related group of checks [forensics-ready]
- GDPR [gdpr] Read more [here](#gdpr-checks)
- HIPAA [hipaa] Read more [here](#hipaa-checks)
- Trust Boundaries [trustboundaries] Read more [here](#trust-boundaries-checks)
- Secrets
- Internet exposed resources
- EKS-CIS
- Also includes PCI-DSS, ISO-27001, FFIEC, SOC2, ENS (Esquema Nacional de Seguridad of Spain).
- AWS FTR [FTR] Read more [here](#aws-ftr-checks)

With Prowler you can:

- Get a direct colorful or monochrome report
- A HTML, CSV, JUNIT, JSON or JSON ASFF (Security Hub) format report
- Send findings directly to Security Hub
- Run specific checks and groups or create your own
- Check multiple AWS accounts in parallel or sequentially
- And more! Read examples below

## High level architecture

You can run Prowler from your workstation, an EC2 instance, Fargate or any other container, Codebuild, CloudShell and Cloud9.

![Prowler high level architecture](https://user-images.githubusercontent.com/3985464/109143232-1488af80-7760-11eb-8d83-726790fda592.jpg)
## Requirements and Installation

Prowler has been written in bash using AWS-CLI underneath and it works in Linux, Mac OS or Windows with cygwin or virtualization. Also requires `jq` and `detect-secrets` to work properly.

- Make sure the latest version of AWS-CLI is installed. It works with either v1 or v2, however _latest v2 is recommended if using new regions since they require STS v2 token_, and other components needed, with Python pip already installed.

- For Amazon Linux (`yum` based Linux distributions and AWS CLI v2):
    ```
    sudo yum update -y
    sudo yum remove -y awscli
    curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
    unzip awscliv2.zip
    sudo ./aws/install
    sudo yum install -y python3 jq git
    sudo pip3 install detect-secrets==1.0.3
    git clone https://github.com/prowler-cloud/prowler
    ```
- For Ubuntu Linux (`apt` based Linux distributions and AWS CLI v2):
    ```
    sudo apt update
    sudo apt install python3 python3-pip jq git zip
    pip install detect-secrets==1.0.3
    curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
    unzip awscliv2.zip
    sudo ./aws/install
    git clone https://github.com/prowler-cloud/prowler
    ```

    > NOTE: detect-secrets Yelp version is no longer supported, the one from IBM is mantained now. Use the one mentioned below or the specific Yelp version 1.0.3 to make sure it works as expected (`pip install detect-secrets==1.0.3`):
    ```sh
    pip install "git+https://github.com/ibm/detect-secrets.git@master#egg=detect-secrets"
    ```

    AWS-CLI can be also installed it using other methods, refer to official documentation for more details: <https://aws.amazon.com/cli/>, but `detect-secrets` has to be installed using `pip` or `pip3`.

- Once Prowler repository is cloned, get into the folder and you can run it:

    ```sh
    cd prowler
    ./prowler
    ```

- Since Prowler users AWS CLI under the hood, you can follow any authentication method as described [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-precedence). Make sure you have properly configured your AWS-CLI with a valid Access Key and Region or declare AWS variables properly (or instance profile/role):

    ```sh
    aws configure
    ```

    or

    ```sh
    export AWS_ACCESS_KEY_ID="ASXXXXXXX"
    export AWS_SECRET_ACCESS_KEY="XXXXXXXXX"
    export AWS_SESSION_TOKEN="XXXXXXXXX"
    ```

- Those credentials must be associated to a user or role with proper permissions to do all checks. To make sure, add the AWS managed policies, SecurityAudit and ViewOnlyAccess, to the user or role being used.  Policy ARNs are:

    ```sh
    arn:aws:iam::aws:policy/SecurityAudit
    arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
    ```

    > Additional permissions needed: to make sure Prowler can scan all services included in the group *Extras*, make sure you attach also the custom policy [prowler-additions-policy.json](https://github.com/prowler-cloud/prowler/blob/master/iam/prowler-additions-policy.json) to the role you are using. If you want Prowler to send findings to [AWS Security Hub](https://aws.amazon.com/security-hub), make sure you also attach the custom policy [prowler-security-hub.json](https://github.com/prowler-cloud/prowler/blob/master/iam/prowler-security-hub.json).

## Usage

1. Run the `prowler` command without options (it will use your environment variable credentials if they exist or will default to using the `~/.aws/credentials` file and run checks over all regions when needed. The default region is us-east-1):

    ```sh
    ./prowler
    ```

    Use `-l` to list all available checks and the groups (sections) that reference them. To list all groups use `-L` and to list content of a group use `-l -g <groupname>`.

    If you want to avoid installing dependencies run it using Docker:

    ```sh
    docker run -ti --rm --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest
    ```

    In case you want to get reports created by Prowler use docker volume option like in the example below:
    ```sh
    docker run -ti --rm -v /your/local/output:/prowler/output --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest -g hipaa -M csv,json,html
    ```

1. For custom AWS-CLI profile and region, use the following: (it will use your custom profile and run checks over all regions when needed):

    ```sh
    ./prowler -p custom-profile -r us-east-1
    ```

1. For a single check use option `-c`:

    ```sh
    ./prowler -c check310
    ```

    With Docker:

    ```sh
    docker run -ti --rm --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest "-c check310"
    ```

    or multiple checks separated by comma:

    ```sh
    ./prowler -c check310,check722
    ```

    or all checks but some of them:

    ```sh
    ./prowler -e check42,check43
    ```

    or for custom profile and region:

    ```sh
    ./prowler -p custom-profile -r us-east-1 -c check11
    ```

    or for a group of checks use group name:

    ```sh
    ./prowler -g group1 # for iam related checks
    ```

    or exclude some checks in the group:

    ```sh
    ./prowler -g group4 -e check42,check43
    ```

    Valid check numbers are based on the AWS CIS Benchmark guide, so 1.1 is check11 and 3.10 is check310

### Regions

By default, Prowler scans all opt-in regions available, that might take a long execution time depending on the number of resources and regions used. Same applies for GovCloud or China regions. See below Advance usage for examples.

Prowler has two parameters related to regions: `-r` that is used query AWS services API endpoints (it uses `us-east-1` by default and required for GovCloud or China) and the option `-f` that is to filter those regions you only want to scan. For example if you want to scan Dublin only use `-f eu-west-1` and if you want to scan Dublin and Ohio `-f eu-west-1,us-east-1`, note the regions are separated by a comma deliminator (it can be used as before with `-f 'eu-west-1,us-east-1'`).

## Screenshots

- Sample screenshot of default console report first lines of command `./prowler`:

    <img width="900" src="https://user-images.githubusercontent.com/3985464/141444529-84640bed-be0b-4112-80a2-2a43e3ebf53f.png">

- Sample screenshot of the html output `-M html`:

    <img width="900" alt="Prowler html" src="https://user-images.githubusercontent.com/3985464/141443976-41d32cc2-533d-405a-92cb-affc3995d6ec.png">

- Sample screenshot of the Quicksight dashboard, see [quicksight-security-dashboard.workshop.aws](https://quicksight-security-dashboard.workshop.aws/):

    <img width="900" alt="Prowler with Quicksight" src="https://user-images.githubusercontent.com/3985464/128932819-0156e838-286d-483c-b953-fda68a325a3d.png">

- Sample screenshot of the junit-xml output in CodeBuild `-M junit-xml`:

    <img width="900" src="https://user-images.githubusercontent.com/3985464/113942824-ca382b00-9801-11eb-84e5-d7731548a7a9.png">

### Save your reports

1. If you want to save your report for later analysis thare are different ways, natively (supported text, mono, csv, json, json-asff, junit-xml and html, see note below for more info):

    ```sh
    ./prowler -M csv
    ```

    or with multiple formats at the same time:

    ```sh
    ./prowler -M csv,json,json-asff,html
    ```

    or just a group of checks in multiple formats:

    ```sh
    ./prowler -g gdpr -M csv,json,json-asff
    ```

    or if you want a sorted and dynamic HTML report do:

    ```sh
    ./prowler -M html
    ```

    Now `-M` creates a file inside the prowler `output` directory named `prowler-output-AWSACCOUNTID-YYYYMMDDHHMMSS.format`. You don't have to specify anything else, no pipes, no redirects.

    or just saving the output to a file like below:

    ```sh
    ./prowler -M mono > prowler-report.txt
    ```

    To generate JUnit report files, include the junit-xml format. This can be combined with any other format. Files are written inside a prowler root directory named `junit-reports`:

    ```sh
    ./prowler -M text,junit-xml
    ```

    >Note about output formats to use with `-M`: "text" is the default one with colors, "mono" is like default one but monochrome, "csv" is comma separated values, "json" plain basic json (without comma between lines) and "json-asff" is also json with Amazon Security Finding Format that you can ship to Security Hub using `-S`.

    To save your report in an S3 bucket, use `-B` to define a custom output bucket along with `-M` to define the output format that is going to be uploaded to S3:

    ```sh
    ./prowler -M csv -B my-bucket/folder/
    ```
    >In the case you do not want to use the assumed role credentials but the initial credentials to put the reports into the S3 bucket, use `-D` instead of `-B`. Make sure that the used credentials have s3:PutObject permissions in the S3 path where the reports are going to be uploaded.

    When generating multiple formats and running using Docker, to retrieve the reports, bind a local directory to the container, e.g.:

    ```sh
    docker run -ti --rm --name prowler --volume "$(pwd)":/prowler/output --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest -M csv,json
    ```

1. To perform an assessment based on CIS Profile Definitions you can use cislevel1 or cislevel2 with `-g` flag, more information about this [here, page 8](https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf):

    ```sh
    ./prowler -g cislevel1
    ```

1. If you want to run Prowler to check multiple AWS accounts in parallel (runs up to 4 simultaneously `-P 4`) but you may want to read below in Advanced Usage section to do so assuming a role:

    ```sh
    grep -E '^\[([0-9A-Aa-z_-]+)\]'  ~/.aws/credentials | tr -d '][' | shuf |  \
    xargs -n 1 -L 1 -I @ -r -P 4 ./prowler -p @ -M csv  2> /dev/null  >> all-accounts.csv
    ```

1. For help about usage run:

    ```
    ./prowler -h
    ```

## Advanced Usage

### Assume Role:

Prowler uses the AWS CLI underneath so it uses the same authentication methods. However, there are few ways to run Prowler against multiple accounts using IAM Assume Role feature depending on eachg use case. You can just set up your custom profile inside `~/.aws/config` with all needed information about the role to assume then call it with `./prowler -p your-custom-profile`. Additionally you can use `-A 123456789012` and `-R RemoteRoleToAssume` and Prowler will get those temporary credentials using `aws sts assume-role`, set them up as environment variables and run against that given account. To create a role to assume in multiple accounts easier eather as CFN Stack or StackSet, look at [this CloudFormation template](iam/create_role_to_assume_cfn.yaml) and adapt it.

```sh
./prowler -A 123456789012 -R ProwlerRole
```

```sh
./prowler -A 123456789012 -R ProwlerRole -I 123456
```

> *NOTE 1 about Session Duration*: By default it gets credentials valid for 1 hour (3600 seconds). Depending on the mount of checks you run and the size of your infrastructure, Prowler may require more than 1 hour to finish. Use option `-T <seconds>`  to allow up to 12h (43200 seconds). To allow more than 1h you need to modify *"Maximum CLI/API session duration"* for that particular role, read more [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session).

> *NOTE 2 about Session Duration*: Bear in mind that if you are using roles assumed by role chaining there is a hard limit of 1 hour so consider not using role chaining if possible, read more about that, in foot note 1 below the table [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html).

For example, if you want to get only the fails in CSV format from all checks regarding RDS without banner from the AWS Account 123456789012 assuming the role RemoteRoleToAssume and set a fixed session duration of 1h:

```sh
./prowler -A 123456789012 -R RemoteRoleToAssume -T 3600 -b -M cvs -q -g rds
```
or with a given External ID:
```sh
./prowler -A 123456789012 -R RemoteRoleToAssume -T 3600 -I 123456 -b -M cvs -q -g rds
```

### Assume Role and across all accounts in AWS Organizations or just a list of accounts:

If you want to run Prowler or just a check or a group across all accounts of AWS Organizations you can do this:

First get a list of accounts that are not suspended:
```
ACCOUNTS_IN_ORGS=$(aws organizations list-accounts --query Accounts[?Status==`ACTIVE`].Id --output text)
```
Then run Prowler to assume a role (same in all members) per each account, in this example it is just running one particular check:
```
for accountId in $ACCOUNTS_IN_ORGS; do ./prowler -A $accountId -R RemoteRoleToAssume -c extra79; done
```
Using the same for loop it can be scanned a list of accounts with a variable like `ACCOUNTS_LIST='11111111111 2222222222 333333333'`

### Get AWS Account details from your AWS Organization:

From Prowler v2.8, you can get additional information of the scanned account in CSV and JSON outputs. When scanning a single account you get the Account ID as part of the output. Now, if you have AWS Organizations and are scanning multiple accounts using the assume role functionality, Prowler can get your account details like Account Name, Email, ARN, Organization ID and Tags and you will have them next to every finding in the CSV and JSON outputs.
In order to do that you can use the new option `-O <management account id>`, requires `-R <role to assume>` and also needs permissions `organizations:ListAccounts*` and `organizations:ListTagsForResource`. See the following sample command:
```
./prowler -R ProwlerScanRole -A 111111111111 -O 222222222222 -M json,csv
```
In that command Prowler will scan the account `111111111111` assuming the role `ProwlerScanRole` and getting the account details from the AWS Organizatiosn management account `222222222222` assuming the same role `ProwlerScanRole` for that and creating two reports with those details in JSON and CSV.

In the JSON output below (redacted) you can see tags coded in base64 to prevent breaking CSV or JSON due to its format:

```json
  "Account Email": "my-prod-account@domain.com",
  "Account Name": "my-prod-account",
  "Account ARN": "arn:aws:organizations::222222222222:account/o-abcde1234/111111111111",
  "Account Organization": "o-abcde1234",
  "Account tags": "\"eyJUYWdzIjpasf0=\""
```
The additional fields in CSV header output are as follow:

```csv
ACCOUNT_DETAILS_EMAIL,ACCOUNT_DETAILS_NAME,ACCOUNT_DETAILS_ARN,ACCOUNT_DETAILS_ORG,ACCOUNT_DETAILS_TAGS
```

### GovCloud

Prowler runs in GovCloud regions as well. To make sure it points to the right API endpoint use `-r` to either `us-gov-west-1` or `us-gov-east-1`. If not filter region is used it will look for resources in both GovCloud regions by default:
```sh
./prowler -r us-gov-west-1
```
> For Security Hub integration see below in Security Hub section.

### Custom folder for custom checks

Flag `-x /my/own/checks` will include any check in that particular directory (files must start by check). To see how to write checks see [Add Custom Checks](#add-custom-checks) section.

S3 URIs are also supported as custom folders for custom checks, e.g. `s3://bucket/prefix/checks`. Prowler will download the folder locally and run the checks as they are called with default execution,`-c` or `-g`.
>Make sure that the used credentials have s3:GetObject permissions in the S3 path where the custom checks are located.

### Show or log only FAILs

In order to remove noise and get only FAIL findings there is a `-q` flag that makes Prowler to show and log only FAILs.
It can be combined with any other option.
Will show WARNINGS when a resource is excluded, just to take into consideration.

```sh
# -q option combined with -M csv -b
./prowler -q -M csv -b
```

### Set the entropy limit for detect-secrets

Sets the entropy limit for high entropy base64 strings from environment variable `BASE64_LIMIT`. Value must be between 0.0 and 8.0, defaults is 4.5.
Sets the entropy limit for high entropy hex strings from environment variable `HEX_LIMIT`. Value must be between 0.0 and 8.0, defaults is 3.0.

```sh
export BASE64_LIMIT=4.5
export HEX_LIMIT=3.0
```
### Run Prowler using AWS CloudShell

An easy way to run Prowler to scan your account is using AWS CloudShell. Read more and learn how to do it [here](util/cloudshell/README.md).

## Security Hub integration

Since October 30th 2020 (version v2.3RC5), Prowler supports natively and as **official integration** sending findings to [AWS Security Hub](https://aws.amazon.com/security-hub). This integration allows Prowler to import its findings to AWS Security Hub. With Security Hub, you now have a single place that aggregates, organizes, and prioritizes your security alerts, or findings, from multiple AWS services, such as Amazon GuardDuty, Amazon Inspector, Amazon Macie, AWS Identity and Access Management (IAM) Access Analyzer, and AWS Firewall Manager, as well as from AWS Partner solutions and from Prowler for free.

Before sending findings to Prowler, you need to perform next steps:
1. Since Security Hub is a region based service, enable it in the region or regions you require. Use the AWS Management Console or using the AWS CLI with this command if you have enough permissions:
    - `aws securityhub enable-security-hub --region <region>`.
2. Enable Prowler as partner integration integration. Use the AWS Management Console or using the AWS CLI with this command if you have enough permissions:
    - `aws securityhub enable-import-findings-for-product --region <region> --product-arn arn:aws:securityhub:<region>::product/prowler/prowler` (change region also inside the ARN).
    - Using the AWS Management Console:
    ![Screenshot 2020-10-29 at 10 26 02 PM](https://user-images.githubusercontent.com/3985464/97634660-5ade3400-1a36-11eb-9a92-4a45cc98c158.png)
3. As mentioned in section "Custom IAM Policy", to allow Prowler to import its findings to AWS Security Hub you need to add the policy below to the role or user running Prowler:
    - [iam/prowler-security-hub.json](iam/prowler-security-hub.json)

Once it is enabled, it is as simple as running the command below (for all regions):

```sh
./prowler -M json-asff -S
```
or for only one filtered region like eu-west-1:
```sh
./prowler -M json-asff -q -S -f eu-west-1
```
> Note 1: It is recommended to send only fails to Security Hub and that is possible adding `-q` to the command.

> Note 2: Since Prowler perform checks to all regions by defaults you may need to filter by region when runing Security Hub integration, as shown in the example above. Remember to enable Security Hub in the region or regions you need by calling `aws securityhub enable-security-hub --region <region>` and run Prowler with the option `-f <region>` (if no region is used it will try to push findings in all regions hubs).

> Note 3: to have updated findings in Security Hub you have to run Prowler periodically. Once a day or every certain amount of hours.

Once you run findings for first time you will be able to see Prowler findings in Findings section:

![Screenshot 2020-10-29 at 10 29 05 PM](https://user-images.githubusercontent.com/3985464/97634676-66c9f600-1a36-11eb-9341-70feb06f6331.png)

### Security Hub in GovCloud regions

To use Prowler and Security Hub integration in GovCloud there is an additional requirement, usage of `-r` is needed to point the API queries to the right API endpoint. Here is a sample command that sends only failed findings to Security Hub in region `us-gov-west-1`:
```
./prowler -r us-gov-west-1 -f us-gov-west-1 -S -M csv,json-asff -q
```

### Security Hub in China regions

To use Prowler and Security Hub integration in China regions there is an additional requirement, usage of `-r` is needed to point the API queries to the right API endpoint. Here is a sample command that sends only failed findings to Security Hub in region `cn-north-1`:
```
./prowler -r cn-north-1 -f cn-north-1 -q -S -M csv,json-asff
```

## CodeBuild deployment

Either to run Prowler once or based on a schedule this template makes it pretty straight forward. This template will create a CodeBuild environment and run Prowler directly leaving all reports in a bucket and creating a report also inside CodeBuild basedon the JUnit output from Prowler. Scheduling can be cron based like `cron(0 22 * * ? *)` or rate based like `rate(5 hours)` since CloudWatch Event rules (or Eventbridge) is used here.

The Cloud Formation template that helps you doing that is [here](https://github.com/prowler-cloud/prowler/blob/master/util/codebuild/codebuild-prowler-audit-account-cfn.yaml).

> This is a simple solution to monitor one account. For multiples accounts see [Multi Account and Continuous Monitoring](util/org-multi-account/README.md).

## Allowlist or remove a fail from resources

Sometimes you may find resources that are intentionally configured in a certain way that may be a bad practice but it is all right with it, for example an S3 bucket open to the internet hosting a web site, or a security group with an open port needed in your use case. Now you can use `-w allowlist_sample.txt` and add your resources as `checkID:resourcename` as in this command:

```
./prowler -w allowlist_sample.txt
```

S3 URIs are also supported as allowlist file, e.g. `s3://bucket/prefix/allowlist_sample.txt`
>Make sure that the used credentials have s3:GetObject permissions in the S3 path where the allowlist file is located.

DynamoDB table ARNs are also supported as allowlist file, e.g. `arn:aws:dynamodb:us-east-1:111111222222:table/allowlist`
>Make sure that the table has `account_id` as partition key and `rule` as sort key, and that the used credentials have `dynamodb:PartiQLSelect` permissions in the table.
><p align="left"><img src="https://user-images.githubusercontent.com/38561120/165769502-296f9075-7cc8-445e-8158-4b21804bfe7e.png" alt="image" width="397" height="252" /></p>

>The field `account_id` can contains either an account ID or an `*` (which applies to all the accounts that use this table as a whitelist). As in the traditional allowlist file, the `rule` field must contain `checkID:resourcename` pattern.
><p><img src="https://user-images.githubusercontent.com/38561120/165770610-ed5c2764-7538-44c2-9195-bcfdecc4ef9b.png" alt="image" width="394" /></p>



Allowlist option works along with other options and adds a `WARNING` instead of `INFO`, `PASS` or `FAIL` to any output format except for `json-asff`.

## How to fix every FAIL

Check your report and fix the issues following all specific guidelines per check in <https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf>

## Troubleshooting

### STS expired token

If you are using an STS token for AWS-CLI and your session is expired you probably get this error:

```sh
A client error (ExpiredToken) occurred when calling the GenerateCredentialReport operation: The security token included in the request is expired
```

To fix it, please renew your token by authenticating again to the AWS API, see next section below if you use MFA.

### Run Prowler with MFA protected credentials

To run Prowler using a profile that requires MFA you just need to get the session token before hand. Just make sure you use this command:

```sh
aws --profile <YOUR_AWS_PROFILE> sts get-session-token --duration 129600 --serial-number <ARN_OF_MFA> --token-code <MFA_TOKEN_CODE> --output text
```

Once you get your token you can export it as environment variable:

```sh
export AWS_PROFILE=YOUR_AWS_PROFILE
export AWS_SESSION_TOKEN=YOUR_NEW_TOKEN
AWS_SECRET_ACCESS_KEY=YOUR_SECRET
export AWS_ACCESS_KEY_ID=YOUR_KEY
```

or set manually up your `~/.aws/credentials` file properly.

There are some helpfull tools to save time in this process like [aws-mfa-script](https://github.com/asagage/aws-mfa-script) or [aws-cli-mfa](https://github.com/sweharris/aws-cli-mfa).

### AWS Managed IAM Policies

[ViewOnlyAccess](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_view-only-user)
- Use case: This user can view a list of AWS resources and basic metadata in the account across all services. The user cannot read resource content or metadata that goes beyond the quota and list information for resources.
- Policy description: This policy grants List*, Describe*, Get*, View*, and Lookup* access to resources for most AWS services. To see what actions this policy includes for each service, see [ViewOnlyAccess Permissions](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/job-function/ViewOnlyAccess)

[SecurityAudit](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_security-auditor)
- Use case: This user monitors accounts for compliance with security requirements. This user can access logs and events to investigate potential security breaches or potential malicious activity.
- Policy description: This policy grants permissions to view configuration data for many AWS services and to review their logs. To see what actions this policy includes for each service, see [SecurityAudit Permissions](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/SecurityAudit)

### Custom IAM Policy

[Prowler-Additions-Policy](iam/prowler-additions-policy.json)

Some new and specific checks require Prowler to inherit more permissions than SecurityAudit and ViewOnlyAccess to work properly. In addition to the AWS managed policies, "SecurityAudit" and "ViewOnlyAccess", the user/role you use for checks may need to be granted a custom policy with a few more read-only permissions (to support additional services mostly). Here is an example policy with the additional rights, "Prowler-Additions-Policy" (see below bootstrap script for set it up):

- [iam/prowler-additions-policy.json](iam/prowler-additions-policy.json)

[Prowler-Security-Hub Policy](iam/prowler-security-hub.json)

Allows Prowler to import its findings to [AWS Security Hub](https://aws.amazon.com/security-hub). More information in [Security Hub integration](#security-hub-integration):

- [iam/prowler-security-hub.json](iam/prowler-security-hub.json)

### Bootstrap Script

Quick bash script to set up a "prowler" IAM user with "SecurityAudit" and "ViewOnlyAccess" group with the required permissions (including "Prowler-Additions-Policy"). To run the script below, you need user with administrative permissions; set the `AWS_DEFAULT_PROFILE` to use that account:

```sh
export AWS_DEFAULT_PROFILE=default
export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' | tr -d '"')
aws iam create-group --group-name Prowler
aws iam create-policy --policy-name Prowler-Additions-Policy --policy-document file://$(pwd)/iam/prowler-additions-policy.json
aws iam attach-group-policy --group-name Prowler --policy-arn arn:aws:iam::aws:policy/SecurityAudit
aws iam attach-group-policy --group-name Prowler --policy-arn arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
aws iam attach-group-policy --group-name Prowler --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/Prowler-Additions-Policy
aws iam create-user --user-name prowler
aws iam add-user-to-group --user-name prowler --group-name Prowler
aws iam create-access-key --user-name prowler
unset ACCOUNT_ID AWS_DEFAULT_PROFILE
```

The `aws iam create-access-key` command will output the secret access key and the key id; keep these somewhere safe, and add them to `~/.aws/credentials` with an appropriate profile name to use them with Prowler. This is the only time they secret key will be shown.  If you lose it, you will need to generate a replacement.

> [This CloudFormation template](iam/create_role_to_assume_cfn.yaml) may also help you on that task.

## Extras

We are adding additional checks to improve the information gather from each account, these checks are out of the scope of the CIS benchmark for AWS, but we consider them very helpful to get to know each AWS account set up and find issues on it.

Some of these checks look for publicly facing resources may not actually be fully public due to other layered controls like S3 Bucket Policies, Security Groups or Network ACLs.

To list all existing checks in the extras group run the command below:

```sh
./prowler -l -g extras
```

>There are some checks not included in that list, they are experimental or checks that takes long to run like `extra759` and `extra760` (search for secrets in Lambda function variables and code).

To check all extras in one command:

```sh
./prowler -g extras
```

or to run just one of the checks:

```sh
./prowler -c extraNUMBER
```

or to run multiple extras in one go:

```sh
./prowler -c extraNumber,extraNumber
```


## Forensics Ready Checks

With this group of checks, Prowler looks if each service with logging or audit capabilities has them enabled to ensure all needed evidences are recorded and collected for an eventual digital forensic investigation in case of incident. List of checks part of this group (you can also see all groups with `./prowler -L`). The list of checks can be seen in the group file at:

[groups/group8_forensics](groups/group8_forensics)

The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command:

```sh
./prowler -g forensics-ready
```

## GDPR Checks

With this group of checks, Prowler shows result of checks related to GDPR, more information [here](https://github.com/prowler-cloud/prowler/issues/189). The list of checks can be seen in the group file at:

[groups/group9_gdpr](groups/group9_gdpr)

The `gdpr` group of checks uses existing and extra checks. To get a GDPR report, run this command:

```sh
./prowler -g gdpr
```

## AWS FTR Checks

With this group of checks, Prowler shows result of checks related to the AWS Foundational Technical Review, more information [here](https://apn-checklists.s3.amazonaws.com/foundational/partner-hosted/partner-hosted/CVLHEC5X7.html). The list of checks can be seen in the group file at:

[groups/group25_ftr](groups/group25_FTR)

The `ftr` group of checks uses existing and extra checks. To get a AWS FTR report, run this command:

```sh
./prowler -g ftr
```

## HIPAA Checks

With this group of checks, Prowler shows results of controls related to the "Security Rule" of the Health Insurance Portability and Accountability Act aka [HIPAA](https://www.hhs.gov/hipaa/for-professionals/security/index.html) as defined in [45 CFR Subpart C - Security Standards for the Protection of Electronic Protected Health Information](https://www.law.cornell.edu/cfr/text/45/part-164/subpart-C) within [PART 160 - GENERAL ADMINISTRATIVE REQUIREMENTS](https://www.law.cornell.edu/cfr/text/45/part-160) and [Subpart A](https://www.law.cornell.edu/cfr/text/45/part-164/subpart-A) and [Subpart C](https://www.law.cornell.edu/cfr/text/45/part-164/subpart-C) of PART 164 - SECURITY AND PRIVACY

More information on the original PR is [here](https://github.com/prowler-cloud/prowler/issues/227).

### Note on Business Associate Addendum's (BAA)

Under the HIPAA regulations, cloud service providers (CSPs) such as AWS are considered business associates. The Business Associate Addendum (BAA) is an AWS contract that is required under HIPAA rules to ensure that AWS appropriately safeguards protected health information (PHI). The BAA also serves to clarify and limit, as appropriate, the permissible uses and disclosures of PHI by AWS, based on the relationship between AWS and our customers, and the activities or services being performed by AWS. Customers may use any AWS service in an account designated as a HIPAA account, but they should only process, store, and transmit protected health information (PHI) in the HIPAA-eligible services defined in the Business Associate Addendum (BAA). For the latest list of HIPAA-eligible AWS services, see [HIPAA Eligible Services Reference](https://aws.amazon.com/compliance/hipaa-eligible-services-reference/).

More information on AWS & HIPAA can be found [here](https://aws.amazon.com/compliance/hipaa-compliance/)

The list of checks showed by this group is as follows, they will be mostly relevant for Subsections [164.306 Security standards: General rules](https://www.law.cornell.edu/cfr/text/45/164.306) and [164.312 Technical safeguards](https://www.law.cornell.edu/cfr/text/45/164.312). Prowler is only able to make checks in the spirit of the technical requirements outlined in these Subsections, and cannot cover all procedural controls required. They be found in the group file at:

[groups/group10_hipaa](groups/group10_hipaa)

The `hipaa` group of checks uses existing and extra checks. To get a HIPAA report, run this command:

```sh
./prowler -g hipaa
```

## Trust Boundaries Checks

### Definition and Terms

The term "trust boundary" is originating from the threat modelling process and the most popular contributor Adam Shostack and author of "Threat Modeling: Designing for Security" defines it as following ([reference](https://adam.shostack.org/uncover.html)):

> Trust boundaries are perhaps the most subjective of all: these represent the border between trusted and untrusted elements. Trust is complex. You might trust your mechanic with your car, your dentist with your teeth, and your banker with your money, but you probably don't trust your dentist to change your spark plugs.

AWS is made to be flexible for service links within and between different AWS accounts, we all know that.

This group of checks helps to analyse a particular AWS account (subject) on existing links to other AWS accounts across various AWS services, in order to identify untrusted links.

### Run
To give it a quick shot just call:

```sh
./prowler -g trustboundaries
```

### Scenarios

Currently, this check group supports two different scenarios:

1. Single account environment: no action required, the configuration is happening automatically for you.
2. Multi account environment: in case you environment has multiple trusted and known AWS accounts you maybe want to append them manually to [groups/group16_trustboundaries](groups/group16_trustboundaries) as a space separated list into `GROUP_TRUSTBOUNDARIES_TRUSTED_ACCOUNT_IDS` variable, then just run prowler.

### Coverage

Current coverage of Amazon Web Service (AWS) taken from [here](https://docs.aws.amazon.com/whitepapers/latest/aws-overview/introduction.html):
| Topic                           | Service    | Trust Boundary                                                            |
|---------------------------------|------------|---------------------------------------------------------------------------|
| Networking and Content Delivery | Amazon VPC | VPC endpoints connections ([extra786](checks/check_extra786))             |
|                                 |            | VPC endpoints allowlisted principals ([extra787](checks/check_extra787))  |

All ideas or recommendations to extend this group are very welcome [here](https://github.com/prowler-cloud/prowler/issues/new/choose).

### Detailed Explanation of the Concept

The diagrams depict two common scenarios, single account and multi account environments.
Every circle represents one AWS account.
The dashed line represents the trust boundary, that separates trust and untrusted AWS accounts.
The arrow simply describes the direction of the trust, however the data can potentially flow in both directions.

Single Account environment assumes that only the AWS account subject to this analysis is trusted. However, there is a chance that two VPCs are existing within that one AWS account which are still trusted as a self reference.
![single-account-environment](/docs/images/prowler-single-account-environment.png)

Multi Account environments assumes a minimum of two trusted or known accounts. For this particular example all trusted and known accounts will be tested. Therefore `GROUP_TRUSTBOUNDARIES_TRUSTED_ACCOUNT_IDS` variable in [groups/group16_trustboundaries](groups/group16_trustboundaries) should include all trusted accounts Account #A, Account #B, Account #C, and Account #D in order to finally raise Account #E and Account #F for being untrusted or unknown.
![multi-account-environment](/docs/images/prowler-multi-account-environment.png)

## Custom Checks
Using  `./prowler -c extra9999 -a` you can build your own on-the-fly custom check by specifying the AWS CLI command to execute.
> Omit the "aws" command and only use its parameters within quotes and do not nest quotes in the aws parameter, --output text is already included in the check.
>
Here is an example of a check to find SGs with inbound port 80:

```sh
./prowler -c extra9999 -a 'ec2 describe-security-groups --filters Name=ip-permission.to-port,Values=80 --query SecurityGroups[*].GroupId[]]'
```

In order to add any new check feel free to create a new extra check in the extras group or other group. To do so, you will need to follow these steps:

1. Follow structure in file `checks/check_sample`
2. Name your check with a number part of an existing group or a new one
3. Save changes and run it as `./prowler -c extraNN`
4. Send me a pull request! :)

## Add Custom Groups

1. Follow structure in file `groups/groupN_sample`
1. Name your group with a non existing number
1. Save changes and run it as `./prowler -g extraNN`
1. Send me a pull request! :)

- You can also create a group with only the checks that you want to perform in your company, for instance a group named `group9_mycompany` with only the list of checks that you care or your particular compliance applies.

## Third Party Integrations

### Telegram

Javier Pecete has done an awesome job integrating Prowler with Telegram, you have more details here <https://github.com/i4specete/ServerTelegramBot>

### Cloud Security Suite

The guys of SecurityFTW have added Prowler in their Cloud Security Suite along with other cool security tools <https://github.com/SecurityFTW/cs-suite>

## License

Prowler is licensed as Apache License 2.0 as specified in each file. You may obtain a copy of the License at
<http://www.apache.org/licenses/LICENSE-2.0>

**I'm not related anyhow with CIS organization, I just write and maintain Prowler to help companies over the world to make their cloud infrastructure more secure.**

If you want to contact me visit <https://blyx.com/contact> or follow me on Twitter <https://twitter.com/prowler-cloud> my DMs are open.


## Prowler 3.0
### Project Structure
```
.
├── README.md
├── check
│   └── check.py
├── poc.py
└── providers
    └── aws
        ├── aws_provider.py
        └── services
            └── iam
                ├── iam_disable_30_days_credentials
                │   ├── iam_disable_30_days_credentials.metadata.json
                │   └── iam_disable_30_days_credentials.py
                └── iam_service.py
```

Pipfile.lock METASEP
{
  "_meta": {
    "hash": {
      "sha256": "c5d684bfa255c02f7676641165c651ebb46c7f4cab5c3d2916da4a7d4bd0f0ce"
    },
    "pipfile-spec": 6,
    "requires": {
      "python_version": "3"
    },
    "sources": [
      {
        "name": "pypi",
        "url": "https://pypi.org/simple",
        "verify_ssl": true
      }
    ]
  },
  "default": {
    "arnparse": {
      "hashes": [
        "sha256:b0906734e4b8f19e39b1e32944c6cd6274b6da90c066a83882ac7a11d27553e0",
        "sha256:cb87f17200d07121108a9085d4a09cc69a55582647776b9a917b0b1f279db8f8"
      ],
      "index": "pypi",
      "version": "==0.0.2"
    },
    "attrs": {
      "hashes": [
        "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
        "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
      ],
      "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
      "version": "==21.4.0"
    },
    "bandit": {
      "hashes": [
        "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2",
        "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"
      ],
      "index": "pypi",
      "version": "==1.7.4"
    },
    "boto3": {
      "hashes": [
        "sha256:5c775dcb12ca5d6be3f5aa3c49d77783faa64eb30fd3f4af93ff116bb42f9ffb",
        "sha256:5d9bcc355cf6edd7f3849fedac4252e12a0aa2b436cdbc0d4371b16a0f852a30"
      ],
      "index": "pypi",
      "version": "==1.24.34"
    },
    "botocore": {
      "hashes": [
        "sha256:0d824a5315f5f5c3bea53c14107a69695ef43190edf647f1281bac8f172ca77c",
        "sha256:9c695d47f1f1212f3e306e51f7bacdf67e58055194ddcf7d8296660b124cf135"
      ],
      "index": "pypi",
      "version": "==1.27.34"
    },
    "certifi": {
      "hashes": [
        "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
        "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==2022.6.15"
    },
    "cffi": {
      "hashes": [
        "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5",
        "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef",
        "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104",
        "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426",
        "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405",
        "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375",
        "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a",
        "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e",
        "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc",
        "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf",
        "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185",
        "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497",
        "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3",
        "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35",
        "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
        "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83",
        "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21",
        "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca",
        "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984",
        "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac",
        "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd",
        "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee",
        "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a",
        "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2",
        "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192",
        "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7",
        "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585",
        "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f",
        "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e",
        "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27",
        "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b",
        "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e",
        "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e",
        "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d",
        "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c",
        "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415",
        "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82",
        "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02",
        "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314",
        "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
        "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
        "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3",
        "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914",
        "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045",
        "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d",
        "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9",
        "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5",
        "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2",
        "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c",
        "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3",
        "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2",
        "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
        "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d",
        "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d",
        "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
        "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162",
        "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76",
        "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4",
        "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e",
        "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9",
        "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6",
        "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b",
        "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01",
        "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"
      ],
      "version": "==1.15.1"
    },
    "charset-normalizer": {
      "hashes": [
        "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
        "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==2.1.0"
    },
    "click": {
      "hashes": [
        "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
        "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==8.1.3"
    },
    "colorama": {
      "hashes": [
        "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da",
        "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"
      ],
      "index": "pypi",
      "version": "==0.4.5"
    },
    "coverage": {
      "hashes": [
        "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32",
        "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7",
        "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996",
        "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55",
        "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46",
        "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de",
        "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039",
        "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee",
        "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1",
        "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f",
        "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63",
        "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083",
        "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe",
        "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0",
        "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6",
        "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe",
        "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933",
        "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0",
        "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c",
        "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07",
        "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8",
        "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b",
        "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e",
        "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120",
        "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f",
        "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e",
        "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd",
        "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f",
        "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386",
        "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8",
        "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae",
        "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc",
        "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783",
        "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d",
        "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c",
        "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97",
        "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978",
        "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf",
        "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29",
        "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39",
        "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"
      ],
      "index": "pypi",
      "version": "==6.4.2"
    },
    "cryptography": {
      "hashes": [
        "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59",
        "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596",
        "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3",
        "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5",
        "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab",
        "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884",
        "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82",
        "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b",
        "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441",
        "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa",
        "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d",
        "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b",
        "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a",
        "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6",
        "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157",
        "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280",
        "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282",
        "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67",
        "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8",
        "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046",
        "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327",
        "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==37.0.4"
    },
    "dparse": {
      "hashes": [
        "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367",
        "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"
      ],
      "markers": "python_version >= '3.5'",
      "version": "==0.5.1"
    },
    "gitdb": {
      "hashes": [
        "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd",
        "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==4.0.9"
    },
    "gitpython": {
      "hashes": [
        "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704",
        "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==3.1.27"
    },
    "idna": {
      "hashes": [
        "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
        "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
      ],
      "markers": "python_version >= '3.5'",
      "version": "==3.3"
    },
    "iniconfig": {
      "hashes": [
        "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
        "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
      ],
      "version": "==1.1.1"
    },
    "jinja2": {
      "hashes": [
        "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
        "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==3.1.2"
    },
    "jmespath": {
      "hashes": [
        "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980",
        "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==1.0.1"
    },
    "markupsafe": {
      "hashes": [
        "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
        "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
        "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
        "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
        "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
        "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
        "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
        "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
        "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
        "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
        "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
        "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
        "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
        "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
        "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
        "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
        "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
        "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
        "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
        "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
        "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
        "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
        "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
        "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
        "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
        "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
        "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
        "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
        "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
        "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
        "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
        "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
        "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
        "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
        "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
        "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
        "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
        "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
        "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
        "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==2.1.1"
    },
    "mock": {
      "hashes": [
        "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62",
        "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==4.0.3"
    },
    "moto": {
      "extras": [
        "iam"
      ],
      "hashes": [
        "sha256:8bb8e267d9b948509d4739d81d995615a193d2c459f5c0a979aaeb0d3bd4b381",
        "sha256:cbe8ad8a949f519771e5d25b670738604757fb67cd474d75d14c20677582e81f"
      ],
      "index": "pypi",
      "version": "==3.1.16"
    },
    "packaging": {
      "hashes": [
        "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
        "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==21.3"
    },
    "pbr": {
      "hashes": [
        "sha256:e547125940bcc052856ded43be8e101f63828c2d94239ffbe2b327ba3d5ccf0a",
        "sha256:e8dca2f4b43560edef58813969f52a56cef023146cbb8931626db80e6c1c4308"
      ],
      "markers": "python_version >= '2.6'",
      "version": "==5.9.0"
    },
    "pluggy": {
      "hashes": [
        "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
        "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==1.0.0"
    },
    "py": {
      "hashes": [
        "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
        "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
      ],
      "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
      "version": "==1.11.0"
    },
    "pycparser": {
      "hashes": [
        "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
        "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
      ],
      "version": "==2.21"
    },
    "pydantic": {
      "hashes": [
        "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f",
        "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74",
        "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1",
        "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b",
        "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537",
        "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310",
        "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810",
        "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a",
        "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761",
        "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892",
        "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58",
        "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761",
        "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195",
        "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1",
        "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd",
        "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b",
        "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee",
        "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580",
        "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608",
        "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918",
        "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380",
        "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a",
        "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0",
        "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd",
        "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728",
        "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49",
        "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166",
        "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6",
        "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131",
        "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11",
        "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193",
        "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a",
        "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd",
        "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e",
        "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"
      ],
      "index": "pypi",
      "version": "==1.9.1"
    },
    "pyparsing": {
      "hashes": [
        "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
        "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
      ],
      "markers": "python_full_version >= '3.6.8'",
      "version": "==3.0.9"
    },
    "pytest": {
      "hashes": [
        "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c",
        "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"
      ],
      "index": "pypi",
      "version": "==7.1.2"
    },
    "python-dateutil": {
      "hashes": [
        "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
        "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
      ],
      "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
      "version": "==2.8.2"
    },
    "pytz": {
      "hashes": [
        "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7",
        "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"
      ],
      "version": "==2022.1"
    },
    "pyyaml": {
      "hashes": [
        "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
        "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
        "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
        "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
        "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
        "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
        "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
        "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
        "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
        "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
        "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
        "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
        "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
        "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
        "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
        "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
        "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
        "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
        "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
        "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
        "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
        "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
        "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
        "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
        "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
        "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
        "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
        "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
        "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
        "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
        "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
        "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
        "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==6.0"
    },
    "requests": {
      "hashes": [
        "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
        "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
      ],
      "markers": "python_version >= '3.7' and python_version < '4'",
      "version": "==2.28.1"
    },
    "responses": {
      "hashes": [
        "sha256:2dcc863ba63963c0c3d9ee3fa9507cbe36b7d7b0fccb4f0bdfd9e96c539b1487",
        "sha256:b82502eb5f09a0289d8e209e7bad71ef3978334f56d09b444253d5ad67bf5253"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==0.21.0"
    },
    "ruamel.yaml": {
      "hashes": [
        "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7",
        "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"
      ],
      "markers": "python_version >= '3'",
      "version": "==0.17.21"
    },
    "ruamel.yaml.clib": {
      "hashes": [
        "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd",
        "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee",
        "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0",
        "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7",
        "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277",
        "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104",
        "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd",
        "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0",
        "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78",
        "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de",
        "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99",
        "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527",
        "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84",
        "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7",
        "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468",
        "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b",
        "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94",
        "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233",
        "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb",
        "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5",
        "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe",
        "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751",
        "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502",
        "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed",
        "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"
      ],
      "markers": "python_version < '3.11' and platform_python_implementation == 'CPython'",
      "version": "==0.2.6"
    },
    "s3transfer": {
      "hashes": [
        "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd",
        "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==0.6.0"
    },
    "safety": {
      "hashes": [
        "sha256:05ba551fb61ef24c864835d21089f75bc8b37292680047b9f29693a6552e2fc7",
        "sha256:dbc5dffa2e47da76cc43dfe8cbbbfca99d29118d0c6c54dfcfa11c2bd349dff6"
      ],
      "index": "pypi",
      "version": "==2.1.1"
    },
    "setuptools": {
      "hashes": [
        "sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16",
        "sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==63.2.0"
    },
    "six": {
      "hashes": [
        "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
        "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
      ],
      "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
      "version": "==1.16.0"
    },
    "smmap": {
      "hashes": [
        "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94",
        "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"
      ],
      "markers": "python_version >= '3.6'",
      "version": "==5.0.0"
    },
    "stevedore": {
      "hashes": [
        "sha256:87e4d27fe96d0d7e4fc24f0cbe3463baae4ec51e81d95fbe60d2474636e0c7d8",
        "sha256:f82cc99a1ff552310d19c379827c2c64dd9f85a38bcd5559db2470161867b786"
      ],
      "markers": "python_version >= '3.8'",
      "version": "==4.0.0"
    },
    "sure": {
      "hashes": [
        "sha256:34ae88c846046742ef074036bf311dc90ab152b7bc09c342b281cebf676727a2"
      ],
      "index": "pypi",
      "version": "==2.0.0"
    },
    "toml": {
      "hashes": [
        "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
        "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
      ],
      "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
      "version": "==0.10.2"
    },
    "tomli": {
      "hashes": [
        "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
        "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==2.0.1"
    },
    "typing-extensions": {
      "hashes": [
        "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
        "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==4.3.0"
    },
    "urllib3": {
      "hashes": [
        "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
        "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
      ],
      "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
      "version": "==1.26.10"
    },
    "vulture": {
      "hashes": [
        "sha256:2831694055eb2e36a09c3b7680934837102b9b6c0969206e3902d513612177c3",
        "sha256:a7c7e7a23b11e78840fdd821509d05a6134aa9fd60418fe39d60b3026fe698d9"
      ],
      "index": "pypi",
      "version": "==2.5"
    },
    "werkzeug": {
      "hashes": [
        "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6",
        "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"
      ],
      "markers": "python_version >= '3.7'",
      "version": "==2.1.2"
    },
    "xmltodict": {
      "hashes": [
        "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56",
        "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"
      ],
      "markers": "python_version >= '3.4'",
      "version": "==0.13.0"
    }
  },
  "develop": {}
}

Pipfile METASEP
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
colorama = "0.4.4"
boto3 = "1.24.8"
arnparse = "0.0.2"
botocore = "1.27.8"
pydantic = "1.9.1"
moto = {extras = ["iam"], version = "3.1.14"}
sure = "2.0.0"
bandit = "1.7.4"
safety = "1.10.3"
vulture = "2.4"
coverage = "6.4.1"
pytest = "7.1.2"

[dev-packages]

[requires]
python_version = "3"

Makefile METASEP
.DEFAULT_GOAL:=help

test:   ## Test with pytest
	pytest -v

coverage: ## Show Test Coverage
	coverage run --skip-covered -m pytest -v && \
	coverage report -m && \
	rm -rf .coverage
##@ Help
help:     ## Show this help.
	@echo "Prowler Makefile"
	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

LIST_OF_CHECKS_AND_GROUPS.md METASEP
```
./prowler -l # to see all available checks and their groups.
./prowler -L # to see all available groups only.
./prowler -l -g groupname # to see checks in a particular group
```

CODE_OF_CONDUCT.md METASEP
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
 advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
 address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
 professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at community@prowler.cloud. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

lib/utils/utils.py METASEP
import json
import sys
from hashlib import sha512
from io import TextIOWrapper
from os.path import exists
from typing import Any

from lib.logger import logger


def open_file(input_file: str, mode: str = "r") -> TextIOWrapper:
    try:
        f = open(input_file, mode)
    except Exception as e:
        logger.critical(f"{input_file}: {e.__class__.__name__}")
        sys.exit()
    else:
        return f


# Parse checks from file
def parse_json_file(input_file: TextIOWrapper) -> Any:
    try:
        json_file = json.load(input_file)
    except Exception as e:
        logger.critical(f"{input_file.name}: {e.__class__.__name__}")
        sys.exit()
    else:
        return json_file


# check if file exists
def file_exists(filename: str):
    try:
        exists_filename = exists(filename)
    except Exception as e:
        logger.critical(f"{exists_filename.name}: {e.__class__.__name__}")
        quit()
    else:
        if exists_filename:
            return True
        else:
            return False


# create sha512 hash for string
def hash_sha512(string: str) -> str:
    return sha512(string.encode("utf-8")).hexdigest()[0:9]

lib/utils/__init__.py METASEP

lib/check/models.py METASEP
import sys
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import List

from pydantic import BaseModel, ValidationError

from lib.logger import logger


@dataclass
class Output_From_Options:
    is_quiet: bool
    output_modes: list
    output_directory: str
    security_hub_enabled: bool


# Testing Pending
def load_check_metadata(metadata_file: str) -> dict:
    try:
        check_metadata = Check_Metadata_Model.parse_file(metadata_file)
    except ValidationError as error:
        logger.critical(f"Metadata from {metadata_file} is not valid: {error}")
        sys.exit()
    else:
        return check_metadata


class ComplianceItem(BaseModel):
    Control: List[str]
    Framework: str
    Group: List[str]
    Version: str


class Code(BaseModel):
    NativeIaC: str
    Terraform: str
    CLI: str
    Other: str


class Recommendation(BaseModel):
    Text: str
    Url: str


class Remediation(BaseModel):
    Code: Code
    Recommendation: Recommendation


class Check_Metadata_Model(BaseModel):
    Provider: str
    CheckID: str
    # CheckName: str
    CheckTitle: str
    # CheckAlias: str
    CheckType: str
    ServiceName: str
    SubServiceName: str
    ResourceIdTemplate: str
    Severity: str
    ResourceType: str
    Description: str
    Risk: str
    RelatedUrl: str
    Remediation: Remediation
    Categories: List[str]
    Tags: dict
    DependsOn: List[str]
    RelatedTo: List[str]
    Notes: str
    Compliance: List[ComplianceItem]


class Check(ABC):
    def __init__(self):
        # Load metadata from check
        check_path_name = self.__class__.__module__.replace(".", "/")
        metadata_file = f"{check_path_name}.metadata.json"
        self.__check_metadata__ = load_check_metadata(metadata_file)
        # Assign metadata values
        self.__Provider__ = self.__check_metadata__.Provider
        self.__CheckID__ = self.__check_metadata__.CheckID
        # self.__CheckName__ = self.__check_metadata__.CheckName
        self.__CheckTitle__ = self.__check_metadata__.CheckTitle
        # self.__CheckAlias__ = self.__check_metadata__.CheckAlias
        self.__CheckType__ = self.__check_metadata__.CheckType
        self.__ServiceName__ = self.__check_metadata__.ServiceName
        self.__SubServiceName__ = self.__check_metadata__.SubServiceName
        self.__ResourceIdTemplate__ = self.__check_metadata__.ResourceIdTemplate
        self.__Severity__ = self.__check_metadata__.Severity
        self.__ResourceType__ = self.__check_metadata__.ResourceType
        self.__Description__ = self.__check_metadata__.Description
        self.__Risk__ = self.__check_metadata__.Risk
        self.__RelatedUrl__ = self.__check_metadata__.RelatedUrl
        self.__Remediation__ = self.__check_metadata__.Remediation
        self.__Categories__ = self.__check_metadata__.Categories
        self.__Tags__ = self.__check_metadata__.Tags
        self.__DependsOn__ = self.__check_metadata__.DependsOn
        self.__RelatedTo__ = self.__check_metadata__.RelatedTo
        self.__Notes__ = self.__check_metadata__.Notes
        self.__Compliance__ = self.__check_metadata__.Compliance

    @property
    def provider(self):
        return self.__Provider__

    @property
    def checkID(self):
        return self.__CheckID__

    # @property
    # def checkName(self):
    #     return self.__CheckName__

    @property
    def checkTitle(self):
        return self.__CheckTitle__

    # @property
    # def checkAlias(self):
    #     return self.__CheckAlias__

    @property
    def checkType(self):
        return self.__CheckType__

    @property
    def serviceName(self):
        return self.__ServiceName__

    @property
    def subServiceName(self):
        return self.__SubServiceName__

    @property
    def resourceIdTemplate(self):
        return self.__ResourceIdTemplate__

    @property
    def severity(self):
        return self.__Severity__

    @property
    def resourceType(self):
        return self.__ResourceType__

    @property
    def description(self):
        return self.__Description__

    @property
    def relatedUrl(self):
        return self.__RelatedUrl__

    @property
    def risk(self):
        return self.__Risk__

    @property
    def remediation(self):
        return self.__Remediation__

    @property
    def categories(self):
        return self.__Categories__

    @property
    def tags(self):
        return self.__Tags__

    @property
    def dependsOn(self):
        return self.__DependsOn__

    @property
    def relatedTo(self):
        return self.__RelatedTo__

    @property
    def notes(self):
        return self.__Notes__

    @property
    def compliance(self):
        return self.__Compliance__

    @property
    def metadata(self):
        return self.__check_metadata__

    @abstractmethod
    def execute(self):
        pass


@dataclass
class Check_Report:
    status: str
    region: str
    status_extended: str
    check_metadata: dict
    resource_id: str
    resource_details: str
    resource_tags: list
    resource_arn: str

    def __init__(self, metadata):
        self.check_metadata = metadata
        self.status_extended = ""
        self.resource_details = ""
        self.resource_tags = []
        self.resource_id = ""
        self.resource_arn = ""

lib/check/checks_loader.py METASEP
from config.config import groups_file
from lib.check.check import (
    load_checks_to_execute_from_groups,
    parse_checks_from_file,
    parse_groups_from_file,
    recover_checks_from_provider,
)
from lib.logger import logger


#  Generate the list of checks to execute
# test this function
def load_checks_to_execute(
    bulk_checks_metadata: dict,
    checks_file: str,
    check_list: list,
    service_list: list,
    group_list: list,
    severities: list,
    provider: str,
) -> set:

    checks_to_execute = set()

    # Handle if there are checks passed using -c/--checks
    if check_list:
        for check_name in check_list:
            checks_to_execute.add(check_name)

    # Handle if there are some severities passed using --severity
    elif severities:
        for check in bulk_checks_metadata:
            # Check check's severity
            if bulk_checks_metadata[check].Severity in severities:
                checks_to_execute.add(check)

    # Handle if there are checks passed using -C/--checks-file
    elif checks_file:
        try:
            checks_to_execute = parse_checks_from_file(checks_file, provider)
        except Exception as e:
            logger.error(f"{e.__class__.__name__} -- {e}")

    # Handle if there are services passed using -s/--services
    elif service_list:
        # Loaded dynamically from modules within provider/services
        for service in service_list:
            modules = recover_checks_from_provider(provider, service)
            if not modules:
                logger.error(f"Service '{service}' was not found for the AWS provider")
            else:
                for check_module in modules:
                    # Recover check name and module name from import path
                    # Format: "providers.{provider}.services.{service}.{check_name}.{check_name}"
                    check_name = check_module.split(".")[-1]
                    # If the service is present in the group list passed as parameters
                    # if service_name in group_list: checks_to_execute.add(check_name)
                    checks_to_execute.add(check_name)

    # Handle if there are groups passed using -g/--groups
    elif group_list:
        try:
            available_groups = parse_groups_from_file(groups_file)
            checks_to_execute = load_checks_to_execute_from_groups(
                available_groups, group_list, provider
            )
        except Exception as e:
            logger.error(f"{e.__class__.__name__} -- {e}")

    # If there are no checks passed as argument
    else:
        try:
            # Get all check modules to run with the specific provider
            checks = recover_checks_from_provider(provider)
        except Exception as e:
            logger.error(f"{e.__class__.__name__} -- {e}")
        else:
            for check_name in checks:
                # Recover check name from import path (last part)
                # Format: "providers.{provider}.services.{service}.{check_name}.{check_name}"
                check_name = check_name.split(".")[-1]
                checks_to_execute.add(check_name)

    return checks_to_execute

lib/check/check_test.py METASEP
import os

from lib.check.check import (
    exclude_checks_to_run,
    exclude_groups_to_run,
    exclude_services_to_run,
    load_checks_to_execute_from_groups,
    parse_checks_from_file,
    parse_groups_from_file,
)
from lib.check.models import load_check_metadata


class Test_Check:
    # def test_import_check(self):
    #     test_cases = [
    #         {
    #             "name": "Test valid check path",
    #             "input": "providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials",
    #             "expected": "providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials",
    #         }
    #     ]
    #     for test in test_cases:
    #         assert importlib.import_module(test["input"]).__name__ == test["expected"

    def test_parse_groups_from_file(self):
        test_cases = [
            {
                "input": {
                    "path": f"{os.path.dirname(os.path.realpath(__file__))}/fixtures/groupsA.json",
                    "provider": "aws",
                },
                "expected": {
                    "aws": {
                        "gdpr": {
                            "description": "GDPR Readiness",
                            "checks": ["check11", "check12"],
                        },
                        "iam": {
                            "description": "Identity and Access Management",
                            "checks": [
                                "iam_disable_30_days_credentials",
                                "iam_disable_90_days_credentials",
                            ],
                        },
                    }
                },
            }
        ]
        for test in test_cases:
            check_file = test["input"]["path"]
            assert parse_groups_from_file(check_file) == test["expected"]

    def test_load_check_metadata(self):
        test_cases = [
            {
                "input": {
                    "metadata_path": f"{os.path.dirname(os.path.realpath(__file__))}/fixtures/metadata.json",
                },
                "expected": {
                    "CheckID": "iam_disable_30_days_credentials",
                    "CheckTitle": "Ensure credentials unused for 30 days or greater are disabled",
                    "ServiceName": "iam",
                    "Severity": "low",
                },
            }
        ]
        for test in test_cases:
            metadata_path = test["input"]["metadata_path"]
            check_metadata = load_check_metadata(metadata_path)
            assert check_metadata.CheckID == test["expected"]["CheckID"]
            assert check_metadata.CheckTitle == test["expected"]["CheckTitle"]
            assert check_metadata.ServiceName == test["expected"]["ServiceName"]
            assert check_metadata.Severity == test["expected"]["Severity"]

    def test_parse_checks_from_file(self):
        test_cases = [
            {
                "input": {
                    "path": f"{os.path.dirname(os.path.realpath(__file__))}/fixtures/checklistA.json",
                    "provider": "aws",
                },
                "expected": {"check11", "check12", "check7777"},
            }
        ]
        for test in test_cases:
            check_file = test["input"]["path"]
            provider = test["input"]["provider"]
            assert parse_checks_from_file(check_file, provider) == test["expected"]

    def test_load_checks_to_execute_from_groups(self):
        test_cases = [
            {
                "input": {
                    "groups_json": {
                        "aws": {
                            "gdpr": {
                                "description": "GDPR Readiness",
                                "checks": ["check11", "check12"],
                            },
                            "iam": {
                                "description": "Identity and Access Management",
                                "checks": [
                                    "iam_disable_30_days_credentials",
                                    "iam_disable_90_days_credentials",
                                ],
                            },
                        }
                    },
                    "provider": "aws",
                    "groups": ["gdpr"],
                },
                "expected": {"check11", "check12"},
            }
        ]

        for test in test_cases:
            provider = test["input"]["provider"]
            groups = test["input"]["groups"]
            group_file = test["input"]["groups_json"]

            assert (
                load_checks_to_execute_from_groups(group_file, groups, provider)
                == test["expected"]
            )

    def test_exclude_checks_to_run(self):
        test_cases = [
            {
                "input": {
                    "check_list": {"check12", "check11", "extra72", "check13"},
                    "excluded_checks": {"check12", "check13"},
                },
                "expected": {"check11", "extra72"},
            },
            {
                "input": {
                    "check_list": {"check112", "check11", "extra72", "check13"},
                    "excluded_checks": {"check12", "check13", "check14"},
                },
                "expected": {"check112", "check11", "extra72"},
            },
        ]
        for test in test_cases:
            check_list = test["input"]["check_list"]
            excluded_checks = test["input"]["excluded_checks"]
            assert (
                exclude_checks_to_run(check_list, excluded_checks) == test["expected"]
            )

    def test_exclude_groups_to_run(self):
        test_cases = [
            {
                "input": {
                    "excluded_group_list": {"gdpr"},
                    "provider": "aws",
                    "checks_to_run": {
                        "iam_disable_30_days_credentials",
                        "iam_disable_90_days_credentials",
                    },
                },
                "expected": {
                    "iam_disable_30_days_credentials",
                },
            },
            {
                "input": {
                    "excluded_group_list": {"pci"},
                    "provider": "aws",
                    "checks_to_run": {
                        "iam_disable_30_days_credentials",
                        "iam_disable_90_days_credentials",
                    },
                },
                "expected": {
                    "iam_disable_30_days_credentials",
                },
            },
        ]
        for test in test_cases:
            excluded_group_list = test["input"]["excluded_group_list"]
            checks_to_run = test["input"]["checks_to_run"]
            provider = test["input"]["provider"]
            assert (
                exclude_groups_to_run(checks_to_run, excluded_group_list, provider)
                == test["expected"]
            )

    def test_exclude_services_to_run(self):
        test_cases = [
            {
                "input": {
                    "checks_to_run": {
                        "iam_disable_30_days_credentials",
                        "iam_disable_90_days_credentials",
                    },
                    "excluded_services": {"ec2"},
                    "provider": "aws",
                },
                "expected": {
                    "iam_disable_30_days_credentials",
                    "iam_disable_90_days_credentials",
                },
            },
            {
                "input": {
                    "checks_to_run": {
                        "iam_disable_30_days_credentials",
                        "iam_disable_90_days_credentials",
                    },
                    "excluded_services": {"iam"},
                    "provider": "aws",
                },
                "expected": set(),
            },
        ]
        for test in test_cases:
            excluded_services = test["input"]["excluded_services"]
            checks_to_run = test["input"]["checks_to_run"]
            provider = test["input"]["provider"]
            assert (
                exclude_services_to_run(checks_to_run, excluded_services, provider)
                == test["expected"]
            )

lib/check/check.py METASEP
import importlib
from pkgutil import walk_packages
from types import ModuleType
from typing import Any

from colorama import Fore, Style

from config.config import groups_file
from lib.check.models import Output_From_Options, load_check_metadata
from lib.logger import logger
from lib.outputs.outputs import report
from lib.utils.utils import open_file, parse_json_file


# Load all checks metadata
def bulk_load_checks_metadata(provider: str) -> dict:
    bulk_check_metadata = {}
    checks = recover_checks_from_provider(provider)
    # Build list of check's metadata files
    for check_name in checks:
        # Build check path name
        check_path_name = check_name.replace(".", "/")
        # Append metadata file extension
        metadata_file = f"{check_path_name}.metadata.json"
        # Load metadata
        check_metadata = load_check_metadata(metadata_file)
        bulk_check_metadata[check_metadata.CheckID] = check_metadata

    return bulk_check_metadata


# Exclude checks to run
def exclude_checks_to_run(checks_to_execute: set, excluded_checks: list) -> set:
    for check in excluded_checks:
        checks_to_execute.discard(check)
    return checks_to_execute


# Exclude groups to run
def exclude_groups_to_run(
    checks_to_execute: set, excluded_groups: list, provider: str
) -> set:
    # Recover checks from the input groups
    available_groups = parse_groups_from_file(groups_file)
    checks_from_groups = load_checks_to_execute_from_groups(
        available_groups, excluded_groups, provider
    )
    for check_name in checks_from_groups:
        checks_to_execute.discard(check_name)
    return checks_to_execute


# Exclude services to run
def exclude_services_to_run(
    checks_to_execute: set, excluded_services: list, provider: str
) -> set:
    # Recover checks from the input services
    for service in excluded_services:
        modules = recover_checks_from_provider(provider, service)
        if not modules:
            logger.error(f"Service '{service}' was not found for the AWS provider")
        else:
            for check_module in modules:
                # Recover check name and module name from import path
                # Format: "providers.{provider}.services.{service}.{check_name}.{check_name}"
                check_name = check_module.split(".")[-1]
                # Exclude checks from the input services
                checks_to_execute.discard(check_name)
    return checks_to_execute


# Load checks from checklist.json
def parse_checks_from_file(input_file: str, provider: str) -> set:
    checks_to_execute = set()
    f = open_file(input_file)
    json_file = parse_json_file(f)

    for check_name in json_file[provider]:
        checks_to_execute.add(check_name)

    return checks_to_execute


def list_services(provider: str) -> set:
    available_services = set()
    checks = recover_checks_from_provider(provider)
    for check_name in checks:
        # Format: "providers.{provider}.services.{service}.{check_name}.{check_name}"
        service_name = check_name.split(".")[3]
        available_services.add(service_name)
    return available_services


def print_services(service_list: set):
    print(f"Available Services:")
    for service in service_list:
        print(f"- {service}")


def print_checks(provider: str, check_list: set, bulk_checks_metadata: dict):
    for check in check_list:
        try:
            print(
                f"[{bulk_checks_metadata[check].CheckID}] {bulk_checks_metadata[check].CheckTitle} - {Fore.MAGENTA}{bulk_checks_metadata[check].ServiceName} {Fore.YELLOW}[{bulk_checks_metadata[check].Severity}]{Style.RESET_ALL}"
            )
        except KeyError as error:
            logger.error(
                f"Check {error} was not found for the {provider.upper()} provider"
            )


# List available groups
def list_groups(provider: str):
    groups = parse_groups_from_file(groups_file)
    print(f"Available Groups:")

    for group, value in groups[provider].items():
        group_description = value["description"]
        print(f"\t - {group_description} -- [{group}] ")


# Parse groups from groups.json
def parse_groups_from_file(group_file: str) -> Any:
    f = open_file(group_file)
    available_groups = parse_json_file(f)
    return available_groups


# Parse checks from groups to execute
def load_checks_to_execute_from_groups(
    available_groups: Any, group_list: list, provider: str
) -> set:
    checks_to_execute = set()

    for group in group_list:
        if group in available_groups[provider]:
            for check_name in available_groups[provider][group]["checks"]:
                checks_to_execute.add(check_name)
        else:
            logger.error(
                f"Group '{group}' was not found for the {provider.upper()} provider"
            )
    return checks_to_execute


# Recover all checks from the selected provider and service
def recover_checks_from_provider(provider: str, service: str = None) -> list:
    checks = []
    modules = list_modules(provider, service)
    for module_name in modules:
        # Format: "providers.{provider}.services.{service}.{check_name}.{check_name}"
        check_name = module_name.name
        # We need to exclude common shared libraries in services
        if check_name.count(".") == 5 and "lib" not in check_name:
            checks.append(check_name)
    return checks


# List all available modules in the selected provider and service
def list_modules(provider: str, service: str):
    module_path = f"providers.{provider}.services"
    if service:
        module_path += f".{service}"
    return walk_packages(
        importlib.import_module(module_path).__path__,
        importlib.import_module(module_path).__name__ + ".",
    )


# Import an input check using its path
def import_check(check_path: str) -> ModuleType:
    lib = importlib.import_module(f"{check_path}")
    return lib


def set_output_options(
    quiet: bool,
    output_modes: list,
    input_output_directory: str,
    security_hub_enabled: bool,
):
    global output_options
    output_options = Output_From_Options(
        is_quiet=quiet,
        output_modes=output_modes,
        output_directory=input_output_directory,
        security_hub_enabled=security_hub_enabled
        # set input options here
    )
    return output_options


def run_check(check, audit_info, output_options):
    print(
        f"\nCheck ID: {check.checkID} - {Fore.MAGENTA}{check.serviceName}{Fore.YELLOW} [{check.severity}]{Style.RESET_ALL}"
    )
    logger.debug(f"Executing check: {check.checkID}")
    try:
        findings = check.execute()
    except Exception as error:
        print(f"Something went wrong in {check.checkID}, please use --log-level ERROR")
        logger.error(f"{check.checkID} -- {error.__class__.__name__}: {error}")
    else:
        report(findings, output_options, audit_info)
    finally:
        pass


def import_check(check_path: str) -> ModuleType:
    lib = importlib.import_module(f"{check_path}")
    return lib

lib/check/__init__.py METASEP

lib/logger.py METASEP
import logging

# Logging levels
logging_levels = {
    "CRITICAL": logging.CRITICAL,
    "ERROR": logging.ERROR,
    "WARNING": logging.WARNING,
    "INFO": logging.INFO,
    "DEBUG": logging.DEBUG,
}


def set_logging_config(log_file: str = None, log_level: str = "ERROR"):
    # Logs formatter
    stream_formatter = logging.Formatter(
        "%(asctime)s [File: %(filename)s:%(lineno)d] \t[Module: %(module)s]\t %(levelname)s: %(message)s"
    )
    log_file_formatter = logging.Formatter(
        '{"timestamp": "%(asctime)s", "filename": "%(filename)s:%(lineno)d", "level": "%(levelname)s", "module": "%(module)s", "message": "%(message)s"}'
    )

    # Where to put logs
    logging_handlers = []

    # Include stdout by default
    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(stream_formatter)
    logging_handlers.append(stream_handler)

    # Log to file configuration
    if log_file:
        # Set log to file handler
        log_file_handler = logging.FileHandler(log_file)
        log_file_handler.setFormatter(log_file_formatter)
        # Append the log formatter
        logging_handlers.append(log_file_handler)

    # Configure Logger
    # Initialize you log configuration using the base class
    # https://docs.python.org/3/library/logging.html#logrecord-attributes
    logging.basicConfig(
        level=logging_levels.get(log_level),
        handlers=logging_handlers,
        datefmt="%m/%d/%Y %I:%M:%S %p",
    )


# Retrieve the logger instance
logger = logging.getLogger()

lib/banner.py METASEP
from colorama import Fore, Style

from config.config import prowler_version, timestamp


def print_version():
    print(f"Prowler {prowler_version}")


def print_banner():
    banner = f"""{Fore.CYAN}                         _
 _ __  _ __ _____      _| | ___ _ __
| '_ \| '__/ _ \ \ /\ / / |/ _ \ '__|
| |_) | | | (_) \ V  V /| |  __/ |
| .__/|_|  \___/ \_/\_/ |_|\___|_|v{prowler_version}
|_|{Fore.BLUE} the handy cloud security tool

{Fore.YELLOW}Date: {timestamp.strftime("%Y-%m-%d %H:%M:%S")}{Style.RESET_ALL}

Color code for results:
 - {Fore.YELLOW}INFO (Information){Style.RESET_ALL}
 - {Fore.GREEN}PASS (Recommended value){Style.RESET_ALL}
 - {Fore.YELLOW}WARNING (Ignored by allowlist){Style.RESET_ALL}
 - {Fore.RED}FAIL (Fix required){Style.RESET_ALL}
"""
    print(banner)

lib/__init__.py METASEP

lib/outputs/outputs.py METASEP
import json
import os
from csv import DictWriter

from colorama import Fore, Style

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp,
    timestamp_utc,
)
from lib.outputs.models import (
    Check_Output_CSV,
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.utils.utils import file_exists, hash_sha512, open_file
from providers.aws.lib.security_hub import send_to_security_hub


def report(check_findings, output_options, audit_info):
    check_findings.sort(key=lambda x: x.region)

    csv_fields = []
    # check output options
    file_descriptors = {}
    if output_options.output_modes:
        if "csv" in output_options.output_modes:
            csv_fields = generate_csv_fields()

        file_descriptors = fill_file_descriptors(
            output_options.output_modes,
            audit_info.audited_account,
            output_options.output_directory,
            csv_fields,
        )

    if check_findings:
        for finding in check_findings:
            # Print findings by stdout
            color = set_report_color(finding.status)
            if output_options.is_quiet and "FAIL" in finding.status:
                print(
                    f"\t{color}{finding.status}{Style.RESET_ALL} {finding.region}: {finding.status_extended}"
                )
            elif not output_options.is_quiet:
                print(
                    f"\t{color}{finding.status}{Style.RESET_ALL} {finding.region}: {finding.status_extended}"
                )

            if file_descriptors:
                # sending the finding to input options
                if "csv" in file_descriptors:
                    finding_output = Check_Output_CSV(
                        audit_info.audited_account,
                        audit_info.profile,
                        finding,
                        audit_info.organizations_metadata,
                    )
                    csv_writer = DictWriter(
                        file_descriptors["csv"], fieldnames=csv_fields, delimiter=";"
                    )
                    csv_writer.writerow(finding_output.__dict__)

                if "json" in file_descriptors:
                    finding_output = Check_Output_JSON(**finding.check_metadata.dict())
                    fill_json(finding_output, audit_info, finding)

                    json.dump(finding_output.dict(), file_descriptors["json"], indent=4)
                    file_descriptors["json"].write(",")

                if "json-asff" in file_descriptors:
                    finding_output = Check_Output_JSON_ASFF()
                    fill_json_asff(finding_output, audit_info, finding)

                    json.dump(
                        finding_output.dict(), file_descriptors["json-asff"], indent=4
                    )
                    file_descriptors["json-asff"].write(",")

                # Check if it is needed to send findings to security hub
                if output_options.security_hub_enabled:
                    send_to_security_hub(
                        finding.region, finding_output, audit_info.audit_session
                    )
    else:  # No service resources in the whole account
        color = set_report_color("WARNING")
        if not output_options.is_quiet:
            print(f"\t{color}INFO{Style.RESET_ALL} There are no resources")

    if file_descriptors:
        # Close all file descriptors
        for file_descriptor in file_descriptors:
            file_descriptors.get(file_descriptor).close()


def fill_file_descriptors(output_modes, audited_account, output_directory, csv_fields):
    file_descriptors = {}
    for output_mode in output_modes:
        if output_mode == "csv":
            filename = (
                f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}"
            )
            if file_exists(filename):
                file_descriptor = open_file(
                    filename,
                    "a",
                )
            else:
                file_descriptor = open_file(
                    filename,
                    "a",
                )
                csv_header = [x.upper() for x in csv_fields]
                csv_writer = DictWriter(
                    file_descriptor, fieldnames=csv_header, delimiter=";"
                )
                csv_writer.writeheader()

            file_descriptors.update({output_mode: file_descriptor})

        if output_mode == "json":
            filename = f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}"
            if file_exists(filename):
                file_descriptor = open_file(
                    filename,
                    "a",
                )
            else:
                file_descriptor = open_file(
                    filename,
                    "a",
                )
                file_descriptor.write("[")

            file_descriptors.update({output_mode: file_descriptor})

        if output_mode == "json-asff":
            filename = f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}"
            if file_exists(filename):
                file_descriptor = open_file(
                    filename,
                    "a",
                )
            else:
                file_descriptor = open_file(
                    filename,
                    "a",
                )
                file_descriptor.write("[")

            file_descriptors.update({output_mode: file_descriptor})

    return file_descriptors


def set_report_color(status):
    color = ""
    if status == "PASS":
        color = Fore.GREEN
    elif status == "FAIL":
        color = Fore.RED
    elif status == "ERROR":
        color = Fore.BLACK
    elif status == "WARNING":
        color = Fore.YELLOW
    else:
        raise Exception("Invalid Report Status. Must be PASS, FAIL, ERROR or WARNING")
    return color


def generate_csv_fields():
    csv_fields = []
    for field in Check_Output_CSV.__dict__["__annotations__"].keys():
        csv_fields.append(field)
    return csv_fields


def fill_json(finding_output, audit_info, finding):
    finding_output.AssessmentStartTime = timestamp.isoformat()
    finding_output.FindingUniqueId = ""
    finding_output.Profile = audit_info.profile
    finding_output.AccountId = audit_info.audited_account
    if audit_info.organizations_metadata:
        finding_output.OrganizationsInfo = audit_info.organizations_metadata.__dict__
    finding_output.Region = finding.region
    finding_output.Status = finding.status
    finding_output.StatusExtended = finding.status_extended
    finding_output.ResourceId = finding.resource_id
    finding_output.ResourceArn = finding.resource_arn
    finding_output.ResourceDetails = finding.resource_details

    return finding_output


def fill_json_asff(finding_output, audit_info, finding):
    # Check if there are no resources in the finding
    if finding.resource_id == "":
        finding.resource_id = "NONE_PROVIDED"
    finding_output.Id = f"prowler-{finding.check_metadata.CheckID}-{audit_info.audited_account}-{finding.region}-{hash_sha512(finding.resource_id)}"
    finding_output.ProductArn = f"arn:{audit_info.audited_partition}:securityhub:{finding.region}::product/prowler/prowler"
    finding_output.ProductFields = ProductFields(
        ProviderVersion=prowler_version, ProwlerResourceName=finding.resource_id
    )
    finding_output.GeneratorId = "prowler-" + finding.check_metadata.CheckID
    finding_output.AwsAccountId = audit_info.audited_account
    finding_output.Types = [finding.check_metadata.CheckType]
    finding_output.FirstObservedAt = (
        finding_output.UpdatedAt
    ) = finding_output.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
    finding_output.Severity = Severity(Label=finding.check_metadata.Severity.upper())
    finding_output.Title = finding.check_metadata.CheckTitle
    finding_output.Description = finding.check_metadata.Description
    finding_output.Resources = [
        Resource(
            Id=finding.resource_id,
            Type=finding.check_metadata.ResourceType,
            Partition=audit_info.audited_partition,
            Region=finding.region,
        )
    ]
    # Add ED to PASS or FAIL (PASSED/FAILED)
    finding_output.Compliance = Compliance(
        Status=finding.status + "ED",
        RelatedRequirements=[finding.check_metadata.CheckType],
    )
    finding_output.Remediation = {
        "Recommendation": finding.check_metadata.Remediation.Recommendation
    }

    return finding_output


def close_json(output_directory, audited_account, mode):
    suffix = json_file_suffix
    if mode == "json-asff":
        suffix = json_asff_file_suffix
    filename = f"{output_directory}/prowler-output-{audited_account}-{suffix}"
    file_descriptor = open_file(
        filename,
        "a",
    )
    # Replace last comma for square bracket
    file_descriptor.seek(file_descriptor.tell() - 1, os.SEEK_SET)
    file_descriptor.truncate()
    file_descriptor.write("]")
    file_descriptor.close()

lib/outputs/models.py METASEP
from dataclasses import asdict, dataclass
from typing import List, Optional

from pydantic import BaseModel

from config.config import timestamp
from lib.check.models import Check_Report, ComplianceItem, Remediation
from providers.aws.models import AWS_Organizations_Info


@dataclass
class Compliance_Framework:
    Framework: str
    Version: str
    Group: list
    Control: list


class Check_Output_JSON(BaseModel):
    AssessmentStartTime: str = ""
    FindingUniqueId: str = ""
    Provider: str
    Profile: str = ""
    AccountId: str = ""
    OrganizationsInfo: Optional[AWS_Organizations_Info]
    Region: str = ""
    CheckID: str
    # CheckName: str
    CheckTitle: str
    CheckType: str
    ServiceName: str
    SubServiceName: str
    Status: str = ""
    StatusExtended: str = ""
    Severity: str
    ResourceId: str = ""
    ResourceArn: str = ""
    ResourceType: str
    ResourceDetails: str = ""
    Tags: dict
    Description: str
    Risk: str
    RelatedUrl: str
    Remediation: Remediation
    Categories: List[str]
    DependsOn: List[str]
    RelatedTo: List[str]
    Notes: str
    Compliance: List[ComplianceItem]


# JSON ASFF Output
class ProductFields(BaseModel):
    ProviderName: str = "Prowler"
    ProviderVersion: str
    ProwlerResourceName: str


class Severity(BaseModel):
    Label: str


class Resource(BaseModel):
    Type: str
    Id: str
    Partition: str
    Region: str


class Compliance(BaseModel):
    Status: str
    RelatedRequirements: List[str]


class Check_Output_JSON_ASFF(BaseModel):
    SchemaVersion: str = "2018-10-08"
    Id: str = ""
    ProductArn: str = ""
    RecordState: str = "ACTIVE"
    ProductFields: ProductFields = None
    GeneratorId: str = ""
    AwsAccountId: str = ""
    Types: List[str] = None
    FirstObservedAt: str = ""
    UpdatedAt: str = ""
    CreatedAt: str = ""
    Severity: Severity = None
    Title: str = ""
    Description: str = ""
    Resources: List[Resource] = None
    Compliance: Compliance = None
    Remediation: dict = None


@dataclass
class Check_Output_CSV:
    assessment_start_time: str
    finding_unique_id: str
    provider: str
    profile: str
    account_id: int
    account_name: str
    account_email: str
    account_arn: str
    account_org: str
    account_tags: str
    region: str
    check_id: str
    # check_name: str
    check_title: str
    check_type: str
    status: str
    status_extended: str
    service_name: str
    subservice_name: str
    severity: str
    resource_id: str
    resource_arn: str
    resource_type: str
    resource_details: str
    resource_tags: list
    description: dict
    risk: list
    related_url: list
    remediation_recommendation_text: str
    remediation_recommendation_url: list
    remediation_recommendation_code_nativeiac: str
    remediation_recommendation_code_terraform: str
    remediation_recommendation_code_cli: str
    remediation_recommendation_code_other: str
    categories: str
    depends_on: str
    related_to: str
    notes: str
    compliance: str

    def get_csv_header(self):
        csv_header = []
        for key in asdict(self):
            csv_header = csv_header.append(key)
        return csv_header

    def __init__(
        self,
        account: str,
        profile: str,
        report: Check_Report,
        organizations: AWS_Organizations_Info,
    ):
        self.assessment_start_time = timestamp.isoformat()
        self.finding_unique_id = ""
        self.provider = report.check_metadata.Provider
        self.profile = profile
        self.account_id = account
        if organizations:
            self.account_name = organizations.account_details_name
            self.account_email = organizations.account_details_email
            self.account_arn = organizations.account_details_arn
            self.account_org = organizations.account_details_org
            self.account_tags = organizations.account_details_tags
        self.region = report.region
        self.check_id = report.check_metadata.CheckID
        # self.check_name = report.check_metadata.CheckName
        self.check_title = report.check_metadata.CheckTitle
        self.check_type = report.check_metadata.CheckType
        self.status = report.status
        self.status_extended = report.status_extended
        self.service_name = report.check_metadata.ServiceName
        self.subservice_name = report.check_metadata.SubServiceName
        self.severity = report.check_metadata.Severity
        self.resource_id = report.resource_id
        self.resource_arn = report.resource_arn
        self.resource_type = report.check_metadata.ResourceType
        self.resource_details = report.resource_details
        self.resource_tags = report.resource_tags
        self.description = report.check_metadata.Description
        self.risk = report.check_metadata.Risk
        self.related_url = report.check_metadata.RelatedUrl
        self.remediation_recommendation_text = (
            report.check_metadata.Remediation.Recommendation.Text
        )
        self.remediation_recommendation_url = (
            report.check_metadata.Remediation.Recommendation.Url
        )
        self.remediation_recommendation_code_nativeiac = (
            report.check_metadata.Remediation.Code.NativeIaC
        )
        self.remediation_recommendation_code_terraform = (
            report.check_metadata.Remediation.Code.Terraform
        )
        self.remediation_recommendation_code_cli = (
            report.check_metadata.Remediation.Code.CLI
        )
        self.remediation_recommendation_code_other = (
            report.check_metadata.Remediation.Code.Other
        )
        self.categories = self.__unroll_list__(report.check_metadata.Categories)
        self.depends_on = self.__unroll_list__(report.check_metadata.DependsOn)
        self.related_to = self.__unroll_list__(report.check_metadata.RelatedTo)
        self.notes = report.check_metadata.Notes
        self.compliance = self.__unroll_compliance__(report.check_metadata.Compliance)

    def __unroll_list__(self, listed_items: list):
        unrolled_items = ""
        separator = "|"
        for item in listed_items:
            if not unrolled_items:
                unrolled_items = f"{item}"
            else:
                unrolled_items = f"{unrolled_items}{separator}{item}"

        return unrolled_items

    def __unroll_dict__(self, dict_items: dict):
        unrolled_items = ""
        separator = "|"
        for key, value in dict_items.items():
            unrolled_item = f"{key}:{value}"
            if not unrolled_items:
                unrolled_items = f"{unrolled_item}"
            else:
                unrolled_items = f"{unrolled_items}{separator}{unrolled_item}"

        return unrolled_items

    def __unroll_compliance__(self, compliance: list):
        compliance_frameworks = []
        # fill list of dataclasses
        for item in compliance:
            compliance_framework = Compliance_Framework(
                Framework=item.Framework,
                Version=item.Version,
                Group=item.Group,
                Control=item.Control,
            )
            compliance_frameworks.append(compliance_framework)
        # iterate over list of dataclasses to output info
        unrolled_compliance = ""
        groups = ""
        controls = ""
        item_separator = ","
        framework_separator = "|"
        generic_separator = "/"
        for framework in compliance_frameworks:
            for group in framework.Group:
                if groups:
                    groups = f"{groups}{generic_separator}"
                groups = f"{groups}{group}"
            for control in framework.Control:
                if controls:
                    controls = f"{controls}{generic_separator}"
                controls = f"{controls}{control}"

            if unrolled_compliance:
                unrolled_compliance = f"{unrolled_compliance}{framework_separator}"
            unrolled_compliance = f"{unrolled_compliance}{framework.Framework}{item_separator}{framework.Version}{item_separator}{groups}{item_separator}{controls}"
            # unset groups and controls for next framework
            controls = ""
            groups = ""

        return unrolled_compliance

lib/outputs/outputs_test.py METASEP

from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
        expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"
        expected.ProductArn = f"arn:aws:securityhub:eu-west-1::product/prowler/prowler"
        expected.ProductFields = ProductFields(
            ProviderVersion=prowler_version, ProwlerResourceName="test-resource"
        )
        expected.GeneratorId = "prowler-" + finding.check_metadata.CheckID
        expected.AwsAccountId = "123456789012"
        expected.Types = [finding.check_metadata.CheckType]
        expected.FirstObservedAt = (
            expected.UpdatedAt
        ) = expected.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
        expected.Severity = Severity(Label=finding.check_metadata.Severity.upper())
        expected.Title = finding.check_metadata.CheckTitle
        expected.Description = finding.check_metadata.Description
        expected.Resources = [
            Resource(
                Id="test-resource",
                Type=finding.check_metadata.ResourceType,
                Partition="aws",
                Region="eu-west-1",
            )
        ]

        expected.Compliance = Compliance(
            Status="PASS" + "ED",
            RelatedRequirements=[finding.check_metadata.CheckType],
        )
        expected.Remediation = {
            "Recommendation": finding.check_metadata.Remediation.Recommendation
        }

inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
        expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"
        expected.ProductArn = f"arn:aws:securityhub:eu-west-1::product/prowler/prowler"
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
        expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"
        expected.ProductArn = f"arn:aws:securityhub:eu-west-1::product/prowler/prowler"
        expected.ProductFields = ProductFields(
            ProviderVersion=prowler_version, ProwlerResourceName="test-resource"
        )
        expected.GeneratorId = "prowler-" + finding.check_metadata.CheckID
        expected.AwsAccountId = "123456789012"
        expected.Types = [finding.check_metadata.CheckType]
        expected.FirstObservedAt = (
            expected.UpdatedAt
        ) = expected.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
        expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"
        expected.ProductArn = f"arn:aws:securityhub:eu-west-1::product/prowler/prowler"
        expected.ProductFields = ProductFields(
            ProviderVersion=prowler_version, ProwlerResourceName="test-resource"
        )
        expected.GeneratorId = "prowler-" + finding.check_metadata.CheckID
        expected.AwsAccountId = "123456789012"
        expected.Types = [finding.check_metadata.CheckType]
        expected.FirstObservedAt = (
            expected.UpdatedAt
        ) = expected.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
        expected.Severity = Severity(Label=finding.check_metadata.Severity.upper())
        expected.Title = finding.check_metadata.CheckTitle
        expected.Description = finding.check_metadata.Description
        expected.Resources = [
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
        expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"
        expected.ProductArn = f"arn:aws:securityhub:eu-west-1::product/prowler/prowler"
        expected.ProductFields = ProductFields(
            ProviderVersion=prowler_version, ProwlerResourceName="test-resource"
        )
        expected.GeneratorId = "prowler-" + finding.check_metadata.CheckID
        expected.AwsAccountId = "123456789012"
        expected.Types = [finding.check_metadata.CheckType]
        expected.FirstObservedAt = (
            expected.UpdatedAt
        ) = expected.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
        expected.Severity = Severity(Label=finding.check_metadata.Severity.upper())
        expected.Title = finding.check_metadata.CheckTitle
        expected.Description = finding.check_metadata.Description
        expected.Resources = [
            Resource(
                Id="test-resource",
                Type=finding.check_metadata.ResourceType,
                Partition="aws",
                Region="eu-west-1",
            )
        ]

inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
        expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"
        expected.ProductArn = f"arn:aws:securityhub:eu-west-1::product/prowler/prowler"
        expected.ProductFields = ProductFields(
            ProviderVersion=prowler_version, ProwlerResourceName="test-resource"
        )
        expected.GeneratorId = "prowler-" + finding.check_metadata.CheckID
        expected.AwsAccountId = "123456789012"
        expected.Types = [finding.check_metadata.CheckType]
        expected.FirstObservedAt = (
            expected.UpdatedAt
        ) = expected.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
        expected.Severity = Severity(Label=finding.check_metadata.Severity.upper())
        expected.Title = finding.check_metadata.CheckTitle
        expected.Description = finding.check_metadata.Description
        expected.Resources = [
            Resource(
                Id="test-resource",
                Type=finding.check_metadata.ResourceType,
                Partition="aws",
                Region="eu-west-1",
            )
        ]

        expected.Compliance = Compliance(
            Status="PASS" + "ED",
            RelatedRequirements=[finding.check_metadata.CheckType],
        )
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
        expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"
        expected.ProductArn = f"arn:aws:securityhub:eu-west-1::product/prowler/prowler"
        expected.ProductFields = ProductFields(
            ProviderVersion=prowler_version, ProwlerResourceName="test-resource"
        )
        expected.GeneratorId = "prowler-" + finding.check_metadata.CheckID
        expected.AwsAccountId = "123456789012"
        expected.Types = [finding.check_metadata.CheckType]
        expected.FirstObservedAt = (
            expected.UpdatedAt
        ) = expected.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
        expected.Severity = Severity(Label=finding.check_metadata.Severity.upper())
        expected.Title = finding.check_metadata.CheckTitle
        expected.Description = finding.check_metadata.Description
        expected.Resources = [
            Resource(
                Id="test-resource",
                Type=finding.check_metadata.ResourceType,
                Partition="aws",
                Region="eu-west-1",
            )
        ]

        expected.Compliance = Compliance(
            Status="PASS" + "ED",
            RelatedRequirements=[finding.check_metadata.CheckType],
        )
        expected.Remediation = {
inproject
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
common
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

common
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
common
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


non_informative
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
non_informative
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
non_informative
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON_ASFF()

        expected = Check_Output_JSON_ASFF()
        expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"
        expected.ProductArn = f"arn:aws:securityhub:eu-west-1::product/prowler/prowler"
        expected.ProductFields = ProductFields(
            ProviderVersion=prowler_version, ProwlerResourceName="test-resource"
        )
        expected.GeneratorId = "prowler-" + finding.check_metadata.CheckID
        expected.AwsAccountId = "123456789012"
        expected.Types = [finding.check_metadata.CheckType]
        expected.FirstObservedAt = (
            expected.UpdatedAt
        ) = expected.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
        expected.Severity = Severity(Label=finding.check_metadata.Severity.upper())
        expected.Title = finding.check_metadata.CheckTitle
        expected.Description = finding.check_metadata.Description
        expected.Resources = [
            Resource(
                Id="test-resource",
                Type=finding.check_metadata.ResourceType,
                Partition="aws",
                Region="eu-west-1",
            )
        ]

        expected.Compliance = Compliance(
            Status="PASS" + "ED",
            RelatedRequirements=[finding.check_metadata.CheckType],
        )
        expected.Remediation = {
            "Recommendation": finding.check_metadata.Remediation.Recommendation
        }

        assert fill_json_asff(input, input_audit_info, finding) == expected
non_informative
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                ),
            },
        ]

        for index, output_mode_list in enumerate(test_output_modes):
            test_output_file_descriptors = fill_file_descriptors(
                output_mode_list, audited_account, output_directory, csv_fields
            )
            for output_mode in output_mode_list:
                assert (
                    test_output_file_descriptors[output_mode].name
                    == expected[index][output_mode].name
                )
                remove(expected[index][output_mode].name)

    def test_set_report_color(self):
        test_status = ["PASS", "FAIL", "ERROR", "WARNING"]
        test_colors = [Fore.GREEN, Fore.RED, Fore.BLACK, Fore.YELLOW]

        for status in test_status:
            assert set_report_color(status) in test_colors

    def test_generate_csv_fields(self):
        expected = [
            "assessment_start_time",
            "finding_unique_id",
            "provider",
            "profile",
            "account_id",
            "account_name",
            "account_email",
            "account_arn",
            "account_org",
            "account_tags",
            "region",
            "check_id",
            "check_title",
            "check_type",
            "status",
            "status_extended",
            "service_name",
            "subservice_name",
            "severity",
            "resource_id",
            "resource_arn",
            "resource_type",
            "resource_details",
            "resource_tags",
            "description",
            "risk",
            "related_url",
            "remediation_recommendation_text",
            "remediation_recommendation_url",
            "remediation_recommendation_code_nativeiac",
            "remediation_recommendation_code_terraform",
            "remediation_recommendation_code_cli",
            "remediation_recommendation_code_other",
            "categories",
            "depends_on",
            "related_to",
            "notes",
            "compliance",
        ]

        assert generate_csv_fields() == expected

    def test_fill_json(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
            credentials=None,
            assumed_role_info=None,
            audited_regions=["eu-west-2", "eu-west-1"],
            organizations_metadata=None,
        )
        finding = Check_Report(
            load_check_metadata(
                f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json"
            )
        )
        finding.resource_details = "Test resource details"
        finding.resource_id = "test-resource"
        finding.resource_arn = "test-arn"
        finding.region = "eu-west-1"
        finding.status = "PASS"
        finding.status_extended = "This is a test"

        input = Check_Output_JSON(**finding.check_metadata.dict())

        expected = Check_Output_JSON(**finding.check_metadata.dict())
        expected.AssessmentStartTime = timestamp_iso
        expected.FindingUniqueId = ""
        expected.Profile = "default"
        expected.AccountId = "123456789012"
        expected.OrganizationsInfo = None
        expected.Region = "eu-west-1"
        expected.Status = "PASS"
        expected.StatusExtended = "This is a test"
        expected.ResourceId = "test-resource"
        expected.ResourceArn = "test-arn"
        expected.ResourceDetails = "Test resource details"

        assert fill_json(input, input_audit_info, finding) == expected

    def test_fill_json_asff(self):
        input_audit_info = AWS_Audit_Info(
            original_session=None,
            audit_session=None,
            audited_account="123456789012",
            audited_identity_arn="test-arn",
            audited_user_id="test",
            audited_partition="aws",
            profile="default",
            profile_region="eu-west-1",
random
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
                )
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
            },
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                ),
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                ),
                "json-asff": open_file(
random
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
            {
                "json": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_file_suffix}",
                    "a",
                )
            },
            {
                "json-asff": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",
                    "a",
random
from os import path, remove

from colorama import Fore

from config.config import (
    csv_file_suffix,
    json_asff_file_suffix,
    json_file_suffix,
    prowler_version,
    timestamp_iso,
    timestamp_utc,
)
from lib.check.models import Check_Report, load_check_metadata
from lib.outputs.models import (
    Check_Output_JSON,
    Check_Output_JSON_ASFF,
    Compliance,
    ProductFields,
    Resource,
    Severity,
)
from lib.outputs.outputs import (
    fill_file_descriptors,
    fill_json,
    fill_json_asff,
    generate_csv_fields,
    set_report_color,
)
from lib.utils.utils import hash_sha512, open_file
from providers.aws.models import AWS_Audit_Info


class Test_Outputs:
    def test_fill_file_descriptors(self):
        audited_account = "123456789012"
        output_directory = "."
        csv_fields = generate_csv_fields()
        test_output_modes = [
            ["csv"],
            ["json"],
            ["json-asff"],
            ["csv", "json"],
            ["csv", "json", "json-asff"],
        ]

        expected = [
            {
                "csv": open_file(
                    f"{output_directory}/prowler-output-{audited_account}-{csv_file_suffix}",
                    "a",
                )
            },
random
        assert fill_json_asff(input, input_audit_info, finding) == expected     Check_Output_JSON,     Check_Output_JSON_ASFF,     Compliance,     ProductFields,     Resource,     Severity,     fill_file_descriptors,     fill_json,     fill_json_asff,         input_audit_info = AWS_Audit_Info(     generate_csv_fields,     set_report_color,         assert generate_csv_fields() == expected         csv_fields = generate_csv_fields()         finding = Check_Report(             load_check_metadata(                 "csv": open_file(         input = Check_Output_JSON(**finding.check_metadata.dict())                 "json": open_file(         expected = Check_Output_JSON(**finding.check_metadata.dict())                 "json-asff": open_file(         assert fill_json(input, input_audit_info, finding) == expected             test_output_file_descriptors = fill_file_descriptors(         input = Check_Output_JSON_ASFF()         expected = Check_Output_JSON_ASFF()         expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}"         expected.ProductFields = ProductFields(             assert set_report_color(status) in test_colors         expected.Severity = Severity(Label=finding.check_metadata.Severity.upper())             Resource(         expected.Compliance = Compliance(         expected.Remediation = {             "Recommendation": finding.check_metadata.Remediation.Recommendation                     == expected[index][output_mode].name         for index, output_mode_list in enumerate(test_output_modes):                 remove(expected[index][output_mode].name) class Test_Outputs:     prowler_version, )             credentials=None,                     f"{output_directory}/prowler-output-{audited_account}-{json_asff_file_suffix}",                 )             {