├── .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 |
--------------------------------------------------------------------------------