├── .coveragerc ├── .gitignore ├── .travis.yml ├── AWSScout2 ├── __init__.py ├── __listall__.py ├── __main__.py ├── __rules_generator__.py ├── cli_parser.py ├── configs │ ├── __init__.py │ ├── base.py │ ├── browser.py │ ├── data │ │ └── metadata.json │ ├── regions.py │ ├── scout2.py │ ├── services.py │ ├── threads.py │ └── vpc.py ├── data │ └── requirements.txt ├── output │ ├── __init__.py │ ├── console.py │ ├── data │ │ ├── html │ │ │ ├── partials │ │ │ │ ├── accordion.html │ │ │ │ ├── accordion_policy.html │ │ │ │ ├── dashboard.html │ │ │ │ ├── details.html │ │ │ │ ├── details_for_region.html │ │ │ │ ├── details_for_vpc.html │ │ │ │ ├── ec2_grants.html │ │ │ │ ├── filters.html │ │ │ │ ├── generic_object.html │ │ │ │ ├── ip_grants.html │ │ │ │ ├── left_menu.html │ │ │ │ ├── left_menu_for_region.html │ │ │ │ ├── left_menu_for_vpc.html │ │ │ │ ├── metadata.html │ │ │ │ ├── network_interface.html │ │ │ │ ├── policy.html │ │ │ │ ├── regionfilters.html │ │ │ │ ├── resource_link.html │ │ │ │ ├── services.awslambda.regions.id.functions.html │ │ │ │ ├── services.cloudformation.regions.id.stacks.html │ │ │ │ ├── services.cloudtrail.regions.html │ │ │ │ ├── services.cloudtrail.regions.id.trails.html │ │ │ │ ├── services.cloudwatch.regions.id.alarms.html │ │ │ │ ├── services.ec2.regions.id.volumes.html │ │ │ │ ├── services.ec2.regions.id.vpcs.id.instances.html │ │ │ │ ├── services.ec2.regions.id.vpcs.id.security_groups.html │ │ │ │ ├── services.ec2.regions.vpcs.security_groups.resource_list.html │ │ │ │ ├── services.ec2.regions.vpcs.security_groups.rule_list.html │ │ │ │ ├── services.elasticache.regions.id.vpcs.id.clusters.html │ │ │ │ ├── services.elb.regions.id.elb_policies.html │ │ │ │ ├── services.elb.regions.id.vpcs.id.elbs.html │ │ │ │ ├── services.elb.regions.id.vpcs.id.elbs.linked_resources.html │ │ │ │ ├── services.elb.regions.id.vpcs.id.elbs.listener.html │ │ │ │ ├── services.elb.regions.id.vpcsid.elbs.linked_policy.html │ │ │ │ ├── services.elbv2.regions.id.vpcs.id.lbs.html │ │ │ │ ├── services.emr.regions.id.vpcs.id.clusters.html │ │ │ │ ├── services.iam.groups.html │ │ │ │ ├── services.iam.inline_policies.html │ │ │ │ ├── services.iam.managed_policies.html │ │ │ │ ├── services.iam.managed_policies_list.html │ │ │ │ ├── services.iam.roles.html │ │ │ │ ├── services.iam.users.html │ │ │ │ ├── services.rds.regions.id.parameter_groups.html │ │ │ │ ├── services.rds.regions.id.security_groups.html │ │ │ │ ├── services.rds.regions.id.vpcs.id.instances.html │ │ │ │ ├── services.rds.regions.id.vpcs.id.snapshots.html │ │ │ │ ├── services.rds.regions.id.vpcs.id.subnet_groups.html │ │ │ │ ├── services.redshift.regions.id.parameter_groups.html │ │ │ │ ├── services.redshift.regions.id.vpcs.id.clusters.html │ │ │ │ ├── services.redshift.regions.id.vpcs.id.security_groups.html │ │ │ │ ├── services.redshift.regions.vpcs.cluster_nodes.html │ │ │ │ ├── services.route53.domains.html │ │ │ │ ├── services.route53.hosted_zones.html │ │ │ │ ├── services.s3.acls.html │ │ │ │ ├── services.s3.bucket_iam_policies.html │ │ │ │ ├── services.s3.buckets.html │ │ │ │ ├── services.s3.buckets.objects.html │ │ │ │ ├── services.ses.regions.id.identities.html │ │ │ │ ├── services.sns.regions.id.topics.html │ │ │ │ ├── services.sqs.regions.id.queues.html │ │ │ │ ├── services.vpc.regions.id.peering_connections.html │ │ │ │ ├── services.vpc.regions.id.vpcs.html │ │ │ │ ├── services.vpc.regions.id.vpcs.id.flow_logs.html │ │ │ │ ├── services.vpc.regions.id.vpcs.id.network_acls.html │ │ │ │ ├── services.vpc.regions.id.vpcs.id.peering_connections.html │ │ │ │ ├── services.vpc.regions.id.vpcs.id.subnets.html │ │ │ │ └── singles.html │ │ │ ├── report.html │ │ │ ├── ruleset-generator.html │ │ │ └── summaries │ │ │ │ ├── attack_surface.html │ │ │ │ ├── service_groups.compute.summaries.external_attack_surface.html │ │ │ │ ├── service_groups.database.summaries.external_attack_surface.html │ │ │ │ ├── services.ec2.external_attack_surface.html │ │ │ │ ├── services.elb.external_attack_surface.html │ │ │ │ ├── services.elbv2.external_attack_surface.html │ │ │ │ ├── services.iam.credential_report.root_account.html │ │ │ │ ├── services.iam.password_policy.html │ │ │ │ ├── services.iam.permissions.html │ │ │ │ ├── services.rds.external_attack_surface.html │ │ │ │ └── services.redshift.external_attack_surface.html │ │ ├── inc-scout2 │ │ │ ├── helpers.js │ │ │ ├── ruleset-generator.js │ │ │ ├── scout2.css │ │ │ └── scout2.js │ │ ├── includes.zip │ │ └── listall-configs │ │ │ └── ec2.regions.id.vpcs.id.security_groups.id.json │ ├── html.py │ ├── js.py │ └── utils.py ├── rules │ ├── __init__.py │ ├── data │ │ ├── conditions │ │ │ ├── cidr-is-all.json │ │ │ ├── ec2-security-group-in-use.json │ │ │ ├── ec2-security-group-not-used.json │ │ │ ├── instance-with-open-nacls.json │ │ │ ├── instance-with-public-ip.json │ │ │ ├── ip-not-in-private-space.json │ │ │ ├── policy-statement-any-principal.json │ │ │ ├── policy-statement-poor-condition.json │ │ │ └── security-group-opens-all-ports.json │ │ ├── filters │ │ │ ├── ec2-instance-with-open-nacls.json │ │ │ ├── ec2-security-group-with-public-cidr-grant.json │ │ │ ├── iam-role-for-aws-account.json │ │ │ └── iam-role-for-service.json │ │ ├── findings │ │ │ ├── cloudformation-stack-with-role.json │ │ │ ├── cloudtrail-duplicated-global-services-logging.json │ │ │ ├── cloudtrail-no-data-logging.json │ │ │ ├── cloudtrail-no-global-services-logging.json │ │ │ ├── cloudtrail-no-log-file-validation.json │ │ │ ├── cloudtrail-no-logging.json │ │ │ ├── cloudtrail-not-configured.json │ │ │ ├── cloudwatch-alarm-without-actions.json │ │ │ ├── ec2-default-security-group-in-use.json │ │ │ ├── ec2-default-security-group-with-rules.json │ │ │ ├── ec2-ebs-volume-not-encrypted.json │ │ │ ├── ec2-instance-in-security-group.json │ │ │ ├── ec2-instance-type.json │ │ │ ├── ec2-instance-types.json │ │ │ ├── ec2-instance-with-public-ip.json │ │ │ ├── ec2-security-group-opens-all-ports-to-all.json │ │ │ ├── ec2-security-group-opens-all-ports-to-self.json │ │ │ ├── ec2-security-group-opens-all-ports.json │ │ │ ├── ec2-security-group-opens-known-port-to-all.json │ │ │ ├── ec2-security-group-opens-plaintext-port.json │ │ │ ├── ec2-security-group-opens-port-range.json │ │ │ ├── ec2-security-group-opens-port-to-all.json │ │ │ ├── ec2-security-group-whitelists-aws-ip-from-banned-region.json │ │ │ ├── ec2-security-group-whitelists-aws.json │ │ │ ├── ec2-security-group-whitelists-non-elastic-ips.json │ │ │ ├── ec2-security-group-whitelists-unknown-aws.json │ │ │ ├── ec2-security-group-whitelists-unknown-cidrs.json │ │ │ ├── ec2-unused-security-group.json │ │ │ ├── elb-no-access-logs.json │ │ │ ├── elbv2-no-access-logs.json │ │ │ ├── elbv2-no-deletion-protection.json │ │ │ ├── elbv2-older-ssl-policy.json │ │ │ ├── iam-assume-role-lacks-external-id-and-mfa.json │ │ │ ├── iam-assume-role-no-mfa.json │ │ │ ├── iam-assume-role-policy-allows-all.json │ │ │ ├── iam-ec2-role-without-instances.json │ │ │ ├── iam-group-with-inline-policies.json │ │ │ ├── iam-group-with-no-users.json │ │ │ ├── iam-human-user-with-policies.json │ │ │ ├── iam-inline-policy-allows-NotActions.json │ │ │ ├── iam-inline-policy-allows-non-sts-action.json │ │ │ ├── iam-inline-policy-for-role.json │ │ │ ├── iam-managed-policy-allows-NotActions.json │ │ │ ├── iam-managed-policy-allows-non-sts-action.json │ │ │ ├── iam-managed-policy-for-role.json │ │ │ ├── iam-managed-policy-no-attachments.json │ │ │ ├── iam-password-policy-expiration-threshold.json │ │ │ ├── iam-password-policy-lowercase-required.json │ │ │ ├── iam-password-policy-minimum-length.json │ │ │ ├── iam-password-policy-no-expiration.json │ │ │ ├── iam-password-policy-no-lowercase-required.json │ │ │ ├── iam-password-policy-no-number-required.json │ │ │ ├── iam-password-policy-no-symbol-required.json │ │ │ ├── iam-password-policy-no-uppercase-required.json │ │ │ ├── iam-password-policy-reuse-enabled.json │ │ │ ├── iam-role-with-inline-policies.json │ │ │ ├── iam-root-account-no-mfa.json │ │ │ ├── iam-root-account-used-recently.json │ │ │ ├── iam-root-account-with-active-certs.json │ │ │ ├── iam-root-account-with-active-keys.json │ │ │ ├── iam-service-user-with-password.json │ │ │ ├── iam-user-no-key-rotation.json │ │ │ ├── iam-user-not-in-category-group.json │ │ │ ├── iam-user-not-in-common-group.json │ │ │ ├── iam-user-with-multiple-access-keys.json │ │ │ ├── iam-user-with-password-and-key.json │ │ │ ├── iam-user-with-policies.json │ │ │ ├── iam-user-without-mfa.json │ │ │ ├── rds-instance-backup-disabled.json │ │ │ ├── rds-instance-no-minor-upgrade.json │ │ │ ├── rds-instance-short-backup-retention-period.json │ │ │ ├── rds-instance-single-az.json │ │ │ ├── rds-instance-storage-not-encrypted.json │ │ │ ├── rds-postgres-instance-with-invalid-certificate.json │ │ │ ├── rds-security-group-allows-all.json │ │ │ ├── rds-snapshot-public.json │ │ │ ├── redshift-cluster-database-not-encrypted.json │ │ │ ├── redshift-cluster-no-version-upgrade.json │ │ │ ├── redshift-cluster-publicly-accessible.json │ │ │ ├── redshift-parameter-group-logging-disabled.json │ │ │ ├── redshift-parameter-group-ssl-not-required.json │ │ │ ├── redshift-security-group-whitelists-all.json │ │ │ ├── route53-domain-no-autorenew.json │ │ │ ├── route53-domain-no-transferlock.json │ │ │ ├── route53-domain-transferlock-not-authorized.json │ │ │ ├── s3-bucket-allowing-cleartext.json │ │ │ ├── s3-bucket-no-default-encryption.json │ │ │ ├── s3-bucket-no-logging.json │ │ │ ├── s3-bucket-no-mfa-delete.json │ │ │ ├── s3-bucket-no-versioning.json │ │ │ ├── s3-bucket-website-enabled.json │ │ │ ├── s3-bucket-world-acl.json │ │ │ ├── s3-bucket-world-policy-arg.json │ │ │ ├── s3-bucket-world-policy-star.json │ │ │ ├── ses-identity-dkim-not-enabled.json │ │ │ ├── ses-identity-dkim-not-verified.json │ │ │ ├── ses-identity-world-policy.json │ │ │ ├── sns-topic-world-policy.json │ │ │ ├── sqs-queue-world-policy.json │ │ │ ├── vpc-custom-network-acls-allow-all.json │ │ │ ├── vpc-default-network-acls-allow-all.json │ │ │ ├── vpc-network-acl-not-used.json │ │ │ ├── vpc-subnet-with-bad-acls.json │ │ │ ├── vpc-subnet-with-default-acls.json │ │ │ └── vpc-subnet-without-flow-log.json │ │ └── rulesets │ │ │ ├── cis-02-29-2016.json │ │ │ ├── default.json │ │ │ ├── detailed.json │ │ │ ├── filters.json │ │ │ └── sample.json │ ├── exceptions.py │ ├── postprocessing.py │ ├── preprocessing.py │ ├── processingengine.py │ ├── rule.py │ ├── rule_definition.py │ ├── ruleset.py │ └── utils.py ├── services │ ├── __init__.py │ ├── awslambda.py │ ├── cloudformation.py │ ├── cloudtrail.py │ ├── cloudwatch.py │ ├── directconnect.py │ ├── ec2.py │ ├── efs.py │ ├── elasticache.py │ ├── elb.py │ ├── elbv2.py │ ├── emr.py │ ├── iam.py │ ├── rds.py │ ├── redshift.py │ ├── route53.py │ ├── s3.py │ ├── ses.py │ ├── sns.py │ ├── sqs.py │ └── vpc.py └── utils.py ├── LICENSE ├── MANIFEST.in ├── README.rst ├── Scout2.py ├── Scout2Listall.py ├── Scout2RulesGenerator.py ├── requirements.txt ├── setup.py ├── tests ├── data │ ├── invalid-file.json │ ├── rule-configs │ │ ├── ec2-default-security-group-in-use.json │ │ ├── ec2-default-security-group-with-rules.json │ │ ├── ec2-security-group-opens-all-ports-to-all.json │ │ ├── ec2-security-group-opens-all-ports-to-self.json │ │ ├── ec2-security-group-opens-all-ports.json │ │ ├── ec2-security-group-opens-known-port-to-all.json │ │ ├── ec2-security-group-opens-port-range.json │ │ ├── ec2-security-group-opens-port-to-all.json │ │ ├── ec2-security-group-whitelists-aws-ip-from-banned-region.json │ │ ├── ec2-security-group-whitelists-aws.json │ │ ├── ec2.json │ │ ├── iam-password-policy-expiration-threshold.json │ │ ├── iam-password-policy-lowercase-required.json │ │ ├── iam-password-policy-minimum-length.json │ │ ├── iam-password-policy-no-expiration.json │ │ ├── iam-password-policy-no-lowercase-required.json │ │ ├── iam-password-policy-no-number-required.json │ │ ├── iam-password-policy-no-symbol-required.json │ │ ├── iam-password-policy-no-uppercase-required.json │ │ ├── iam-password-policy-reuse-enabled.json │ │ ├── iam-password-policy.json │ │ └── iam-root.json │ ├── rule-results │ │ ├── ec2-default-security-group-in-use.json │ │ ├── ec2-default-security-group-with-rules.json │ │ ├── ec2-security-group-opens-all-ports-to-all.json │ │ ├── ec2-security-group-opens-all-ports-to-self.json │ │ ├── ec2-security-group-opens-all-ports.json │ │ ├── ec2-security-group-opens-known-port-to-all.json │ │ ├── ec2-security-group-opens-port-range.json │ │ ├── ec2-security-group-opens-port-to-all.json │ │ ├── ec2-security-group-whitelists-aws-ip-from-banned-region.json │ │ ├── ec2-security-group-whitelists-aws.json │ │ ├── iam-password-policy-expiration-threshold.json │ │ ├── iam-password-policy-lowercase-required.json │ │ ├── iam-password-policy-minimum-length.json │ │ ├── iam-password-policy-no-expiration.json │ │ ├── iam-password-policy-no-lowercase-required.json │ │ ├── iam-password-policy-no-number-required.json │ │ ├── iam-password-policy-no-symbol-required.json │ │ ├── iam-password-policy-no-uppercase-required.json │ │ └── iam-password-policy-reuse-enabled.json │ └── test-ruleset.json ├── test-listall.py ├── test-rules-processingengine.py ├── test-rules-ruleset.py ├── test-rulesgenerator.py ├── test-scout2.py ├── test-utils.py ├── test-utils_cloudwatch.py └── test-utils_sns.py └── tools ├── gen-tests.py └── sort-ruleset.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | include = AWSScout2/*.py 3 | AWSScout2/configs/*.py 4 | AWSScout2/output/*.py 5 | AWSScout2/rules/*.py 6 | AWSScout2/services/*.py 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # virtualenv 2 | env/ 3 | venv/ 4 | 5 | *.py[cod] 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Packages 11 | *.egg 12 | *.egg-info 13 | .eggs 14 | .cache/ 15 | dist 16 | build 17 | eggs 18 | parts 19 | bin 20 | var 21 | sdist 22 | develop-eggs 23 | .installed.cfg 24 | lib 25 | lib64 26 | __pycache__ 27 | 28 | # Installer logs 29 | pip-log.txt 30 | 31 | # Unit test / coverage reports 32 | .coverage 33 | .tox 34 | nosetests.xml 35 | 36 | # Translations 37 | *.mo 38 | 39 | # Mr Developer 40 | .mr.developer.cfg 41 | .project 42 | .pydevproject 43 | 44 | # In case people do a `git add .` ... 45 | *.csv 46 | tests/data/ruleset-test.json 47 | 48 | # Data folder and files 49 | scout2-report* 50 | inc-awsconfig* 51 | report-* 52 | 53 | # PyCharm 54 | .idea/ 55 | 56 | # Mac cruft 57 | .DS_Store 58 | 59 | # Emacs backups 60 | *~ 61 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | - "3.3" 6 | - "3.4" 7 | - "3.5" 8 | - "3.6" 9 | 10 | before_script: 11 | - pip install -r requirements.txt 12 | - pip install coveralls 13 | 14 | # command to run tests 15 | script: 16 | - nosetests --with-coverage tests/test-utils.py 17 | # - 'nosetests tests/test-listall.py' 18 | # - 'nosetests tests/test-rulesgenerator.py' 19 | # - 'nosetests tests/test-utils.py' 20 | # - '[ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] && nosetests tests/test-utils_sns.py || false' 21 | - nosetests --with-coverage tests/test-rules-ruleset.py 22 | - nosetests --with-coverage tests/test-rules-processingengine.py 23 | - nosetests --with-coverage --nocapture tests/test-scout2.py 24 | 25 | # use container-base infrastructure 26 | sudo: false 27 | 28 | # Update test coverage -- only when run locally 29 | #after_success: 30 | # - coveralls 31 | -------------------------------------------------------------------------------- /AWSScout2/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __author__ = 'l01cd3v' 4 | __version__ = '3.2.1' 5 | 6 | AWSCONFIG = 42 7 | EXCEPTIONS = 4242 8 | HTMLREPORT = 424242 9 | AWSRULESET = 42424242 10 | 11 | DEFAULT_REPORT_DIR = 'scout2-report' 12 | AWSCONFIG_FILE = 'inc-awsconfig/aws_config.js' 13 | AWSRULESET_FILE = 'inc-awsconfig/aws_ruleset.js' 14 | EXCEPTIONS_FILE = 'inc-awsconfig/exceptions.js' 15 | HTMLREPORT_FILE = 'report.html' 16 | GENERATOR_FILE = 'ruleset-generator.html' 17 | 18 | REPORT_TITLE = 'AWS Scout2 Report' 19 | -------------------------------------------------------------------------------- /AWSScout2/__rules_generator__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | import webbrowser 7 | 8 | try: 9 | from opinel.utils.console import configPrintException, printInfo 10 | from opinel.utils.globals import check_requirements 11 | except Exception as e: 12 | print('Error: Scout2 depends on the opinel package. Install all the requirements with the following command:') 13 | print(' $ pip install -r requirements.txt') 14 | print(e) 15 | sys.exit(42) 16 | 17 | from AWSScout2.cli_parser import RulesArgumentParser 18 | from AWSScout2.configs.scout2 import Scout2Config 19 | from AWSScout2.rules.ruleset import Ruleset 20 | from AWSScout2.output.html import RulesetGenerator 21 | 22 | 23 | ######################################## 24 | ##### Main 25 | ######################################## 26 | 27 | def main(): 28 | 29 | # Parse arguments 30 | parser = RulesArgumentParser() 31 | args = parser.parse_args() 32 | 33 | # Configure the debug level 34 | configPrintException(args.debug) 35 | 36 | # Check version of opinel 37 | if not check_requirements(os.path.realpath(__file__)): 38 | return 42 39 | 40 | # Load ruleset 41 | ruleset = Ruleset(filename = args.base_ruleset, name = args.ruleset_name, rules_dir = args.rules_dir, ruleset_generator = True) 42 | 43 | # Generate the HTML generator 44 | ruleset_generator = RulesetGenerator(args.ruleset_name, args.generator_dir) 45 | ruleset.ruleset_generator_metadata = Scout2Config('default', None, None, [], []).metadata 46 | ruleset_generator_path = ruleset_generator.save(ruleset, args.force_write, args.debug) 47 | 48 | # Open the HTML ruleset generator in a browser 49 | if not args.no_browser: 50 | printInfo('Starting the HTML ruleset generator...') 51 | url = 'file://%s' % os.path.abspath(ruleset_generator_path) 52 | webbrowser.open(url, new=2) 53 | -------------------------------------------------------------------------------- /AWSScout2/configs/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | resource_id_map = { 3 | 'network_interfaces': 'NetworkInterfaceId', 4 | 'peering_connections': 'VpcPeeringConnectionId', 5 | 'subnet_groups': 'DBSubnetGroupName' 6 | } 7 | 8 | -------------------------------------------------------------------------------- /AWSScout2/configs/threads.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | thread_configs = [ 4 | {'list': 1, 'parse': 1}, 5 | {'list': 1, 'parse': 1}, 6 | {'list': 3, 'parse': 5}, 7 | {'list': 5, 'parse': 10}, 8 | {'list': 10, 'parse': 20}, 9 | {'list': 20, 'parse': 40} 10 | ] 11 | -------------------------------------------------------------------------------- /AWSScout2/configs/vpc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Base class for vpc-specific services 4 | """ 5 | 6 | class VPCConfig(object): 7 | """ 8 | Configuration for a single VPC in a single service 9 | """ 10 | 11 | def __init__(self, vpc_resource_types, name = None): 12 | self.name = name 13 | for resource_type in vpc_resource_types: 14 | setattr(self, resource_type, {}) 15 | -------------------------------------------------------------------------------- /AWSScout2/data/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3>=1.4.4 2 | python-dateutil>=2.2 3 | netaddr>=0.7.11 4 | opinel>=3.3.4,<4.0.0 5 | -------------------------------------------------------------------------------- /AWSScout2/output/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nccgroup/Scout2/5d86d46d7ed91a92000496189e9cfa6b98243937/AWSScout2/output/__init__.py -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/accordion.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 38 | 41 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/accordion_policy.html: -------------------------------------------------------------------------------- 1 | 2 | 16 | 19 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 35 | 36 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/details.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/details_for_region.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/details_for_vpc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/ec2_grants.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/filters.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/generic_object.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/ip_grants.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/left_menu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/left_menu_for_region.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 31 | 34 | 35 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/left_menu_for_vpc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/policy.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 23 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/regionfilters.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/resource_link.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.awslambda.regions.id.functions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.cloudtrail.regions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 38 | 41 | 42 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.cloudtrail.regions.id.trails.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 36 | 37 | 38 | 42 | 45 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.cloudwatch.regions.id.alarms.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 37 | 40 | 41 | 42 | 46 | 49 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.ec2.regions.id.volumes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.ec2.regions.id.vpcs.id.instances.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 36 | 37 | 38 | 42 | 45 | 46 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.ec2.regions.vpcs.security_groups.resource_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.elasticache.regions.id.vpcs.id.clusters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.elb.regions.id.elb_policies.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 44 | 47 | 48 | 49 | 53 | 56 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.elb.regions.id.vpcs.id.elbs.linked_resources.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | 28 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.elb.regions.id.vpcs.id.elbs.listener.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.elb.regions.id.vpcsid.elbs.linked_policy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.iam.groups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 34 | 37 | 38 | 39 | 43 | 46 | 47 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.iam.inline_policies.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.iam.managed_policies.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | 28 | 29 | 30 | 34 | 37 | 38 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.iam.managed_policies_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 16 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.rds.regions.id.parameter_groups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.rds.regions.id.security_groups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.rds.regions.id.vpcs.id.snapshots.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.rds.regions.id.vpcs.id.subnet_groups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.redshift.regions.id.parameter_groups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.redshift.regions.id.vpcs.id.security_groups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 36 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.redshift.regions.vpcs.cluster_nodes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.route53.domains.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 23 | 24 | 25 | 29 | 32 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.route53.hosted_zones.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 32 | 33 | 34 | 38 | 41 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.s3.acls.html: -------------------------------------------------------------------------------- 1 | 2 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.s3.buckets.objects.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 23 | 24 | 25 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.ses.regions.id.identities.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 24 | 25 | 26 | 30 | 33 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.sqs.regions.id.queues.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 22 | 23 | 24 | 28 | 31 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.vpc.regions.id.peering_connections.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 16 | 17 | 18 | 37 | 40 | 41 | 42 | 46 | 49 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.vpc.regions.id.vpcs.id.flow_logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 22 | 23 | 24 | 28 | 31 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/services.vpc.regions.id.vpcs.id.peering_connections.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 13 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/partials/singles.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/attack_surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/service_groups.compute.summaries.external_attack_surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/service_groups.database.summaries.external_attack_surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/services.ec2.external_attack_surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/services.elb.external_attack_surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/services.elbv2.external_attack_surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/services.iam.credential_report.root_account.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/services.iam.password_policy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 28 | 29 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/services.rds.external_attack_surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /AWSScout2/output/data/html/summaries/services.redshift.external_attack_surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /AWSScout2/output/data/inc-scout2/ruleset-generator.js: -------------------------------------------------------------------------------- 1 | /**********************************************************/ 2 | /* JavaScript code specific to Scout2's Ruleset Generator */ 3 | /**********************************************************/ 4 | 5 | var generate_ruleset = function() { 6 | var ruleset = new Object(); 7 | ruleset['about'] = aws_info['about']; 8 | ruleset['rules'] = new Object(); 9 | // Find all the rules 10 | var rules = $("*[id^='rule-']"); 11 | for (var i=0; i < rules.length; i++) { 12 | var filename = $(rules[i]).find('#filename').val(); 13 | var rule = new Object(); 14 | rule['level'] = $(rules[i]).find('#level').val(); 15 | rule['enabled'] = $(rules[i]).find('#enabled').is(':checked'); 16 | args = $(rules[i]).find("[id^='parameter_']") 17 | if (args.length > 0) { 18 | tmp = new Object(); 19 | for (var j=0; j < args.length; j++) { 20 | id = $(args[j]).attr('id').replace('parameter_', ''); 21 | val = $(args[j]).val(); 22 | tmp[id] = val; 23 | } 24 | rule['args'] = new Array(); 25 | for (k in tmp) { 26 | rule['args'].push(tmp[k]); 27 | } 28 | } 29 | 30 | if (!(filename in ruleset['rules'])) { 31 | ruleset['rules'][filename] = new Array(); 32 | } 33 | ruleset['rules'][filename].push(rule); 34 | } 35 | 36 | download_configuration(ruleset, aws_info['name'], ''); 37 | } 38 | -------------------------------------------------------------------------------- /AWSScout2/output/data/includes.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nccgroup/Scout2/5d86d46d7ed91a92000496189e9cfa6b98243937/AWSScout2/output/data/includes.zip -------------------------------------------------------------------------------- /AWSScout2/output/data/listall-configs/ec2.regions.id.vpcs.id.security_groups.id.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | "ec2.regions.id", 4 | "ec2.regions.id.vpcs.id", 5 | "ec2.regions.id.vpcs.id.security_groups.id", 6 | "name" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AWSScout2/output/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | 4 | import os 5 | import sys 6 | 7 | from opinel.utils.console import printError 8 | 9 | from AWSScout2 import AWSCONFIG, EXCEPTIONS, HTMLREPORT, AWSRULESET, AWSCONFIG_FILE, EXCEPTIONS_FILE, HTMLREPORT_FILE, AWSRULESET_FILE 10 | 11 | 12 | def prompt_4_yes_no(question): 13 | """ 14 | Ask a question and prompt for yes or no 15 | 16 | :param question: Question to ask; answer is yes/no 17 | :return: :boolean 18 | """ 19 | while True: 20 | sys.stdout.write(question + ' (y/n)? ') 21 | try: 22 | choice = raw_input().lower() 23 | except: 24 | choice = input().lower() 25 | if choice == 'yes' or choice == 'y': 26 | return True 27 | elif choice == 'no' or choice == 'n': 28 | return False 29 | else: 30 | printError('\'%s\' is not a valid answer. Enter \'yes\'(y) or \'no\'(n).' % choice) 31 | 32 | 33 | def prompt_4_overwrite(filename, force_write): 34 | """ 35 | Confirm before overwriting existing files. Do not prompt if the file does not exist or force_write is set 36 | 37 | :param filename: Name of the file to be overwritten 38 | :param force_write: Do not ask for confirmation and automatically return True if set 39 | :return: :boolean 40 | """ 41 | # 42 | if not os.path.exists(filename) or force_write: 43 | return True 44 | return prompt_4_yes_no('File \'{}\' already exists. Do you want to overwrite it'.format(filename)) 45 | 46 | 47 | def get_filename(config_type, profile, report_dir): 48 | if config_type == AWSCONFIG: 49 | filename = AWSCONFIG_FILE 50 | first_line = 'aws_info =' 51 | elif config_type == EXCEPTIONS: 52 | filename = EXCEPTIONS_FILE 53 | first_line = 'exceptions =' 54 | elif config_type == HTMLREPORT: 55 | filename = HTMLREPORT_FILE 56 | first_line = None 57 | elif config_type == AWSRULESET: 58 | filename = AWSRULESET_FILE 59 | first_line = 'aws_info =' 60 | else: 61 | printError('invalid config type provided (%s)' % config_type) 62 | raise Exception 63 | # Append profile name if necessary 64 | if profile != 'default' and config_type != AWSRULESET: 65 | name, extention = filename.split('.') 66 | filename = '%s-%s.%s' % (name, profile, extention) 67 | return (os.path.join(report_dir, filename), first_line) 68 | -------------------------------------------------------------------------------- /AWSScout2/rules/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | condition_operators = [ 'and', 'or' ] 6 | 7 | awsscout2_rules_data_dir = os.path.join(os.path.dirname(__file__), 'data') 8 | awsscout2_conditions_dir = os.path.join(awsscout2_rules_data_dir, 'conditions') 9 | awsscout2_filters_dir = os.path.join(awsscout2_rules_data_dir, 'filters') 10 | awsscout2_findings_dir = os.path.join(awsscout2_rules_data_dir, 'findings') 11 | awsscout2_rulesets_dir = os.path.join(awsscout2_rules_data_dir, 'rulesets') 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/cidr-is-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ "or", 3 | [ "this", "equal", "0.0.0.0/0" ], 4 | [ "this", "equal", "::/0" ] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/ec2-security-group-in-use.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ "ec2.regions.id.vpcs.id.security_groups.id.", "withKey", "used_by" ] 3 | } 4 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/ec2-security-group-not-used.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ "ec2.regions.id.vpcs.id.security_groups.id.", "withoutKey", "used_by" ] 3 | } 4 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/instance-with-open-nacls.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": 3 | [ "vpc.regions.id.vpcs.id.network_acls._GET_VALUE_AT_(vpc.regions.id.vpcs.id.subnets._GET_VALUE_AT_(ec2.regions.id.vpcs.id.instances.id.network_interfaces.id.SubnetId).network_acl).allow_all_ingress_traffic", "notEqual", "0" ] 4 | } 5 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/instance-with-public-ip.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ "and", 3 | [ "ec2.regions.id.vpcs.id.instances.id.network_interfaces.id.", "withKey", "Association" ], 4 | [ "ec2.regions.id.vpcs.id.instances.id.network_interfaces.id.Association", "notNull", "" ], 5 | [ "ec2.regions.id.vpcs.id.instances.id.network_interfaces.id.Association.PublicIp", "notNull", "" ] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/ip-not-in-private-space.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ "this", "notInSubnets", [ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ] ] 3 | } 4 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/policy-statement-any-principal.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ "or", 3 | [ "_STATEMENT_.Principal", "containAtLeastOneOf", "*" ], 4 | [ "and", 5 | [ "_STATEMENT_.Principal", "withKey", "AWS" ], 6 | [ "_STATEMENT_.Principal.AWS", "containAtLeastOneOf", "*" ] 7 | ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/policy-statement-poor-condition.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ "or", 3 | [ "_STATEMENT_.", "withoutKey", "Condition" ], 4 | [ "and", 5 | [ "_STATEMENT_.Condition.", "withoutKey", "ArnEquals" ], 6 | [ "or", 7 | [ "_STATEMENT_.Condition.", "withoutKey", "StringEquals" ], 8 | [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "aws:SourceOwner" ], 9 | [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "aws:SourceArn" ] 10 | ], 11 | [ "or", 12 | [ "_STATEMENT_.Condition.", "withoutKey", "ArnLike" ] 13 | ] 14 | ] 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/conditions/security-group-opens-all-ports.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ "and", 3 | [ "or", 4 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", "equal", "0-65535" ], 5 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id", "equal", "ALL" ] 6 | ], 7 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id", "equal", "ingress"] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/filters/ec2-instance-with-open-nacls.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Public instance with open NACLs", 3 | "rationale": "", 4 | "path": "ec2.regions.id.vpcs.id.instances.id.network_interfaces.id", 5 | "dashboard_name": "ENIs", 6 | "display_path": "ec2.regions.id.vpcs.id.instances.id", 7 | "conditions": [ "and", 8 | [ "_INCLUDE_(conditions/instance-with-public-ip.json)", "", "" ], 9 | [ "_INCLUDE_(conditions/instance-with-open-nacls.json)", "", "" ] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/filters/ec2-security-group-with-public-cidr-grant.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Security group whitelists public CIDRs", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.ingress.protocols.id.ports.id.cidrs.id.CIDR", 4 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id", 5 | "dashboard_name": "Rules", 6 | "conditions": [ "and", 7 | [ "this", "notInSubnets", [ "10.0.0.0/8", "172.16.0.0/16", "192.168.0.0/24" ] ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/filters/iam-role-for-aws-account.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Role for _ARG_0_ account", 3 | "key": "iam-role-for-_ARG_0_-account", 4 | "path": "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id", 5 | "display_path": "iam.roles.id", 6 | "dashboard_name": "Roles", 7 | "conditions": [ "and", 8 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 9 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "containAction", "sts:AssumeRole" ], 10 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal", "withKey", "AWS" ], 11 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal.AWS", "_ARG_1_", "_AWS_ACCOUNT_ID_" ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/filters/iam-role-for-service.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Role for _ARG_0_", 3 | "key": "iam-role-for-_ARG_0_", 4 | "path": "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id", 5 | "display_path": "iam.roles.id", 6 | "dashboard_name": "Roles", 7 | "conditions": [ "and", 8 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 9 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "containAction", "sts:AssumeRole" ], 10 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal", "withKey", "Service" ], 11 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal.Service", "equal", "_ARG_1_" ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/cloudformation-stack-with-role.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Role passed to stack", 3 | "rationale": "Passing a role to CloudFormation stacks may result in privilege escalation beause IAM users with privileges within the CloudFormation scope implicitly inherit the stack's role's permissions.", 4 | "path": "cloudformation.regions.id.stacks.id", 5 | "dashboard_name": "Stacks", 6 | "conditions": [ "and", 7 | [ "this", "withKey", "iam_role" ], 8 | [ "this.iam_role.id", "notEmpty", "" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/cloudtrail-duplicated-global-services-logging.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Global service logging duplicated", 3 | "rationale": "Global service logging is enabled in multiple Trails. While this does not jeopardize the security of the environment, duplicated entries in logs increase the difficulty to investate potential incidents.", 4 | "path": "cloudtrail", 5 | "dashboard_name": "Configuration", 6 | "conditions": [ "and", 7 | [ "DuplicatedGlobalServiceEvents", "true", ""] 8 | ], 9 | "id_suffix": "IncludeGlobalServiceEvents" 10 | } -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/cloudtrail-no-data-logging.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Data logging not configured", 3 | "rationale": "CloudTrail data logging is not configured, which means that S3 access is not logged.", 4 | "path": "cloudtrail", 5 | "dashboard_name": "Configuration", 6 | "conditions": [ "and", 7 | [ "data_logging_trails_count", "equal", "0" ] 8 | ], 9 | "id_suffix": "DataLoggingNotConfigured" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/cloudtrail-no-global-services-logging.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Global services logging disabled", 3 | "rationale": "API activity for global services such as IAM and STS is not logged. Investigation of incidents will be incomplete due to the lack of information.", 4 | "path": "cloudtrail", 5 | "dashboard_name": "Configuration", 6 | "conditions": [ "and", 7 | [ "IncludeGlobalServiceEvents", "false", "" ] 8 | ], 9 | "id_suffix": "IncludeGlobalServiceEvents" 10 | } -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/cloudtrail-no-log-file-validation.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Log file validation disabled", 3 | "rationale": "The lack of log file validation prevents one from verifying the integrity of the log files.", 4 | "path": "cloudtrail.regions.id.trails.id", 5 | "dashboard_name": "Trails", 6 | "conditions": [ "and", 7 | [ "cloudtrail.regions.id.trails.id.", "withKey", "LogFileValidationEnabled" ], 8 | [ "cloudtrail.regions.id.trails.id.LogFileValidationEnabled", "false", "" ] 9 | ], 10 | "id_suffix": "LogFileValidationDisabled" 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/cloudtrail-no-logging.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Logging disabled", 3 | "rationale": "Logging is disabled for a given Trail. Depending on the configuration, logs for important API activity may be missing.", 4 | "path": "cloudtrail.regions.id.trails.id", 5 | "dashboard_name": "Trails", 6 | "conditions": [ "and", 7 | [ "cloudtrail.regions.id.trails.id.", "withKey", "IsLogging" ], 8 | [ "cloudtrail.regions.id.trails.id.IsLogging", "false", "" ] 9 | ], 10 | "id_suffix": "IsLogging" 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/cloudtrail-not-configured.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Not configured", 3 | "rationale": "CloudTrail is not configured, which means that API activity is not logged.", 4 | "path": "cloudtrail.regions.id", 5 | "dashboard_name": "Regions", 6 | "conditions": [ "and", 7 | [ "trails_count", "equal", "0" ] 8 | ], 9 | "id_suffix": "NotConfigured" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/cloudwatch-alarm-without-actions.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Alarm without action", 3 | "rationale": "Each alarm should have at least one action", 4 | "path": "cloudwatch.regions.id.alarms.id", 5 | "dashboard_name": "Alarms", 6 | "conditions": [ "and", 7 | [ "cloudwatch.regions.id.alarms.id.AlarmActions", "empty", "" ] 8 | ], 9 | "id_suffix": "NoActions" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-default-security-group-in-use.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Default security groups in use", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id", 4 | "dashboard_name": "Security groups", 5 | "conditions": [ "and", 6 | [ "ec2.regions.id.vpcs.id.security_groups.id.name", "equal", "default" ], 7 | [ "ec2.regions.id.vpcs.id.security_groups.id.", "withKey", "used_by" ] 8 | 9 | ], 10 | "id_suffix": "default_in_use" 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-default-security-group-with-rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Non-empty rulesets for default security groups", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id", 4 | "dashboard_name": "Rulesets", 5 | "conditions": [ "and", 6 | [ "ec2.regions.id.vpcs.id.security_groups.id.name", "equal", "default" ], 7 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols", "notEmpty", "" ] 8 | 9 | ], 10 | "id_suffix": "default_with_rules", 11 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id" 12 | } 13 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-ebs-volume-not-encrypted.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "EBS volume not encrypted", 3 | "rationale": "Enabling encryption of EBS volumes ensures that data is encrypted both at-rest and in-transit (between an instance and its attached EBS storage).", 4 | "path": "ec2.regions.id.volumes.id", 5 | "dashboard_name": "Volumes", 6 | "conditions": [ "and", 7 | [ "Encrypted", "false", "" ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-instance-in-security-group.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Security group name or ID" ], 3 | "description": "EC2 instance belongs to specific security group", 4 | "path": "ec2.regions.id.vpcs.id.instances.id", 5 | "conditions": [ "and", 6 | [ "ec2.regions.id.vpcs.id.instances.id.security_groups", "match", ".*_ARG_0_.*" ] 7 | ], 8 | "arg_names": [ 9 | "ID of the security group EC2 instances may not belong to." 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-instance-type.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Use of _ARG_0_ instances", 3 | "key": "ec2-instance-type-_STRIPDOTS_(_ARG_0_)", 4 | "rationale": "Policies dictacte EC2 instances of type _ARG_0_ should not be used in this environment", 5 | "path": "ec2.regions.id.vpcs.id.instances.id", 6 | "dashboard_name": "Instances", 7 | "conditions": [ "and", 8 | [ "InstanceType", "equal", "_ARG_0_" ] 9 | ], 10 | "arg_names": [ 11 | "Type of EC2 instance (e.g. t2.micro)" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-instance-types.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Use of _ARG_0_ instances", 3 | "key": "ec2-instance-type-_STRIPDOTS_(_ARG_0_)", 4 | "rationale": "Policies dictacte _ARG_0_ EC2 instances should not be used in this environment", 5 | "path": "ec2.regions.id.vpcs.id.instances.id", 6 | "dashboard_name": "Instances", 7 | "conditions": [ "and", 8 | [ "InstanceType", "containAtLeastOneOf", "_ARG_1_" ] 9 | ], 10 | "arg_names": [ 11 | "Display name of types of instances", 12 | "Type of EC2 instances that may not be used." 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-instance-with-public-ip.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Instance with a public IP", 3 | "rationale": "It is good practice to maintain a list of known, publicly accessible instances and flag all other instances thay meet this criteria.", 4 | "path": "ec2.regions.id.vpcs.id.instances.id.network_interfaces.id", 5 | "dashboard_name": "Network interfaces", 6 | "conditions": [ "and", 7 | [ "Association", "notNull", "" ], 8 | [ "ec2.regions.id.vpcs.id.instances.id.network_interfaces.id.Association.PublicIp", "notNull", "" ] 9 | ], 10 | "display_path": "ec2.regions.id.vpcs.id.instances.id" 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-opens-all-ports-to-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "All ports open to all", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", 4 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id", 5 | "dashboard_name": "Rules", 6 | "conditions": [ "and", 7 | [ "_INCLUDE_(conditions/cidr-is-all.json)", "", ""], 8 | [ "_INCLUDE_(conditions/security-group-opens-all-ports.json)", "", ""] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-opens-all-ports-to-self.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Unrestricted network traffic within security group", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.security_groups.id", 4 | "dashboard_name": "Rules", 5 | "conditions": [ "and", 6 | [ "_INCLUDE_(conditions/security-group-opens-all-ports.json)", "", ""], 7 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.security_groups.id.GroupId", "equal", "_GET_VALUE_AT_(ec2.regions.id.vpcs.id.security_groups.id)" ] 8 | ], 9 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-opens-all-ports.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "All ports open", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", 4 | "dashboard_name": "Rules", 5 | "conditions": [ "and", 6 | [ "_INCLUDE_(conditions/security-group-opens-all-ports.json)", "", ""] 7 | ], 8 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-opens-known-port-to-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Network protocol name", "Transport protocol name", "Port number" ], 3 | "key": "ec2-security-group-opens-_ARG_0_-port-to-all", 4 | "description": "_ARG_0_ port open to all", 5 | "dashboard_name": "Rules", 6 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", 7 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id", 8 | "conditions": [ "and", 9 | [ "_INCLUDE_(conditions/cidr-is-all.json)", "", ""], 10 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id", "equal", "ingress" ], 11 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id", "equal", "_ARG_1_" ], 12 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", "equal", "_ARG_2_" ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-opens-plaintext-port.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Network protocol name", "Transport protocol name", "Port number" ], 3 | "key": "ec2-security-group-opens-plaintext-port-_ARG_0_", 4 | "description": "_ARG_0_ port open", 5 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", 6 | "dashboard_name": "Rules", 7 | "conditions": [ "and", 8 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id", "equal", "ingress" ], 9 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id", "equal", "_ARG_1_" ], 10 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", "equal", "_ARG_2_" ] 11 | ], 12 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id" 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-opens-port-range.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Use of port ranges", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", 4 | "dashboard_name": "Rules", 5 | "conditions": [ "and", 6 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id", "equal", "ingress" ], 7 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id", "containAtLeastOneOf", ["ALL", "UDP", "TCP"] ], 8 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", "match", "[0-9]+-[0-9]+" ], 9 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", "notEqual", "0-65535" ] 10 | ], 11 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id" 12 | } 13 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-opens-port-to-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Network transport protocol" ], 3 | "key": "ec2-security-group-opens-_ARG_0_-port-to-all", 4 | "description": "_ARG_0_ port open to all", 5 | "dashboard_name": "Rules", 6 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", 7 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id", 8 | "conditions": [ "and", 9 | [ "_INCLUDE_(conditions/cidr-is-all.json)", "", ""], 10 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id", "equal", "ingress" ], 11 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id", "equal", "_ARG_0_" ], 12 | [ "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id", "containNoneOf", [ "22", "80", "443", "1433", "1521", "3306", "3389", "5432", "27017" ] ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-whitelists-aws-ip-from-banned-region.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Security group whitelists AWS IPs outside the USA", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", 4 | "dashboard_name": "Rules", 5 | "conditions": [ "and", 6 | [ "this", "inSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges/aws.json, [])" ], 7 | [ "this", "notInSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges/aws-in-us.json, [])" ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-whitelists-aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Security group whitelists AWS CIDRs", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", 4 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id", 5 | "dashboard_name": "Rules", 6 | "conditions": [ "and", 7 | [ "this", "inSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges/aws.json, [])" ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-whitelists-non-elastic-ips.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Security group whitelists non elastic IP addresses", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id", 4 | "dashboard_name": "Rules", 5 | "conditions": [ "and", 6 | [ "this", "inSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges-from-args, [])" ], 7 | [ "this", "inSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges-from-args, [[\"is_elastic\", \"false\", \"\"]])" ] 8 | ], 9 | "keys": [ 10 | "ec2.regions.id.vpcs.id.security_groups.id", 11 | "this" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-whitelists-unknown-aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Security group whitelists unknown AWS CIDRs", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", 4 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id", 5 | "dashboard_name": "Rules", 6 | "conditions": [ "and", 7 | [ "this", "inSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges/aws.json, [])" ], 8 | [ "this", "notInSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges-from-args, [])" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-security-group-whitelists-unknown-cidrs.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Security group whitelists unknown CIDRs", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", 4 | "display_path": "ec2.regions.id.vpcs.id.security_groups.id", 5 | "dashboard_name": "Rules", 6 | "conditions": [ "and", 7 | [ "this", "notEqual", "0.0.0.0/0" ], 8 | [ "this", "notEqual", "::/0" ], 9 | [ "this", "notInSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges/aws.json, [])" ], 10 | [ "this", "notInSubnets", "_IP_RANGES_FROM_FILE_(ip-ranges-from-args, [])" ], 11 | [ "_INCLUDE_(conditions/ip-not-in-private-space.json)", "", "" ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ec2-unused-security-group.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Unused security groups", 3 | "path": "ec2.regions.id.vpcs.id.security_groups.id", 4 | "dashboard_name": "Security groups", 5 | "conditions": [ "and", 6 | [ "ec2.regions.id.vpcs.id.security_groups.id.", "withoutKey", "used_by" ], 7 | [ "ec2.regions.id.vpcs.id.security_groups.id.name", "notEqual", "default" ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/elb-no-access-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Lack of access logs", 3 | "rationale": "Access logs enable traffic analysis and identification of security issues.", 4 | "path": "elb.regions.id.vpcs.id.elbs.id.attributes.AccessLog.Enabled", 5 | "dashboard_name": "Load Balancer Attributes", 6 | "display_path": "elb.regions.id.vpcs.id.elbs.id", 7 | "conditions": [ "and", 8 | [ "this", "false", "" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/elbv2-no-access-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Lack of access logs", 3 | "rationale": "Access logs enable traffic analysis and identification of security issues.", 4 | "path": "elbv2.regions.id.vpcs.id.lbs.id.attributes.id", 5 | "dashboard_name": "Load Balancer Attributes", 6 | "display_path": "elbv2.regions.id.vpcs.id.lbs.id", 7 | "conditions": [ "and", 8 | [ "Key", "equal", "access_logs.s3.enabled" ], 9 | [ "Value", "equal", "false"] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/elbv2-no-deletion-protection.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Lack of deletion protection", 3 | "rationale": "Enabling deletion protection on load balancers mitigates risks of accidental deletion.", 4 | "path": "elbv2.regions.id.vpcs.id.lbs.id.attributes.id", 5 | "dashboard_name": "Load Balancer Attributes", 6 | "display_path": "elbv2.regions.id.vpcs.id.lbs.id", 7 | "conditions": [ "and", 8 | [ "Key", "equal", "deletion_protection.enabled" ], 9 | [ "Value", "equal", "false"] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/elbv2-older-ssl-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Older SSL/TLS policy", 3 | "rationale": "Use of AWS latest TLS policy is best practice.", 4 | "path": "elbv2.regions.id.vpcs.id.lbs.id.listeners.id.SslPolicy", 5 | "dashboard_name": "Load Balancer Listeners", 6 | "display_path": "elbv2.regions.id.vpcs.id.lbs.id", 7 | "conditions": [ "and", 8 | [ "this", "containNoneOf", [ "ELBSecurityPolicy-2016-08", "ELBSecurityPolicy-TLS-1-2-2017-01", "ELBSecurityPolicy-TLS-1-1-2017-01" ] ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-assume-role-lacks-external-id-and-mfa.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Cross-account AssumeRole policy lacks external ID and MFA", 3 | "rationale": "When authorizing cross-account role assumption, an external ID or MFA should be required.", 4 | "path": "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id", 5 | "display_path": "iam.roles.id", 6 | "dashboard_name": "Roles", 7 | "conditions": [ "and", 8 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 9 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "containAction", "sts:AssumeRole" ], 10 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal", "withKey", "AWS" ], 11 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal.AWS", "isCrossAccount", "_AWS_ACCOUNT_ID_" ], 12 | [ "or", 13 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "withoutKey", "Condition" ], 14 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition", "withoutKey", "Bool" ], 15 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition.Bool.", "withoutKey", "aws:MultiFactorAuthPresent" ], 16 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition.Bool.aws:MultiFactorAuthPresent", "notTrue", "" ] 17 | ], 18 | [ "or", 19 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "withoutKey", "Condition" ], 20 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition", "withoutKey", "StringEquals" ], 21 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition.StringEquals.", "withoutKey", "sts:ExternalId" ], 22 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition.StringEquals.sts:ExternalId", "empty", "" ] 23 | ] 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-assume-role-no-mfa.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "AssumeRole policy lacks MFA", 3 | "rationale": "", 4 | "path": "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id", 5 | "display_path": "iam.roles.id", 6 | "dashboard_name": "Roles", 7 | "conditions": [ "and", 8 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 9 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "containAction", "sts:AssumeRole" ], 10 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal", "withKey", "AWS" ], 11 | [ "or", 12 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "withoutKey", "Condition" ], 13 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition", "withoutKey", "Bool" ], 14 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition.Bool.", "withoutKey", "aws:MultiFactorAuthPresent" ], 15 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Condition.Bool.aws:MultiFactorAuthPresent", "notTrue", "" ] 16 | ] 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-assume-role-policy-allows-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "AssumeRole policy allows all principals", 3 | "rationale": "Setting the AssumeRole policy's principal attribute to AWS:* means that anyone is authorized to assume the role and access the AWS account.", 4 | "path": "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id", 5 | "display_path": "iam.roles.id", 6 | "dashboard_name": "Roles", 7 | "conditions": [ "and", 8 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 9 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "containAction", "sts:AssumeRole" ], 10 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal", "withKey", "AWS" ], 11 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal.AWS", "containAtLeastOneOf", "*" ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-ec2-role-without-instances.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Unused role for EC2", 3 | "path": "iam.roles.id.assume_role_policy.Statement.id.Principal", 4 | "display_path": "iam.roles.id", 5 | "dashboard_name": "Roles", 6 | "conditions": [ "and", 7 | [ "iam.roles.id.instances_count", "equal", "0" ], 8 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal.", "withKey", "Service" ], 9 | [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal.Service", "equal", "ec2.amazonaws.com" ] 10 | ], 11 | "id_suffix": "instances", 12 | "keys": [ 13 | "iam.roles.id.name", 14 | "this" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-group-with-inline-policies.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Group with inline policies", 3 | "path": "iam.groups.id", 4 | "dashboard_name": "groups", 5 | "conditions": [ "and", 6 | [ "iam.groups.id.", "withKey", "inline_policies" ] 7 | ], 8 | "id_suffix": "inline_policies" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-group-with-no-users.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Group with no users", 3 | "rationale": "Groups with no users should be reviewed and deleted if not necessary.", 4 | "path": "iam.groups.id", 5 | "dashboard_name": "groups", 6 | "conditions": [ "and", 7 | [ "iam.groups.id.users", "empty", "" ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-human-user-with-policies.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Name of IAM group", "Type of policy", "Path to policies" ], 3 | "key": "iam-human-user-with-_ARG_1_-policies", 4 | "description": "Human user with _ARG_1_ policies", 5 | "path": "iam.users.id", 6 | "dashboard_name": "Users", 7 | "conditions": [ "and", 8 | [ "iam.users.id.groups", "containAtLeastOneOf", "_ARG_0_" ], 9 | [ "iam.users.id.", "withKey", "_ARG_2_" ] 10 | ], 11 | "id_suffix": "_ARG_1_" 12 | } 13 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-inline-policy-allows-NotActions.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "IAM entity type" ], 3 | "description": "Inline _ARG_0_ policy allows NotActions", 4 | "rationale": "The combination of \"effect = allow\" and \"NotAction\" results in the policy allowing every action except those listed in the statement. The target policy does not follow the principle of least privilege because thousands of actions exist in AWS and because this policy automatically authorizes users to perform new actions created, regardless of their nature.", 5 | "key": "iam-inline-_ARG_0_-policy-allows-NotActions", 6 | "path": "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id", 7 | "display_path": "iam._ARG_0_s.id", 8 | "dashboard_name": "Policies", 9 | "conditions": [ "and", 10 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 11 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.", "withKey", "NotAction" ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-inline-policy-allows-non-sts-action.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "IAM entity type", "Service", "Action" ], 3 | "description": "Inline _ARG_0_ policy allows non STS action", 4 | "key": "iam-inline-_ARG_0_-policy-allows-non-sts-action", 5 | "path": "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id", 6 | "display_path": "iam._ARG_0_s.id", 7 | "dashboard_name": "Policies", 8 | "conditions": [ "and", 9 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 10 | [ "or", 11 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.", "withoutKey", "Action" ], 12 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.Action", "containAtLeastOneDifferentFrom", "sts:AssumeRole" ] 13 | ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-inline-policy-for-role.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "IAM entity type", "Service", "Action" ], 3 | "description": "Inline _ARG_0_ policy allows _ARG_1_:_ARG_2_ *", 4 | "key": "iam-inline-_ARG_0_-policy-allows-_ARG_1_-_ARG_2_", 5 | "path": "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id", 6 | "display_path": "iam._ARG_0_s.id", 7 | "dashboard_name": "Policies", 8 | "conditions": [ "and", 9 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 10 | [ "or", 11 | [ "and", 12 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.", "withKey", "Action" ], 13 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.Action", "containNoneOf", [ "*", "*:*" ] ] 14 | ], 15 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.", "withKey", "NotAction" ] 16 | ], 17 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.", "containAction", "_ARG_1_:_ARG_2_" ], 18 | [ "iam._ARG_0_s.id.inline_policies.id.PolicyDocument.Statement.id.Resource", "containAtLeastOneOf", [ "*" ] ] 19 | ], 20 | "keys": [ 21 | "iam._ARG_0_s.id.inline_policies.id.name" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-managed-policy-allows-NotActions.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Managed policy allows NotActions", 3 | "rationale": "The combination of \"effect = allow\" and \"NotAction\" results in the policy allowing every action except those listed in the statement. The target policy does not follow the principle of least privilege because thousands of actions exist in AWS and because this policy automatically authorizes users to perform new actions created, regardless of their nature.", 4 | "path": "iam.policies.id.PolicyDocument.Statement.id", 5 | "display_path": "iam.policies.id", 6 | "dashboard_name": "Policies", 7 | "conditions": [ "and", 8 | [ "iam.policies.id.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 9 | [ "iam.policies.id.PolicyDocument.Statement.id.", "withKey", "NotAction" ] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-managed-policy-allows-non-sts-action.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Service", "Action" ], 3 | "description": "Managed policy allows non STS action", 4 | "path": "iam.policies.id.PolicyDocument.Statement.id", 5 | "display_path": "iam.policies.id", 6 | "dashboard_name": "Policies", 7 | "conditions": [ "and", 8 | [ "iam.policies.id.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 9 | [ "or", 10 | [ "iam.policies.id.PolicyDocument.Statement.id.", "withoutKey", "Action" ], 11 | [ "iam.policies.id.PolicyDocument.Statement.id.Action", "containAtLeastOneDifferentFrom", "sts:AssumeRole" ] 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-managed-policy-for-role.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Service", "Action" ], 3 | "description": "Managed policy allows _ARG_0_:_ARG_1_ *", 4 | "key": "iam-managed-policy-allows-_ARG_0_-_ARG_1_", 5 | "path": "iam.policies.id.PolicyDocument.Statement.id", 6 | "display_path": "iam.policies.id", 7 | "dashboard_name": "Policies", 8 | "conditions": [ "and", 9 | [ "iam.policies.id.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], 10 | [ "or", 11 | [ "and", 12 | [ "iam.policies.id.PolicyDocument.Statement.id.", "withKey", "Action" ], 13 | [ "iam.policies.id.PolicyDocument.Statement.id.Action", "containNoneOf", [ "*", "*:*" ] ] 14 | ], 15 | [ "iam.policies.id.PolicyDocument.Statement.id.", "withKey", "NotAction" ] 16 | ], 17 | [ "iam.policies.id.PolicyDocument.Statement.id.", "containAction", "_ARG_0_:_ARG_1_" ], 18 | [ "iam.policies.id.PolicyDocument.Statement.id.Resource", "containAtLeastOneOf", [ "*" ] ] 19 | ], 20 | "keys": [ 21 | "iam.policies.id.name" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-managed-policy-no-attachments.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Managed policy not attached to any entity", 3 | "rationale": "Customer Managed policies should be reviewed and deleted if not necessary.", 4 | "path": "iam.policies.id", 5 | "display_path": "iam.policies.id", 6 | "dashboard_name": "Policies", 7 | "conditions": [ "and", 8 | [ "iam.policies.id.attached_to", "empty", "" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-expiration-threshold.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Maximum password age" ], 3 | "description": "Passwords expire after _ARG_0_ days", 4 | "path": "iam.password_policy", 5 | "display_path": "iam.password_policy.MaxPasswordAge", 6 | "id_suffix": "MaxPasswordAge", 7 | "dashboard_name": "Password policy", 8 | "conditions": [ "or", 9 | [ "iam.password_policy.ExpirePasswords", "false", "" ], 10 | [ "iam.password_policy.MaxPasswordAge", "moreThan", "_ARG_0_" ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-lowercase-required.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Password policy lacks lowercase requirement", 3 | "path": "iam.password_policy.RequireLowercaseCharacters", 4 | "dashboard_name": "Password policy", 5 | "conditions": [ "or", 6 | [ "this", "false", "" ] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-minimum-length.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Minimum password length" ], 3 | "description": "Minimum password length too short", 4 | "path": "iam.password_policy.MinimumPasswordLength", 5 | "dashboard_name": "Password policy", 6 | "conditions": [ "or", 7 | [ "this", "lessThan", "_ARG_0_" ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-no-expiration.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Password expiration disabled", 3 | "path": "iam.password_policy.ExpirePasswords", 4 | "dashboard_name": "Password policy", 5 | "conditions": [ "or", 6 | [ "this", "false", "" ] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-no-lowercase-required.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Password policy lacks lowercase requirement", 3 | "path": "iam.password_policy.RequireLowercaseCharacters", 4 | "dashboard_name": "Password policy", 5 | "conditions": [ "or", 6 | [ "this", "false", "" ] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-no-number-required.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Password policy lacks number requirement", 3 | "path": "iam.password_policy.RequireNumbers", 4 | "dashboard_name": "Password policy", 5 | "conditions": [ "or", 6 | [ "this", "false", "" ] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-no-symbol-required.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Password policy lacks symbol requirement", 3 | "path": "iam.password_policy.RequireSymbols", 4 | "dashboard_name": "Password policy", 5 | "conditions": [ "or", 6 | [ "this", "false", "" ] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-no-uppercase-required.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Password policy lacsk uppercase requirement", 3 | "path": "iam.password_policy.RequireUppercaseCharacters", 4 | "dashboard_name": "Password policy", 5 | "conditions": [ "or", 6 | [ "this", "false", "" ] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-password-policy-reuse-enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Password reuse enabled", 3 | "path": "iam.password_policy.PasswordReusePrevention", 4 | "dashboard_name": "Password policy", 5 | "conditions": [ "or", 6 | [ "this", "false", "" ] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-role-with-inline-policies.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Role with inline policies", 3 | "path": "iam.roles.id", 4 | "dashboard_name": "Roles", 5 | "conditions": [ "and", 6 | [ "iam.roles.id.", "withKey", "inline_policies" ] 7 | ], 8 | "id_suffix": "inline_policies" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-root-account-no-mfa.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Lack of MFA (root account)", 3 | "path": "iam.credential_report.", 4 | "dashboard_name": "Root account", 5 | "conditions": [ "and", 6 | [ "iam.credential_report..mfa_active", "notTrue", "" ] 7 | ], 8 | "id_suffix": "mfa_active", 9 | "keys": [ "this" ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-root-account-used-recently.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Root account used recently", 3 | "path": "iam.credential_report.", 4 | "dashboard_name": "Root account", 5 | "conditions": [ "and", 6 | [ "iam.credential_report..password_last_used", "notEqual", "no_information" ], 7 | [ "iam.credential_report..password_last_used", "newerThan", ["90", "days"] ] 8 | ], 9 | "id_suffix": "password_last_used" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-root-account-with-active-certs.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Root account has active X.509 certs", 3 | "rationale": "Root account X.509 certificates should be deleted as they may be used to make SOAP-protocol requests in the context of the root account.", 4 | "path": "iam.credential_report.", 5 | "dashboard_name": "Root account", 6 | "conditions": [ "or", 7 | [ "iam.credential_report..cert_1_active", "true", "" ], 8 | [ "iam.credential_report..cert_2_active", "true", "" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-root-account-with-active-keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Root account has active keys", 3 | "rationale": "AWS root account access keys should be deleted as they provide unrestricted access to the AWS Account.", 4 | "path": "iam.credential_report.", 5 | "dashboard_name": "Root account", 6 | "conditions": [ "or", 7 | [ "iam.credential_report..access_key_1_active", "true", "" ], 8 | [ "iam.credential_report..access_key_2_active", "true", "" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-service-user-with-password.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Group for service users" ], 3 | "description": "Service user with password enabled", 4 | "path": "iam.users.id", 5 | "dashboard_name": "Users", 6 | "conditions": [ "and", 7 | [ "iam.users.id.", "withKey", "LoginProfile" ], 8 | [ "iam.users.id.groups", "containAtLeastOneOf", "_ARG_0_" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-user-no-key-rotation.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Key status", "Rotation period" ], 3 | "description": "Lack of key rotation (_ARG_0_)", 4 | "rationale": "In case of access key compromise, the lack of credential rotation increases the period during which an attacker has access to the AWS account", 5 | "key": "iam-user-no-_ARG_0_-key-rotation.json", 6 | "path": "iam.users.id.AccessKeys.id", 7 | "dashboard_name": "Access keys", 8 | "display_path": "iam.users.id", 9 | "conditions": [ "and", 10 | [ "iam.users.id.AccessKeys.id.Status", "equal", "_ARG_0_" ], 11 | [ "iam.users.id.AccessKeys.id.CreateDate", "olderThan", ["_ARG_1_", "days"] ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-user-not-in-category-group.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Category groups" ], 3 | "description": "User not in category group", 4 | "path": "iam.users.id", 5 | "dashboard_name": "Users", 6 | "conditions": [ "and", 7 | [ "iam.users.id.groups", "containNoneOf", "_ARG_0_" ] 8 | ], 9 | "id_suffix": "member_of_category_group" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-user-not-in-common-group.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Common group" ], 3 | "description": "User not in common group", 4 | "path": "iam.users.id", 5 | "dashboard_name": "Users", 6 | "conditions": [ "and", 7 | [ "iam.users.id.groups", "containNoneOf", "_ARG_0_" ] 8 | ], 9 | "id_suffix": "member_of_common_group" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-user-with-multiple-access-keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "User with multiple API keys", 3 | "path": "iam.users.id", 4 | "dashboard_name": "Users", 5 | "conditions": [ "and", 6 | [ "iam.users.id.AccessKeys", "lengthMoreThan", "1" ], 7 | [ "iam.users.id.AccessKeys.0.Status", "equal", "Active" ], 8 | [ "iam.users.id.AccessKeys.1.Status", "equal", "Active" ] 9 | ], 10 | "id_suffix": "multiple_api_keys", 11 | "keys": [ 12 | "iam.users.id.name", 13 | "iam.users.id.AccessKeys" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-user-with-password-and-key.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "User with password and keys enabled", 3 | "path": "iam.users.id", 4 | "dashboard_name": "Users", 5 | "conditions": [ "and", 6 | [ "iam.users.id.", "withKey", "LoginProfile" ], 7 | [ "iam.users.id.AccessKeys", "notEmpty", "" ] 8 | ], 9 | "id_suffix": "password_and_keys", 10 | "keys": [ 11 | "iam.users.id.name" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-user-with-policies.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Type of policy", "Path to policies" ], 3 | "key": "iam-user-with-_ARG_0_-policies", 4 | "description": "User with _ARG_0_ policies", 5 | "path": "iam.users.id", 6 | "dashboard_name": "Users", 7 | "conditions": [ "and", 8 | [ "iam.users.id.", "withKey", "_ARG_1_" ] 9 | ], 10 | "id_suffix": "_ARG_1_" 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/iam-user-without-mfa.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "User without MFA", 3 | "path": "iam.users.id", 4 | "dashboard_name": "Users", 5 | "conditions": [ "and", 6 | [ "iam.users.id.", "withKey", "LoginProfile" ], 7 | [ "iam.users.id.MFADevices", "empty", "" ] 8 | ], 9 | "id_suffix": "mfa_enabled", 10 | "keys": [ 11 | "iam.users.id.name" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/rds-instance-backup-disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Backup disabled", 3 | "path": "rds.regions.id.vpcs.id.instances.id", 4 | "dashboard_name": "Instances", 5 | "conditions": [ "and", 6 | [ "rds.regions.id.vpcs.id.instances.id.BackupRetentionPeriod", "lessThan", "1" ] 7 | ], 8 | "id_suffix": "BackupRetentionPeriod" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/rds-instance-no-minor-upgrade.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Auto minor version upgrade disabled", 3 | "path": "rds.regions.id.vpcs.id.instances.id", 4 | "dashboard_name": "Instances", 5 | "conditions": [ "and", 6 | [ "rds.regions.id.vpcs.id.instances.id.AutoMinorVersionUpgrade", "false", "" ], 7 | [ "rds.regions.id.vpcs.id.instances.id.Engine", "containAtLeastOneOf", ["mariadb", "mysql", "postgres" ] ] 8 | ], 9 | "id_suffix": "AutoMinorVersionUpgrade" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/rds-instance-short-backup-retention-period.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Short backup retention period", 3 | "path": "rds.regions.id.vpcs.id.instances.id", 4 | "dashboard_name": "Instances", 5 | "conditions": [ "and", 6 | [ "rds.regions.id.vpcs.id.instances.id.BackupRetentionPeriod", "moreThan", "0" ], 7 | [ "rds.regions.id.vpcs.id.instances.id.BackupRetentionPeriod", "lessThan", "30" ] 8 | ], 9 | "id_suffix": "BackupRetentionPeriod" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/rds-instance-single-az.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Single AZ RDS instance", 3 | "path": "rds.regions.id.vpcs.id.instances.id", 4 | "dashboard_name": "Instances", 5 | "conditions": [ "and", 6 | [ "rds.regions.id.vpcs.id.instances.id.MultiAZ", "false", "" ] 7 | ], 8 | "id_suffix": "MultiAZ" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/rds-instance-storage-not-encrypted.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Instance storage not encrypted", 3 | "path": "rds.regions.id.vpcs.id.instances.id", 4 | "dashboard_name": "Instances", 5 | "conditions": [ "and", 6 | [ "rds.regions.id.vpcs.id.instances.id.StorageEncrypted", "false", "" ] 7 | ], 8 | "id_suffix": "StorageEncrypted" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/rds-postgres-instance-with-invalid-certificate.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Invalid SSL certificate (PostgreSQL)", 3 | "path": "rds.regions.id.vpcs.id.instances.id", 4 | "dashboard_name": "Instances", 5 | "conditions": [ "and", 6 | [ "rds.regions.id.vpcs.id.instances.id.Engine", "equal", "postgres" ], 7 | [ "rds.regions.id.vpcs.id.instances.id.DBInstanceStatus", "notEqual", "creating" ], 8 | [ "rds.regions.id.vpcs.id.instances.id.InstanceCreateTime", "datePriorTo", "08/05/2014" ] 9 | ], 10 | "id_suffix": "pgsslcert" 11 | } 12 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/rds-security-group-allows-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Security group allows all IP addresses", 3 | "path": "rds.regions.id.vpcs.id.security_groups.id.ip_ranges", 4 | "dashboard_name": "Security Groups", 5 | "display_path": "rds.regions.id.vpcs.id.security_groups.id", 6 | "conditions": [ "and", 7 | [ "rds.regions.id.vpcs.id.security_groups.id.ip_ranges", "containAtLeastOneOf", ["0.0.0.0/0", "::/0"] ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/rds-snapshot-public.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Publicly accessible snapshot", 3 | "path": "rds.regions.id.vpcs.id.snapshots.id.attributes.id", 4 | "dashboard_name": "Snapshots", 5 | "display_path": "rds.regions.id.vpcs.id.snapshots.id", 6 | "conditions": [ "and", 7 | [ "rds.regions.id.vpcs.id.snapshots.id.attributes.id.AttributeName", "equal", "restore" ], 8 | [ "rds.regions.id.vpcs.id.snapshots.id.attributes.id.AttributeValues", "containAtLeastOneOf", "all" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/redshift-cluster-database-not-encrypted.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Cluster database encryption disabled", 3 | "path": "redshift.regions.id.vpcs.id.clusters.id", 4 | "dashboard_name": "Clusters", 5 | "conditions": [ "and", 6 | [ "redshift.regions.id.vpcs.id.clusters.id.Encrypted", "false", "" ] 7 | ], 8 | "id_suffix": "Encrypted" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/redshift-cluster-no-version-upgrade.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Version upgrade disabled", 3 | "path": "redshift.regions.id.vpcs.id.clusters.id", 4 | "dashboard_name": "Clusters", 5 | "conditions": [ "and", 6 | [ "redshift.regions.id.vpcs.id.clusters.id.AllowVersionUpgrade", "false", "" ] 7 | ], 8 | "id_suffix": "AllowVersionUpgrade" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/redshift-cluster-publicly-accessible.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Cluster publicly accessible", 3 | "path": "redshift.regions.id.vpcs.id.clusters.id", 4 | "dashboard_name": "Clusters", 5 | "conditions": [ "and", 6 | [ "redshift.regions.id.vpcs.id.clusters.id.PubliclyAccessible", "true", "" ] 7 | ], 8 | "id_suffix": "PubliclyAccessible" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/redshift-parameter-group-logging-disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "User activity logging disabled", 3 | "path": "redshift.regions.id.parameter_groups.id", 4 | "dashboard_name": "Parameter Groups", 5 | "conditions": [ "and", 6 | [ "redshift.regions.id.parameter_groups.id.parameters.enable_user_activity_logging.value", "false", "" ] 7 | ], 8 | "id_suffix": "enable_user_activity_logging" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/redshift-parameter-group-ssl-not-required.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "SSL not required", 3 | "path": "redshift.regions.id.parameter_groups.id", 4 | "dashboard_name": "Parameter Groups", 5 | "conditions": [ "and", 6 | [ "redshift.regions.id.parameter_groups.id.parameters.require_ssl.value", "false", "" ] 7 | ], 8 | "id_suffix": "require_ssl" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/redshift-security-group-whitelists-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Security group allows all", 3 | "path": "redshift.regions.id.vpcs.id.security_groups.id.IPRanges.id.CIDRIP", 4 | "display_path": "redshift.regions.id.vpcs.id.security_groups.id", 5 | "dashboard_name": "Security Groups", 6 | "conditions": [ "and", 7 | [ "this", "equal", "0.0.0.0/0" ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/route53-domain-no-autorenew.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Domains", 3 | "description": "Domain not set to autorenew", 4 | "path": "route53.domains.id", 5 | "conditions": [ "and", 6 | [ "AutoRenew", "false", "" ] 7 | ], 8 | "id_suffix": "AutoRenew" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/route53-domain-no-transferlock.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Domains", 3 | "description": "Domain transfer not locked", 4 | "path": "route53.domains.id", 5 | "conditions": [ "and", 6 | [ "TransferLock", "false", "" ] 7 | ], 8 | "id_suffix": "TransferLock" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/route53-domain-transferlock-not-authorized.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Domains", 3 | "description": "Domain transfer lock not supported by TLD", 4 | "rationale": "", 5 | "path": "route53.domains.id", 6 | "conditions": [ "and", 7 | [ "name", "match", [".*\\.io", ".*\\.co.nz"] ] 8 | ], 9 | "id_suffix": "TransferLockNotAuthorized" 10 | } -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-allowing-cleartext.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Buckets", 3 | "description": "Bucket allowing clear text (HTTP) communication", 4 | "rationale": "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|Internet.", 5 | "path": "s3.buckets.id", 6 | "conditions": [ "and", 7 | [ "s3.buckets.id.secure_transport", "equal", "Disabled" ] 8 | ], 9 | "id_suffix": "secure_transport" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-no-default-encryption.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Buckets", 3 | "description": "Bucket without default encryption enabled", 4 | "path": "s3.buckets.id", 5 | "conditions": [ "and", 6 | [ "s3.buckets.id.default_encryption", "equal", "Disabled" ] 7 | ], 8 | "id_suffix": "default_encryption" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-no-logging.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Buckets", 3 | "description": "Bucket access logging disabled", 4 | "path": "s3.buckets.id", 5 | "conditions": [ "and", 6 | [ "s3.buckets.id.logging", "equal", "Disabled" ] 7 | ], 8 | "id_suffix": "logging" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-no-mfa-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Buckets", 3 | "description": "Versioned bucket without MFA delete", 4 | "path": "s3.buckets.id", 5 | "conditions": [ "and", 6 | [ "s3.buckets.id.versioning_status", "equal", "Enabled" ], 7 | [ "s3.buckets.id.version_mfa_delete", "equal", "Disabled" ] 8 | ], 9 | "id_suffix": "mfa_delete" 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-no-versioning.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Buckets", 3 | "description": "Bucket without versioning", 4 | "path": "s3.buckets.id", 5 | "conditions": [ "and", 6 | [ "s3.buckets.id.versioning_status", "equal", "Disabled" ] 7 | ], 8 | "id_suffix": "versioning" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-website-enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard_name": "Buckets", 3 | "description": "Bucket with static website enabled", 4 | "path": "s3.buckets.id", 5 | "conditions": [ "and", 6 | [ "s3.buckets.id.web_hosting", "equal", "Enabled" ] 7 | ], 8 | "id_suffix": "web_hosting" 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-world-acl.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Grantee", "Permission", "Description", "Level" ], 3 | "key": "s3-bucket-_ARG_0_-_ARG_1_", 4 | "dashboard_name": "Bucket ACLs", 5 | "description": "_ARG_2_", 6 | "path": "s3.buckets.id.grantees.id", 7 | "display_path": "s3.buckets.id", 8 | "conditions": [ "and", 9 | [ "s3.buckets.id.grantees.id.", "withKey", "URI" ], 10 | [ "s3.buckets.id.grantees.id.URI", "equal", "http://acs.amazonaws.com/groups/global/_ARG_0_" ], 11 | [ "s3.buckets.id.grantees.id.permissions._ARG_1_", "true", "" ] 12 | ], 13 | "id_suffix": "_ARG_1_" 14 | } 15 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-world-policy-arg.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names" : [ "Action shortname", "Service:Action" ], 3 | "key": "s3-bucket-world-_ARG_0_-policy", 4 | "dashboard_name": "Buckets", 5 | "description": "_ARG_0_ actions authorized to all principals", 6 | "path": "s3.buckets.id.policy.Statement.id", 7 | "display_path": "s3.buckets.id", 8 | "conditions": [ "and", 9 | [ "s3.buckets.id.", "withKey", "policy" ], 10 | [ "s3.buckets.id.policy.Statement.id.Effect", "equal", "Allow" ], 11 | [ "s3.buckets.id.policy.Statement.id.", "withoutKey", "Condition" ], 12 | [ "s3.buckets.id.policy.Statement.id.", "containAction", "_ARG_1_" ], 13 | [ "s3.buckets.id.policy.Statement.id.Action", "containNoneOf", [ "s3:*", "*" ] ], 14 | [ "or", 15 | [ "s3.buckets.id.policy.Statement.id.Principal", "containAtLeastOneOf", [ "*" ] ], 16 | [ "and", 17 | [ "s3.buckets.id.policy.Statement.id.Principal", "withKey", "AWS" ], 18 | [ "s3.buckets.id.policy.Statement.id.Principal.AWS", "containAtLeastOneOf", [ "*" ] ] 19 | ] 20 | ] 21 | ], 22 | "keys": [ 23 | "s3.buckets.id", 24 | "this", 25 | "s3.buckets.id.policy.Statement.id.Principal" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/s3-bucket-world-policy-star.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names" : [ "Action shortname", "Service:Action" ], 3 | "dashboard_name": "Buckets", 4 | "description": "All actions authorized to all principals", 5 | "path": "s3.buckets.id.policy.Statement.id", 6 | "display_path": "s3.buckets.id", 7 | "conditions": [ "and", 8 | [ "s3.buckets.id.", "withKey", "policy" ], 9 | [ "s3.buckets.id.policy.Statement.id.Effect", "equal", "Allow" ], 10 | [ "s3.buckets.id.policy.Statement.id.", "withoutKey", "Condition" ], 11 | [ "s3.buckets.id.policy.Statement.id.Action", "containAtLeastOneOf", [ "s3:*", "*" ] ], 12 | [ "or", 13 | [ "s3.buckets.id.policy.Statement.id.Principal", "containAtLeastOneOf", [ "*" ] ], 14 | [ "and", 15 | [ "s3.buckets.id.policy.Statement.id.Principal", "withKey", "AWS" ], 16 | [ "s3.buckets.id.policy.Statement.id.Principal.AWS", "containAtLeastOneOf", [ "*" ] ] 17 | ] 18 | ] 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ses-identity-dkim-not-enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "DKIM not enabled", 3 | "rationale": "DKIM signing is not enabled for emails sent from the identity.", 4 | "path": "ses.regions.id.identities.id", 5 | "dashboard_name": "Identities", 6 | "conditions": [ "and", 7 | [ "DkimEnabled", "false", "" ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ses-identity-dkim-not-verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "DKIM not verified", 3 | "rationale": "Amazon SES has not verified the DKIM DNS records (tokens) published in the domain name's DNS.", 4 | "path": "ses.regions.id.identities.id", 5 | "dashboard_name": "Identities", 6 | "conditions": [ "and", 7 | [ "DkimEnabled", "true", "" ], 8 | [ "DkimVerificationStatus", "notEqual", "Success" ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/ses-identity-world-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "SES action" ], 3 | "key": "ses-identity-world-_ARG_0_-policy", 4 | "dashboard_name": "Statements", 5 | "description": "_ARG_0_ authorized to all principals", 6 | "path": "ses.regions.id.identities.id.policies.id.Statement.id", 7 | "display_path": "ses.regions.id.identities.id", 8 | "conditions": [ "and", 9 | [ "ses.regions.id.identities.id.policies.id.Statement.id.Effect", "equal", "Allow" ], 10 | [ "ses.regions.id.identities.id.policies.id.Statement.id.", "containAction", "ses:_ARG_0_" ], 11 | [ "or", 12 | [ "ses.regions.id.identities.id.policies.id.Statement.id.Principal", "containAtLeastOneOf", "*" ], 13 | [ "and", 14 | [ "ses.regions.id.identities.id.policies.id.Statement.id.Principal", "withKey", "AWS" ], 15 | [ "ses.regions.id.identities.id.policies.id.Statement.id.Principal.AWS", "containAtLeastOneOf", "*" ] 16 | ] 17 | ] 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/sns-topic-world-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "SNS action" ], 3 | "key": "sns-topic-world-_ARG_0_-policy", 4 | "dashboard_name": "Statements", 5 | "description": "_ARG_0_ authorized to all principals", 6 | "path": "sns.regions.id.topics.id.Policy.Statement.id", 7 | "display_path": "sns.regions.id.topics.id", 8 | "conditions": [ "and", 9 | [ "sns.regions.id.topics.id.Policy.Statement.id.Effect", "equal", "Allow" ], 10 | [ "sns.regions.id.topics.id.Policy.Statement.id.", "containAction", "SNS:_ARG_0_" ], 11 | [ "sns.regions.id.topics.id.Policy.Statement.id.Principal", "withKey", "AWS" ], 12 | [ "sns.regions.id.topics.id.Policy.Statement.id.Principal.AWS", "containAtLeastOneOf", "*" ], 13 | [ "or", 14 | [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], 15 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "StringEquals" ], 16 | [ "and", 17 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "aws:SourceArn" ], 18 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "aws:SourceOwner" ], 19 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "AWS:SourceArn" ], 20 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "AWS:SourceOwner" ] 21 | ] 22 | ], 23 | [ "or", 24 | [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], 25 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "ArnLike" ], 26 | [ "and", 27 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "aws:SourceArn" ], 28 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "aws:SourceOwner" ], 29 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "AWS:SourceArn" ], 30 | [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "AWS:SourceOwner" ] 31 | ] 32 | ] 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/sqs-queue-world-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "SQS action" ], 3 | "key": "sqs-queue-world-_ARG_0_-policy", 4 | "dashboard_name": "Statements", 5 | "description": "_ARG_0_ authorized to all principals", 6 | "path": "sqs.regions.id.queues.id.Policy.Statement.id", 7 | "display_path": "sqs.regions.id.queues.id", 8 | "conditions": [ "and", 9 | [ "sqs.regions.id.queues.id.Policy", "notNull", ""], 10 | [ "sqs.regions.id.queues.id.Policy.Statement.id.Effect", "equal", "Allow" ], 11 | [ "sqs.regions.id.queues.id.Policy.Statement.id.", "containAction", "sqs:_ARG_0_" ], 12 | [ "_INCLUDE_(conditions/policy-statement-any-principal.json)", [ "_STATEMENT_" ], [ "sqs.regions.id.queues.id.Policy.Statement.id" ] ], 13 | [ "_INCLUDE_(conditions/policy-statement-poor-condition.json)", [ "_STATEMENT_" ], [ "sqs.regions.id.queues.id.Policy.Statement.id" ] ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/vpc-custom-network-acls-allow-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Direction of traffic (ingress or egress)", "Corresponding target (source or destination)" ], 3 | "description": "Network ACLs allow all _ARG_0_ traffic (custom)", 4 | "rationale": "Network ACLs are designed to provide a secondary layer of security. Adding a rule that allows all network traffic (all protocols, IPs, and _ARG_1_) prior to any deny rule defeats the purpose of network ACLs.", 5 | "key": "vpc-custom-network-acls-allow-all-_ARG_0_", 6 | "path": "vpc.regions.id.vpcs.id.network_acls.id", 7 | "dashboard_name": "Network ACLs", 8 | "id_suffix": "_ARG_0_._GET_VALUE_AT_(vpc.regions.id.vpcs.id.network_acls.id.allow_all__ARG_0__traffic)", 9 | "conditions": [ "and", 10 | [ "allow_all__ARG_0__traffic", "notEqual", "0" ], 11 | [ "use_default__ARG_0__rules", "false", ""] 12 | ] 13 | } -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/vpc-default-network-acls-allow-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Direction of traffic (ingress or egress)", "Corresponding target (source or destination)" ], 3 | "description": "Network ACLs allow all _ARG_0_ traffic (default)", 4 | "rationale": "Network ACLs are designed to provide a secondary layer of security. Adding a rule that allows all network traffic (all protocols, IPs, and _ARG_1_) prior to any deny rule defeats the purpose of network ACLs.", 5 | "key": "vpc-default-network-acls-allow-all-_ARG_0_", 6 | "path": "vpc.regions.id.vpcs.id.network_acls.id", 7 | "dashboard_name": "Network ACLs", 8 | "id_suffix": "_ARG_0_._GET_VALUE_AT_(vpc.regions.id.vpcs.id.network_acls.id.allow_all__ARG_0__traffic)", 9 | "conditions": [ "and", 10 | [ "allow_all__ARG_0__traffic", "notEqual", "0" ], 11 | [ "use_default__ARG_0__rules", "true", ""] 12 | ] 13 | } -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/vpc-network-acl-not-used.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Unused network ACLs", 3 | "rationale": "Maintaining unused resources increases risks of misconfigurations and increases the difficulty of audits.", 4 | "path": "vpc.regions.id.vpcs.id.network_acls.id", 5 | "dashboard_name": "Network ACLs", 6 | "id_suffix": "unused", 7 | "conditions": [ "and", 8 | [ "IsDefault", "false", "" ], 9 | [ "Associations", "empty", "" ] 10 | ] 11 | } -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/vpc-subnet-with-bad-acls.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Direction of traffic (ingress or egress)", "Corresponding target (source or destination)" ], 3 | "description": "Subnet with allow all _ARG_0_ NACLs", 4 | "rationale": "Network ACLs associated with subnets and VPCs should not allow all _ARG_0_ traffic.", 5 | "key": "vpc-subnet-with-allow-all-_ARG_0_-acls", 6 | "path": "vpc.regions.id.vpcs.id.subnets.id", 7 | "dashboard_name": "Subnets", 8 | "id_suffix": "bad_nacls", 9 | "conditions": [ "and", 10 | [ "vpc.regions.id.vpcs.id.network_acls._GET_VALUE_AT_(vpc.regions.id.vpcs.id.subnets.id.network_acl).allow_all__ARG_0__traffic", "notEqual", "0" ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/vpc-subnet-with-default-acls.json: -------------------------------------------------------------------------------- 1 | { 2 | "arg_names": [ "Direction of traffic (ingress or egress)", "Corresponding target (source or destination)" ], 3 | "description": "Subnet with default _ARG_0_ NACLs", 4 | "rationale": "Default _ARG_0_ network access control lists allow all network traffic (all protocols, IPs, and _ARG_1_), hence do not provide the secondary layer of security network ACLs are supposed to.", 5 | "key": "vpc-subnet-with-default-_ARG_0_-acls", 6 | "path": "vpc.regions.id.vpcs.id.subnets.id", 7 | "dashboard_name": "Subnets", 8 | "id_suffix": "DefaultNACLs", 9 | "conditions": [ "and", 10 | [ "vpc.regions.id.vpcs.id.network_acls._GET_VALUE_AT_(vpc.regions.id.vpcs.id.subnets.id.network_acl).use_default__ARG_0__rules", "true", ""] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/findings/vpc-subnet-without-flow-log.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Subnet without a flow log", 3 | "rationale": "Flow logs enable the investigatation of incidents involving unauthorized network traffic, such as an attacker exfiltrating data or pivoting to other hosts.", 4 | "path": "vpc.regions.id.vpcs.id.subnets.id", 5 | "dashboard_name": "Subnets", 6 | "id_suffix": "NoFlowLog", 7 | "conditions": [ "or", 8 | [ "this", "withoutKey", "flow_logs"], 9 | [ "flow_logs", "empty", "" ] 10 | ] 11 | } -------------------------------------------------------------------------------- /AWSScout2/rules/data/rulesets/filters.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "iam-role-for-service.json": [ 4 | { 5 | "args": [ 6 | "EC2", 7 | "ec2.amazonaws.com" 8 | ], 9 | "enabled": true 10 | }, 11 | { 12 | "args": [ 13 | "Lambda", 14 | "lambda.amazonaws.com" 15 | ], 16 | "enabled": true 17 | } 18 | ], 19 | "iam-role-for-aws-account.json": [ 20 | { 21 | "args": [ 22 | "same", 23 | "isSameAccount" 24 | ], 25 | "enabled": true 26 | }, 27 | { 28 | "args": [ 29 | "cross", 30 | "isCrossAccount" 31 | ], 32 | "enabled": true 33 | } 34 | ], 35 | "s3-bucket-website-enabled.json": [ 36 | { 37 | "enabled": true 38 | } 39 | ], 40 | "ec2-instance-with-open-nacls.json": [ 41 | { 42 | "enabled": true 43 | } 44 | ], 45 | "ec2-security-group-with-public-cidr-grant.json": [ 46 | { 47 | "enabled": true 48 | } 49 | ] 50 | }, 51 | "about": "Default set of filters for Scout2" 52 | } 53 | -------------------------------------------------------------------------------- /AWSScout2/rules/data/rulesets/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Placeholder", 3 | "rules": { 4 | "rule-name.json": [ 5 | { 6 | "level": "danger", 7 | "filename": "rule-name.json", 8 | "enabled": true 9 | } 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /AWSScout2/rules/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Exceptions handling 4 | """ 5 | 6 | import json 7 | 8 | from opinel.utils.console import printDebug 9 | 10 | from AWSScout2 import EXCEPTIONS 11 | from AWSScout2.output.js import JavaScriptReaderWriter 12 | 13 | class RuleExceptions(object): 14 | 15 | def __init__(self, profile, file_path = None, foobar = None): 16 | self.profile = profile 17 | self.file_path = file_path 18 | self.jsrw = JavaScriptReaderWriter(self.profile) 19 | self.exceptions = self.jsrw.load_from_file(config_type = EXCEPTIONS, config_path = self.file_path, first_line = True) 20 | 21 | def process(self, aws_config): 22 | for service in self.exceptions: 23 | for rule in self.exceptions[service]: 24 | filtered_items = [] 25 | if rule not in aws_config['services'][service]['findings']: 26 | printDebug('Warning:: key error should not be happening') 27 | continue 28 | for item in aws_config['services'][service]['findings'][rule]['items']: 29 | if item not in self.exceptions[service][rule]: 30 | filtered_items.append(item) 31 | aws_config['services'][service]['findings'][rule]['items'] = filtered_items 32 | aws_config['services'][service]['findings'][rule]['flagged_items'] = len(aws_config['services'][service]['findings'][rule]['items']) 33 | -------------------------------------------------------------------------------- /AWSScout2/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nccgroup/Scout2/5d86d46d7ed91a92000496189e9cfa6b98243937/AWSScout2/services/__init__.py -------------------------------------------------------------------------------- /AWSScout2/services/awslambda.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Lambda-related classes and functions 4 | """ 5 | 6 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig 7 | 8 | 9 | 10 | ######################################## 11 | # LambdaRegionConfig 12 | ######################################## 13 | 14 | class LambdaRegionConfig(RegionConfig): 15 | 16 | def parse_function(self, global_params, region, function): 17 | function['name'] = function.pop('FunctionName') 18 | self.functions[function['name']] = function 19 | 20 | 21 | 22 | ######################################## 23 | # LambdaConfig 24 | ######################################## 25 | 26 | class LambdaConfig(RegionalServiceConfig): 27 | """ 28 | Lambda configuration for all AWS regions 29 | """ 30 | 31 | region_config_class = LambdaRegionConfig 32 | 33 | def __init__(self, service_metadata, thread_config = 4): 34 | super(LambdaConfig, self).__init__(service_metadata, thread_config) 35 | -------------------------------------------------------------------------------- /AWSScout2/services/cloudformation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import json 4 | 5 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig, api_clients 6 | 7 | 8 | 9 | ######################################## 10 | # CloudFormationRegionConfig 11 | ######################################## 12 | 13 | class CloudFormationRegionConfig(RegionConfig): 14 | """ 15 | CloudFormation configuration for a single AWS region 16 | """ 17 | 18 | def parse_stack(self, global_params, region, stack): 19 | """ 20 | Parse a single stack and fetch additional attributes 21 | 22 | :param global_params: Parameters shared for all regions 23 | :param region: Name of the AWS region 24 | :param stack_url: URL of the AWS stack 25 | """ 26 | stack['id'] = stack.pop('StackId') 27 | stack['name'] = stack.pop('StackName') 28 | stack_policy = api_clients[region].get_stack_policy(StackName = stack['name']) 29 | if 'StackPolicyBody' in stack_policy: 30 | stack['policy'] = json.loads(stack_policy['StackPolicyBody']) 31 | self.stacks[stack['name']] = stack 32 | 33 | 34 | 35 | ######################################## 36 | # CloudFormationConfig 37 | ######################################## 38 | 39 | class CloudFormationConfig(RegionalServiceConfig): 40 | """ 41 | CloudFormation configuration for all AWS regions 42 | """ 43 | 44 | region_config_class = CloudFormationRegionConfig 45 | 46 | def __init__(self, service_metadata, thread_config = 4): 47 | super(CloudFormationConfig, self).__init__(service_metadata, thread_config) 48 | -------------------------------------------------------------------------------- /AWSScout2/services/cloudwatch.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | CloudWatch-related classes and functions 4 | """ 5 | 6 | from opinel.utils.globals import manage_dictionary 7 | 8 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig 9 | 10 | 11 | 12 | ######################################## 13 | # CloudWatchRegionConfig 14 | ######################################## 15 | 16 | class CloudWatchRegionConfig(RegionConfig): 17 | """ 18 | CloudWatch configuration for a single AWS region 19 | """ 20 | 21 | def parse_alarm(self, global_params, region, alarm): 22 | """ 23 | Parse a single CloudWatch trail 24 | 25 | :param global_params: Parameters shared for all regions 26 | :param region: Name of the AWS region 27 | :param alarm: Alarm 28 | """ 29 | alarm['arn'] = alarm.pop('AlarmArn') 30 | alarm['name'] = alarm.pop('AlarmName') 31 | # Drop some data 32 | for k in ['AlarmConfigurationUpdatedTimestamp', 'StateReason', 'StateReasonData', 'StateUpdatedTimestamp']: 33 | foo = alarm.pop(k) if k in alarm else None 34 | alarm_id = self.get_non_aws_id(alarm['arn']) 35 | self.alarms[alarm_id] = alarm 36 | 37 | 38 | 39 | ######################################## 40 | # CloudWatchConfig 41 | ######################################## 42 | 43 | class CloudWatchConfig(RegionalServiceConfig): 44 | """ 45 | CloudWatch configuration for all AWS regions 46 | """ 47 | 48 | region_config_class = CloudWatchRegionConfig 49 | 50 | def __init__(self, service_metadata, thread_config = 4): 51 | super(CloudWatchConfig, self).__init__(service_metadata, thread_config) 52 | -------------------------------------------------------------------------------- /AWSScout2/services/directconnect.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig 4 | 5 | 6 | 7 | ######################################## 8 | # DirectConnectRegionConfig 9 | ######################################## 10 | 11 | class DirectConnectRegionConfig(RegionConfig): 12 | """ 13 | DirectConnect configuration for a single AWS region 14 | """ 15 | 16 | def parse_connection(self, global_params, region, connection): 17 | """ 18 | Parse a single connection and fetch additional attributes 19 | 20 | :param global_params: Parameters shared for all regions 21 | :param region: Name of the AWS region 22 | :param connection_url: URL of the AWS connection 23 | """ 24 | connection['id'] = connection.pop('connectionId') 25 | connection['name'] = connection.pop('connectionName') 26 | self.connections[connection['id']] = connection 27 | 28 | 29 | 30 | ######################################## 31 | # DirectConnectConfig 32 | ######################################## 33 | 34 | class DirectConnectConfig(RegionalServiceConfig): 35 | """ 36 | DirectConnect configuration for all AWS regions 37 | """ 38 | 39 | region_config_class = DirectConnectRegionConfig 40 | 41 | def __init__(self, service_metadata, thread_config = 4): 42 | super(DirectConnectConfig, self).__init__(service_metadata, thread_config) 43 | -------------------------------------------------------------------------------- /AWSScout2/services/efs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | EFS-related classes and functions 4 | """ 5 | from opinel.utils.aws import get_name, handle_truncated_response 6 | 7 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig, api_clients 8 | 9 | 10 | 11 | ######################################## 12 | # EFSRegionConfig 13 | ######################################## 14 | 15 | class EFSRegionConfig(RegionConfig): 16 | """ 17 | EFS configuration for a single AWS region 18 | """ 19 | 20 | def parse_file_system(self, global_params, region, file_system): 21 | """ 22 | 23 | :param global_params: 24 | :param region: 25 | :param file_system: 26 | :return: 27 | """ 28 | fs_id = file_system.pop('FileSystemId') 29 | file_system['name'] = file_system.pop('Name') 30 | # Get tags 31 | file_system['tags'] = handle_truncated_response(api_clients[region].describe_tags, {'FileSystemId': fs_id}, ['Tags'])['Tags'] 32 | # Get mount targets 33 | mount_targets = handle_truncated_response(api_clients[region].describe_mount_targets, {'FileSystemId': fs_id}, ['MountTargets'])['MountTargets'] 34 | file_system['mount_targets'] = {} 35 | for mt in mount_targets: 36 | mt_id = mt['MountTargetId'] 37 | file_system['mount_targets'][mt_id] = mt 38 | # Get security groups 39 | file_system['mount_targets'][mt_id]['security_groups'] = api_clients[region].describe_mount_target_security_groups(MountTargetId = mt_id)['SecurityGroups'] 40 | self.file_systems[fs_id] = file_system 41 | 42 | 43 | 44 | ######################################## 45 | # EFSConfig 46 | ######################################## 47 | 48 | class EFSConfig(RegionalServiceConfig): 49 | """ 50 | EFS configuration for all AWS regions 51 | """ 52 | 53 | region_config_class = EFSRegionConfig 54 | 55 | def __init__(self, service_metadata, thread_config = 4): 56 | super(EFSConfig, self).__init__(service_metadata, thread_config) 57 | -------------------------------------------------------------------------------- /AWSScout2/services/elasticache.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from opinel.utils.globals import manage_dictionary 4 | 5 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig, api_clients 6 | from AWSScout2.configs.vpc import VPCConfig 7 | from AWSScout2.utils import ec2_classic 8 | 9 | 10 | 11 | ######################################## 12 | # ElastiCacheRegionConfig 13 | ######################################## 14 | 15 | class ElastiCacheRegionConfig(RegionConfig): 16 | """ 17 | ElastiCache configuration for a single AWS region 18 | """ 19 | 20 | def parse_cluster(self, global_params, region, cluster): 21 | """ 22 | Parse a single ElastiCache cluster 23 | 24 | :param global_params: Parameters shared for all regions 25 | :param region: Name of the AWS region 26 | :param cluster: ElastiCache cluster 27 | """ 28 | cluster_name = cluster.pop('CacheClusterId') 29 | cluster['name'] = cluster_name 30 | # Must fetch info about the subnet group to retrieve the VPC ID... 31 | if 'CacheSubnetGroupName' in cluster: 32 | subnet_group = api_clients[region].describe_cache_subnet_groups(CacheSubnetGroupName = cluster['CacheSubnetGroupName'])['CacheSubnetGroups'][0] 33 | vpc_id = subnet_group['VpcId'] 34 | else: 35 | vpc_id = ec2_classic 36 | subnet_group = None 37 | manage_dictionary(self.vpcs, vpc_id, VPCConfig(self.vpc_resource_types)) 38 | self.vpcs[vpc_id].clusters[cluster_name] = cluster 39 | if subnet_group: 40 | self.vpcs[vpc_id].subnet_groups[subnet_group['CacheSubnetGroupName']] = subnet_group 41 | 42 | 43 | def parse_security_group(self, global_params, region, security_group): 44 | """ 45 | Parse a single ElastiCache security group 46 | 47 | :param global_params: 48 | :param region: 49 | :param security_group: 50 | :return: 51 | """ 52 | security_group['name'] = security_group.pop('CacheSecurityGroupName') 53 | self.security_groups[security_group['name']] = security_group 54 | 55 | 56 | 57 | ######################################## 58 | # ElastiCacheConfig 59 | ######################################## 60 | 61 | class ElastiCacheConfig(RegionalServiceConfig): 62 | """ 63 | ElastiCache configuration for all AWS regions 64 | """ 65 | 66 | region_config_class = ElastiCacheRegionConfig 67 | 68 | def __init__(self, service_metadata, thread_config = 4): 69 | super(ElastiCacheConfig, self).__init__(service_metadata, thread_config) 70 | -------------------------------------------------------------------------------- /AWSScout2/services/emr.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from opinel.utils.globals import manage_dictionary 4 | 5 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig, api_clients 6 | from AWSScout2.configs.vpc import VPCConfig 7 | 8 | 9 | 10 | ######################################## 11 | # EMRRegionConfig 12 | ######################################## 13 | 14 | class EMRRegionConfig(RegionConfig): 15 | """ 16 | EMR configuration for a single AWS region 17 | """ 18 | 19 | def parse_cluster(self, global_params, region, cluster): 20 | """ 21 | Parse a single EMR cluster 22 | 23 | :param global_params: Parameters shared for all regions 24 | :param region: Name of the AWS region 25 | :param cluster: EMR cluster 26 | """ 27 | cluster_id = cluster['Id'] 28 | cluster = api_clients[region].describe_cluster(ClusterId = cluster_id)['Cluster'] 29 | cluster['id'] = cluster.pop('Id') 30 | cluster['name'] = cluster.pop('Name') 31 | vpc_id = 'TODO' # The EMR API won't disclose the VPC ID, so wait until all configs have been fetch and look up the VPC based on the subnet ID 32 | manage_dictionary(self.vpcs, vpc_id, VPCConfig(self.vpc_resource_types)) 33 | self.vpcs[vpc_id].clusters[cluster_id] = cluster 34 | 35 | 36 | ######################################## 37 | # EMRConfig 38 | ######################################## 39 | 40 | class EMRConfig(RegionalServiceConfig): 41 | """ 42 | EMR configuration for all AWS regions 43 | """ 44 | 45 | region_config_class = EMRRegionConfig 46 | 47 | def __init__(self, service_metadata, thread_config = 4): 48 | super(EMRConfig, self).__init__(service_metadata, thread_config) 49 | -------------------------------------------------------------------------------- /AWSScout2/services/ses.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import json 4 | 5 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig, api_clients 6 | 7 | 8 | 9 | ######################################## 10 | # SESRegionConfig 11 | ######################################## 12 | 13 | class SESRegionConfig(RegionConfig): 14 | """ 15 | SES configuration for a single AWS region 16 | """ 17 | 18 | def parse_identitie(self, global_params, region, identity_name): 19 | """ 20 | Parse a single identity and fetch additional attributes 21 | 22 | :param global_params: Parameters shared for all regions 23 | :param region: Name of the AWS region 24 | """ 25 | identity = {'name': identity_name, 'policies': {}} 26 | policy_names = api_clients[region].list_identity_policies(Identity = identity_name)['PolicyNames'] 27 | if len(policy_names): 28 | policies = api_clients[region].get_identity_policies(Identity = identity_name, PolicyNames = policy_names)['Policies'] 29 | for policy_name in policies: 30 | identity['policies'][policy_name] = json.loads(policies[policy_name]) 31 | dkim = api_clients[region].get_identity_dkim_attributes(Identities = [ identity_name ])['DkimAttributes'][identity_name] 32 | identity['DkimEnabled'] = dkim['DkimEnabled'] 33 | identity['DkimVerificationStatus'] = dkim['DkimVerificationStatus'] 34 | self.identities[self.get_non_aws_id(identity_name)] = identity 35 | 36 | 37 | 38 | ######################################## 39 | # SESConfig 40 | ######################################## 41 | 42 | class SESConfig(RegionalServiceConfig): 43 | """ 44 | SES configuration for all AWS regions 45 | """ 46 | 47 | region_config_class = SESRegionConfig 48 | 49 | def __init__(self, service_metadata, thread_config = 4): 50 | super(SESConfig, self).__init__(service_metadata, thread_config) 51 | -------------------------------------------------------------------------------- /AWSScout2/services/sqs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import json 4 | 5 | from AWSScout2.configs.regions import RegionalServiceConfig, RegionConfig, api_clients 6 | 7 | 8 | 9 | ######################################## 10 | # SQSRegionConfig 11 | ######################################## 12 | 13 | class SQSRegionConfig(RegionConfig): 14 | """ 15 | SQS configuration for a single AWS region 16 | """ 17 | 18 | def parse_queue(self, global_params, region, queue_url): 19 | """ 20 | Parse a single queue and fetch additional attributes 21 | 22 | :param global_params: Parameters shared for all regions 23 | :param region: Name of the AWS region 24 | :param queue_url: URL of the AWS queue 25 | """ 26 | queue = {'QueueUrl': queue_url} 27 | attributes = api_clients[region].get_queue_attributes(QueueUrl = queue_url, AttributeNames = ['CreatedTimestamp', 'Policy', 'QueueArn'])['Attributes'] 28 | queue['arn'] = attributes.pop('QueueArn') 29 | for k in ['CreatedTimestamp']: 30 | queue[k] = attributes[k] if k in attributes else None 31 | if 'Policy' in attributes: 32 | queue['Policy'] = json.loads(attributes['Policy']) 33 | else: 34 | queue['Policy'] = {'Statement': []} 35 | 36 | queue['name'] = queue['arn'].split(':')[-1] 37 | self.queues[queue['name']] = queue 38 | 39 | 40 | 41 | ######################################## 42 | # SQSConfig 43 | ######################################## 44 | 45 | class SQSConfig(RegionalServiceConfig): 46 | """ 47 | SQS configuration for all AWS regions 48 | """ 49 | 50 | region_config_class = SQSRegionConfig 51 | 52 | def __init__(self, service_metadata, thread_config = 4): 53 | super(SQSConfig, self).__init__(service_metadata, thread_config) 54 | -------------------------------------------------------------------------------- /AWSScout2/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | 4 | import re 5 | 6 | 7 | 8 | ######################################## 9 | # Globals 10 | ######################################## 11 | 12 | ec2_classic = 'EC2-Classic' 13 | 14 | formatted_service_name = { 15 | 'cloudformation': 'CloudFormation', 16 | 'cloudtrail': 'CloudTrail', 17 | 'cloudwatch': 'CloudWatch', 18 | 'directconnect': 'Direct Connect', 19 | 'elasticache': 'ElastiCache', 20 | 'lambda': 'Lambda', 21 | 'redshift': 'RedShift', 22 | 'route53': 'Route53', 23 | 'route53domains': 'Route53Domains' 24 | } 25 | 26 | 27 | ######################################## 28 | # Functions 29 | ######################################## 30 | 31 | def format_service_name(service): 32 | """ 33 | 34 | :param service: 35 | :return: 36 | """ 37 | return formatted_service_name[service] if service in formatted_service_name else service.upper() 38 | 39 | 40 | def get_keys(src, dst, keys): 41 | """ 42 | Copies the value of keys from source object to dest object 43 | 44 | :param src: 45 | :param dst: 46 | :param keys: 47 | :return: 48 | """ 49 | for key in keys: 50 | #dst[no_camel(key)] = src[key] if key in src else None 51 | dst[key] = src[key] if key in src else None 52 | 53 | 54 | def no_camel(name): 55 | """ 56 | Converts CamelCase to camel_case 57 | 58 | :param name: 59 | :return: 60 | """ 61 | s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) 62 | return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() 63 | 64 | 65 | def is_throttled(e): 66 | """ 67 | Determines whether the exception is due to API throttling. 68 | 69 | :param e: Exception raised 70 | :return: True if it's a throttling exception else False 71 | """ 72 | return True if (hasattr(e, 'response') and 73 | e.response is not None and 74 | 'Error' in e.response and 75 | e.response['Error']['Code'] in ['Throttling', 'RequestLimitExceeded', 'ThrottlingException']) else \ 76 | False 77 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include requirements.txt 4 | recursive-include AWSScout2/data * 5 | recursive-include AWSScout2/configs/data * 6 | recursive-include AWSScout2/output/data * 7 | recursive-include AWSScout2/rules/data * 8 | 9 | -------------------------------------------------------------------------------- /Scout2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from AWSScout2.__main__ import main 5 | import sys 6 | 7 | if __name__ == '__main__': 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Scout2Listall.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from AWSScout2.__listall__ import main 5 | import sys 6 | 7 | if __name__ == '__main__': 8 | sys.exit(main()) -------------------------------------------------------------------------------- /Scout2RulesGenerator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from AWSScout2.__rules_generator__ import main 5 | import sys 6 | 7 | if __name__ == '__main__': 8 | sys.exit(main()) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | AWSScout2/data/requirements.txt -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # distutils/setuptools install script for Scout2 4 | import os 5 | from setuptools import setup, find_packages 6 | 7 | # Package info 8 | NAME = 'AWSScout2' 9 | ROOT = os.path.dirname(__file__) 10 | VERSION = __import__(NAME).__version__ 11 | 12 | # Requirements 13 | requirements = [] 14 | with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'AWSScout2', 'data', 'requirements.txt')) as f: 15 | for r in f.readlines(): 16 | requirements.append(r.strip()) 17 | 18 | # Setup 19 | setup( 20 | name=NAME, 21 | version=VERSION, 22 | description='Scout2, TODO', 23 | long_description=open('README.rst').read(), 24 | author='l01cd3v', 25 | author_email='l01cd3v@gmail.com', 26 | url='https://github.com/nccgroup/Scout2', 27 | entry_points={ 28 | 'console_scripts': [ 29 | 'Scout2 = AWSScout2.__main__:main', 30 | 'Scout2RulesGenerator = AWSScout2.__rules_generator__:main', 31 | 'Scout2Listall = AWSScout2.__listall__:main' 32 | ] 33 | }, 34 | packages=[ 35 | 'AWSScout2', 'AWSScout2.configs', 'AWSScout2.output', 'AWSScout2.rules', 'AWSScout2.services' 36 | ], 37 | package_data={ 38 | 'AWSScout2': [ 39 | 'data/requirements.txt' 40 | ], 41 | 'AWSScout2.configs': [ 42 | 'data/*.json' 43 | ], 44 | 'AWSScout2.output': [ 45 | 'data/html/*.html', 46 | 'data/html/partials/*.html', 47 | 'data/html/summaries/*.html', 48 | 'data/includes.zip', 49 | 'data/inc-scout2/*.js', 50 | 'data/inc-scout2/*.css' 51 | ], 52 | 'AWSScout2.rules': [ 53 | 'data/*.html', 54 | 'data/filters/*.json', 55 | 'data/findings/*.json', 56 | 'data/rulesets/*.json' 57 | ] 58 | }, 59 | include_package_data=True, 60 | install_requires=requirements, 61 | license='GNU General Public License v2 (GPLv2)', 62 | classifiers=[ 63 | 'Development Status :: 4 - Beta', 64 | 'Intended Audience :: Developers', 65 | 'Intended Audience :: Information Technology', 66 | 'Intended Audience :: System Administrators', 67 | 'Natural Language :: English', 68 | 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', 69 | 'Programming Language :: Python', 70 | 'Programming Language :: Python :: 2.7', 71 | 'Programming Language :: Python :: 3', 72 | 'Programming Language :: Python :: 3.3', 73 | 'Programming Language :: Python :: 3.4' 74 | ], 75 | ) 76 | -------------------------------------------------------------------------------- /tests/data/invalid-file.json: -------------------------------------------------------------------------------- 1 | {this is not a valid JSON file} 2 | -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-default-security-group-in-use.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-default-security-group-with-rules.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-security-group-opens-all-ports-to-all.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-security-group-opens-all-ports-to-self.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-security-group-opens-all-ports.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-security-group-opens-known-port-to-all.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-security-group-opens-port-range.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-security-group-opens-port-to-all.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-security-group-whitelists-aws-ip-from-banned-region.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/ec2-security-group-whitelists-aws.json: -------------------------------------------------------------------------------- 1 | ec2.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-expiration-threshold.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-lowercase-required.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-minimum-length.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-no-expiration.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-no-lowercase-required.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-no-number-required.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-no-symbol-required.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-no-uppercase-required.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy-reuse-enabled.json: -------------------------------------------------------------------------------- 1 | iam-password-policy.json -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-password-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "aws_account_id": "123456789012", 3 | "services": { 4 | "iam": { 5 | "password_policy": { 6 | "ExpirePasswords": false, 7 | "MinimumPasswordLength": "1", 8 | "PasswordReusePrevention": false, 9 | "RequireLowercaseCharacters": false, 10 | "RequireNumbers": false, 11 | "RequireSymbols": false, 12 | "RequireUppercaseCharacters": false 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/data/rule-configs/iam-root.json: -------------------------------------------------------------------------------- 1 | { 2 | "aws_account_id": "123456789012", 3 | "services": { 4 | "iam": { 5 | "credential_report": { 6 | "": { 7 | "access_key_1_active": "false", 8 | "access_key_1_last_rotated": "N/A", 9 | "access_key_1_last_used_date": "N/A", 10 | "access_key_1_last_used_region": "N/A", 11 | "access_key_1_last_used_service": "N/A", 12 | "access_key_2_active": "false", 13 | "access_key_2_last_rotated": "N/A", 14 | "access_key_2_last_used_date": "N/A", 15 | "access_key_2_last_used_region": "N/A", 16 | "access_key_2_last_used_service": "N/A", 17 | "arn": "arn:aws:iam::123456789012:root", 18 | "cert_1_active": "false", 19 | "cert_1_last_rotated": "N/A", 20 | "cert_2_active": "false", 21 | "cert_2_last_rotated": "N/A", 22 | "mfa_active": "false", 23 | "password_enabled": "not_supported", 24 | "password_last_changed": "not_supported", 25 | "password_last_used": "no_information", 26 | "password_next_rotation": "not_supported", 27 | "user": "", 28 | "user_creation_time": "2017-09-06T13:19:23+00:00" 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-default-security-group-in-use.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.ap-south-1.vpcs.vpc-ap222222.security_groups.sg-ap222222.default_in_use" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-default-security-group-with-rules.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.ap-northeast-2.vpcs.vpc-ap111111.security_groups.sg-ap111111.rules.ingress.default_with_rules" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-security-group-opens-all-ports-to-all.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.sa-east-1.vpcs.vpc-sa111111.security_groups.sg-sa33333333.rules.ingress.protocols.ALL.ports.N/A.cidrs.0.CIDR" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-security-group-opens-all-ports-to-self.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.ap-northeast-2.vpcs.vpc-ap111111.security_groups.sg-ap111111.rules.ingress.protocols.ALL.ports.N/A.security_groups.0" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-security-group-opens-all-ports.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.ap-northeast-2.vpcs.vpc-ap111111.security_groups.sg-ap111111.rules.ingress.protocols.ALL.ports.N/A", 3 | "ec2.regions.sa-east-1.vpcs.vpc-sa111111.security_groups.sg-sa222222.rules.ingress.protocols.ALL.ports.N/A", 4 | "ec2.regions.sa-east-1.vpcs.vpc-sa111111.security_groups.sg-sa33333333.rules.ingress.protocols.ALL.ports.N/A" 5 | ] 6 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-security-group-opens-known-port-to-all.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.sa-east-1.vpcs.vpc-sa111111.security_groups.sg-sa33333333.rules.ingress.protocols.TCP.ports.3306.cidrs.0.CIDR" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-security-group-opens-port-range.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.eu-west-1.vpcs.vpc-eu222222.security_groups.sg-eu222222.rules.ingress.protocols.TCP.ports.10-30" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-security-group-opens-port-to-all.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.sa-east-1.vpcs.vpc-sa111111.security_groups.sg-sa33333333.rules.ingress.protocols.TCP.ports.42.cidrs.0.CIDR" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-security-group-whitelists-aws-ip-from-banned-region.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.eu-west-1.vpcs.vpc-eu222222.security_groups.sg-eu222222.rules.ingress.protocols.TCP.ports.43.cidrs.0.CIDR" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/ec2-security-group-whitelists-aws.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ec2.regions.eu-west-1.vpcs.vpc-eu222222.security_groups.sg-eu222222.rules.ingress.protocols.TCP.ports.42.cidrs.0.CIDR", 3 | "ec2.regions.eu-west-1.vpcs.vpc-eu222222.security_groups.sg-eu222222.rules.ingress.protocols.TCP.ports.43.cidrs.0.CIDR" 4 | ] 5 | -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-expiration-threshold.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.MaxPasswordAge" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-lowercase-required.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.RequireLowercaseCharacters" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-minimum-length.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.MinimumPasswordLength" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-no-expiration.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.ExpirePasswords" 3 | ] -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-no-lowercase-required.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.RequireLowercaseCharacters" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-no-number-required.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.RequireNumbers" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-no-symbol-required.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.RequireSymbols" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-no-uppercase-required.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.RequireUppercaseCharacters" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/rule-results/iam-password-policy-reuse-enabled.json: -------------------------------------------------------------------------------- 1 | [ 2 | "iam.password_policy.PasswordReusePrevention" 3 | ] 4 | -------------------------------------------------------------------------------- /tests/data/test-ruleset.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "iam-password-policy-no-expiration.json": [ 4 | { 5 | "enabled": true, 6 | "level": "danger" 7 | } 8 | ] 9 | }, 10 | "about": "regression test" 11 | } 12 | -------------------------------------------------------------------------------- /tests/test-listall.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | # 4 | # Tests for ListAll.py 5 | # 6 | class TestListAllClass: 7 | 8 | # 9 | # Make sure that ListAll does not crash with --help 10 | # 11 | def test_listall_help(self): 12 | command = './ListAll.py --help' 13 | process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) 14 | process.wait() 15 | assert process.returncode == 0 16 | -------------------------------------------------------------------------------- /tests/test-rules-ruleset.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from opinel.utils.console import configPrintException, printDebug 6 | 7 | from AWSScout2.rules.rule import Rule 8 | from AWSScout2.rules.ruleset import Ruleset 9 | 10 | class TestAWSScout2RulesRuleset: 11 | 12 | def setup(self): 13 | configPrintException(True) 14 | self.test_dir = os.path.dirname(os.path.realpath(__file__)) 15 | self.test_ruleset_001 = os.path.join(self.test_dir, 'data/test-ruleset.json') 16 | self.test_ruleset_002 = os.path.join(self.test_dir, 'data/test-ruleset-absolute-path.json') 17 | 18 | 19 | def test_ruleset_class(self): 20 | test001 = Ruleset(filename = self.test_ruleset_001) 21 | assert('iam-password-policy-no-expiration.json' in test001.rules) 22 | assert(type(test001.rules['iam-password-policy-no-expiration.json']) == list) 23 | assert(type(test001.rules['iam-password-policy-no-expiration.json'][0] == Rule)) 24 | assert(hasattr(test001.rules['iam-password-policy-no-expiration.json'][0], 'path')) 25 | for rule in test001.rules: 26 | printDebug(test001.rules[rule][0].to_string()) 27 | test002 = Ruleset(filename = self.test_ruleset_002) 28 | for rule in test002.rules: 29 | printDebug(test002.rules[rule][0].to_string()) 30 | test003 = Ruleset(filename = 'tests/data/no-such-file.json') 31 | assert(test003.rules == []) 32 | test004 = Ruleset(filename = 'tests/data/invalid-file.json') 33 | test005 = Ruleset(filename = self.test_ruleset_001, ruleset_generator = True) 34 | 35 | 36 | def test_find_file(self): 37 | test101 = Ruleset().find_file(self.test_ruleset_001) 38 | test102 = Ruleset().find_file('default') 39 | 40 | 41 | def test_search_ruleset(self): 42 | test201 = Ruleset().search_ruleset('test', no_prompt = True) 43 | 44 | -------------------------------------------------------------------------------- /tests/test-rulesgenerator.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | # 4 | # Tests for RulesGenerator.py 5 | # 6 | class TestRulesGeneratorClass: 7 | 8 | # 9 | # Make sure that RulesGenerator does not crash with --help 10 | # 11 | def test_rulesgenerator_help(self): 12 | command = './RulesGenerator.py --help' 13 | process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) 14 | process.wait() 15 | assert process.returncode == 0 16 | -------------------------------------------------------------------------------- /tests/test-utils.py: -------------------------------------------------------------------------------- 1 | # Import AWS utils 2 | from AWSScout2.utils import * 3 | 4 | 5 | # 6 | # Test methods for AWSScout2/utils.py 7 | # 8 | class TestAWSScout2UtilsClass: 9 | 10 | def test_format_service_name(self): 11 | assert (format_service_name('iAm') == 'IAM') 12 | assert (format_service_name('cloudformation') == 'CloudFormation') 13 | 14 | 15 | def test_get_keys(self): 16 | test1 = {'a': 'b', 'c': 'd'} 17 | test2 = {'a': '', 'e': 'f'} 18 | get_keys(test1, test2, 'a') 19 | assert (test2['a'] == 'b') 20 | assert ('c' not in test2) 21 | get_keys(test1, test2, 'c') 22 | assert (test2['c'] == 'd') 23 | 24 | 25 | def test_no_camel(self): 26 | assert (no_camel('TestTest') == 'test_test') 27 | 28 | 29 | def test_is_throttled(self): 30 | pass 31 | -------------------------------------------------------------------------------- /tests/test-utils_cloudwatch.py: -------------------------------------------------------------------------------- 1 | from AWSScout2.utils_cloudwatch import * 2 | 3 | # 4 | # Test for Scout2 CloudWatch functions 5 | # 6 | class TestScout2CloudWatchUtilsClass: 7 | 8 | configPrintException(True) 9 | 10 | # 11 | # Test get_cloudwatch_region in us-east-1 12 | # 13 | def test_get_cloudwatch_region(self): 14 | # TODO: change to us-east-1 15 | credentials = read_creds('default') 16 | service_config = {'regions': {'us-east-1': {}}} 17 | get_cloudwatch_region(params = {'region': 'us-east-1', 'creds': credentials, 'cloudwatch_config': service_config}) 18 | 19 | # 20 | # Test get_cloudwatch_info (multiple thread of get_cloudwatch_region) 21 | # 1. in us-east-1 and us-west-1 22 | # 2. in empty region intersection 23 | # 24 | def test_get_cloudwatch_info(self): 25 | credentials = read_creds('default') 26 | service_config = {'regions': {'us-east-1': {}, 'us-west-1': {}}} #, 'cn-north-1': {}}} 27 | get_cloudwatch_info(credentials, service_config, ['us-east-1', 'us-west-1'], 'aws') 28 | get_cloudwatch_info(credentials, service_config, ['us-east-1', 'us-west-1'], 'aws-us-gov') 29 | # get_cloudwatch_info(credentials, service_config, ['us-gov-west-1'], 'aws-us-gov') 30 | 31 | # 32 | # Smoke tests for status display functions 33 | # 34 | def test_cloudwatch_status_init(self): 35 | cloudwatch_status_init() 36 | 37 | def test_cloudwatch_status(self): 38 | cloudwatch_status(True) 39 | cloudwatch_status(False) 40 | cloudwatch_status() 41 | 42 | def test_formatted_status(self): 43 | formatted_status(1, 42, True) 44 | formatted_status(42, 1, False) 45 | 46 | 47 | -------------------------------------------------------------------------------- /tests/test-utils_sns.py: -------------------------------------------------------------------------------- 1 | from AWSScout2.utils_sns import * 2 | 3 | # 4 | # Test for Scout2 SNS functions 5 | # 6 | class TestScout2SNSUtilsClass: 7 | 8 | configPrintException(True) 9 | 10 | # 11 | # Test get_sns_region in us-east-1 12 | # 13 | def test_get_sns_region(self): 14 | # TODO: change to us-east-1 15 | credentials = read_creds('default') 16 | service_config = {'regions': {'us-east-1': {}}} 17 | get_sns_region(params = {'region': 'us-east-1', 'creds': credentials, 'sns_config': service_config}) 18 | 19 | # 20 | # Test get_sns_info (multiple thread of get_sns_region) 21 | # 1. in us-east-1 and us-west-1 22 | # 2. in empty region intersection 23 | # 24 | def test_get_sns_info(self): 25 | credentials = read_creds('default') 26 | service_config = {'regions': {'us-east-1': {}, 'us-west-1': {}}} #, 'cn-north-1': {}}} 27 | get_sns_info(credentials, service_config, ['us-east-1', 'us-west-1'], 'aws') 28 | get_sns_info(credentials, service_config, ['us-east-1', 'us-west-1'], 'aws-us-gov') 29 | # get_sns_info(credentials, service_config, ['us-gov-west-1'], 'aws-us-gov') 30 | 31 | # 32 | # Smoke tests for status display functions 33 | # 34 | def test_sns_status_init(self): 35 | sns_status_init() 36 | 37 | def test_sns_status(self): 38 | sns_status(True) 39 | sns_status(False) 40 | sns_status() 41 | 42 | def test_formatted_status(self): 43 | formatted_status(1, 42, True) 44 | formatted_status(42, 1, False) 45 | 46 | 47 | -------------------------------------------------------------------------------- /tools/gen-tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | 5 | scout2_dir = 'AWSScout2' 6 | tests_dir = 'testsbase' 7 | 8 | for root, dirnames, filenames in os.walk(scout2_dir): 9 | for filename in filenames: 10 | if filename.startswith('__') or not filename.endswith('.py'): 11 | continue 12 | filepath = os.path.join(root, filename) 13 | tmp = filepath.split('.')[0].split('/') 14 | print(str(tmp)) 15 | 16 | test = '# Import AWS utils\nfrom %s import *\n\n#\n# Test methods for %s\n#\n\nclass Test%sClass:\n\n' % ('.'.join(tmp), filepath, ''.join(t.title() for t in tmp)) 17 | 18 | test_filename = 'test-%s.py' % '-'.join(tmp[1:]) 19 | print('%s --> %s' % (filepath, test_filename)) 20 | test_file = os.path.join(tests_dir, test_filename) 21 | if not os.path.isfile(test_file): 22 | with open(test_file, 'w+') as f: 23 | f.write(test) 24 | 25 | -------------------------------------------------------------------------------- /tools/sort-ruleset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import json 4 | import os 5 | import re 6 | import sys 7 | 8 | if len(sys.argv) < 2: 9 | print('Error, you must provide the path to the ruleset as an argument.\nUsage:\n$ sort-ruleset.py \n') 10 | sys.exit(42) 11 | 12 | ruleset_name = sys.argv[1] 13 | if not os.path.isfile(ruleset_name): 14 | print('Error, the path provided is not valid.') 15 | sys.exit(42) 16 | 17 | with open(ruleset_name, 'rt') as f: 18 | ruleset = json.load(f) 19 | 20 | ruleset = json.dumps(ruleset, indent = 4, sort_keys = True) 21 | 22 | 23 | with open(ruleset_name, 'wt') as f: 24 | for line in ruleset.split('\n'): 25 | f.write('%s\n' % line.rstrip()) 26 | --------------------------------------------------------------------------------