Full Code of stelligent/config-lint for AI

master 8e87d18df9df cached
666 files
840.3 KB
283.5k tokens
466 symbols
1 requests
Download .txt
Showing preview only (1,046K chars total). Download the full file or copy to clipboard to get everything.
Repository: stelligent/config-lint
Branch: master
Commit: 8e87d18df9df
Files: 666
Total size: 840.3 KB

Directory structure:
gitextract_py6281yd/

├── .devcontainer/
│   ├── Dockerfile
│   ├── build/
│   │   ├── Dockerfile
│   │   └── dockerhub.sh
│   └── devcontainer.json
├── .dockerhub/
│   └── Dockerfile
├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── build_and_deploy.yml
│       ├── bump_version.yml
│       └── vscode_remote_development.yml
├── .gitignore
├── .goreleaser.yml
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── assertion/
│   ├── compare.go
│   ├── compare_test.go
│   ├── contains.go
│   ├── contains_test.go
│   ├── expression.go
│   ├── expression_test.go
│   ├── has_properties.go
│   ├── helper_test.go
│   ├── invoke.go
│   ├── invoke_test.go
│   ├── ip_operations.go
│   ├── ip_operations_test.go
│   ├── log.go
│   ├── match.go
│   ├── match_test.go
│   ├── rules.go
│   ├── rules_test.go
│   ├── search.go
│   ├── testdata/
│   │   ├── collection-assertions.yaml
│   │   ├── conditions.yaml
│   │   ├── default-severity.yaml
│   │   └── has-properties.yaml
│   ├── types.go
│   ├── util.go
│   ├── util_test.go
│   ├── value.go
│   └── value_test.go
├── cli/
│   ├── app.go
│   ├── app_test.go
│   ├── assets/
│   │   ├── lint-rules.yml
│   │   └── terraform/
│   │       └── aws/
│   │           ├── api_gateway/
│   │           │   └── api_gateway_domain_name/
│   │           │       └── security_policy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── security_policy.tf
│   │           │               └── test.yml
│   │           ├── batch/
│   │           │   └── batch_job_definition/
│   │           │       ├── aws_secrets/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── aws_secrets.tf
│   │           │       │       └── test.yml
│   │           │       └── container_properties_privileged/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── container_properties_privileged.tf
│   │           │               ├── terraform12/
│   │           │               │   └── container_properties_privileged.tf
│   │           │               └── test.yml
│   │           ├── cloudfront/
│   │           │   └── cloudfront_distribution/
│   │           │       ├── custom_origin_config/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── custom_origin_config.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── custom_origin_config.tf
│   │           │       │       └── test.yml
│   │           │       ├── logging_config/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── logging_config.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── logging_config.tf
│   │           │       │       └── test.yml
│   │           │       ├── minimum_ssl_protocol/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── minimum_ssl_protocol.tf
│   │           │       │       └── test.yml
│   │           │       └── viewer_protocol_policy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── viewer_protocol_policy.tf
│   │           │               ├── terraform12/
│   │           │               │   └── viewer_protocol_policy.tf
│   │           │               └── test.yml
│   │           ├── cloudtrail/
│   │           │   └── cloudtrail/
│   │           │       └── kms_key_id/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── kms_key_id.tf
│   │           │               ├── terraform12/
│   │           │               │   └── kms_key_id.tf
│   │           │               └── test.yml
│   │           ├── cloudwatch/
│   │           │   └── cloudwatch_log_destination_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── codebuild/
│   │           │   └── codebuild_project/
│   │           │       ├── artifact_encryption/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── artifact_encryption.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── artifact_encryption.tf
│   │           │       │       └── test.yml
│   │           │       └── project_encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── project_encryption.tf
│   │           │               ├── terraform12/
│   │           │               │   └── project_encryption.tf
│   │           │               └── test.yml
│   │           ├── codepipeline/
│   │           │   └── codepipeline/
│   │           │       └── encryption_key/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── encryption_key.tf
│   │           │               ├── terraform12/
│   │           │               │   └── encryption_key.tf
│   │           │               └── test.yml
│   │           ├── dms/
│   │           │   └── dms_endpoint/
│   │           │       └── endpoint_kms_key/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── kms_key.tf
│   │           │               ├── terraform12/
│   │           │               │   └── kms_key.tf
│   │           │               └── test.yml
│   │           ├── documentdb/
│   │           │   └── docdb_cluster/
│   │           │       ├── audit_logs/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── audit_logs.tf
│   │           │       │       └── test.yml
│   │           │       └── storage_encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── storage_encryption.tf
│   │           │               └── test.yml
│   │           ├── ec2/
│   │           │   ├── ami/
│   │           │   │   └── ebs_block_device_encrypted/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── ebs_block_device_encrypted.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── ebs_block_device_encrypted.tf
│   │           │   │           └── test.yml
│   │           │   ├── ami_copy/
│   │           │   │   └── encrypted/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── encrypted.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── encrypted.tf
│   │           │   │           └── test.yml
│   │           │   ├── ebs_volume/
│   │           │   │   └── encryption/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── encrypted.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── encrypted.tf
│   │           │   │           └── test.yml
│   │           │   └── instance/
│   │           │       └── ebs_block_device_encrypted/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── ebs_block_device_encrypted.tf
│   │           │               └── test.yml
│   │           ├── ecr/
│   │           │   └── ecr_repository_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── ecs/
│   │           │   └── ecs_task_definition/
│   │           │       └── task_definition_secrets/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── secrets.tf
│   │           │               ├── terraform12/
│   │           │               │   └── secrets.tf
│   │           │               └── test.yml
│   │           ├── efs/
│   │           │   └── efs_file_system/
│   │           │       └── encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── encrypted.tf
│   │           │               ├── terraform12/
│   │           │               │   └── encrypted.tf
│   │           │               └── test.yml
│   │           ├── elastic_load_balancing/
│   │           │   ├── alb/
│   │           │   │   └── alb_access_logs_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           └── test.yml
│   │           │   ├── alb_listener/
│   │           │   │   └── alb_listener_https/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── https.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── https.tf
│   │           │   │           └── test.yml
│   │           │   ├── elb/
│   │           │   │   └── access_logs_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           └── test.yml
│   │           │   ├── lb/
│   │           │   │   └── access_logs_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           └── test.yml
│   │           │   └── lb_listener/
│   │           │       ├── listener_https/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── https.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── https.tf
│   │           │       │       └── test.yml
│   │           │       └── listener_ssl_policy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── ssl_policy.tf
│   │           │               ├── terraform12/
│   │           │               │   └── ssl_policy.tf
│   │           │               └── test.yml
│   │           ├── elasticache/
│   │           │   └── elasticache_replication_group/
│   │           │       ├── encryption_at_rest/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── encryption_at_rest.tf
│   │           │       │       └── test.yml
│   │           │       └── encryption_in_transit/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── encryption_in_transit.tf
│   │           │               ├── terraform12/
│   │           │               │   └── encryption_in_transit.tf
│   │           │               └── test.yml
│   │           ├── elasticsearch/
│   │           │   ├── elasticsearch_domain/
│   │           │   │   ├── encryption_at_rest/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── encryption_at_rest.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── encryption_node_to_node/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── encryption_node_to_node.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── vpc_subnets/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── elasticsearch_vpc.tf
│   │           │   │           └── test.yml
│   │           │   └── shared/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   ├── elasticsearch_domain_policy_wildcard_principal.tf
│   │           │               │   └── elasticsearch_domain_wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── elastictranscoder/
│   │           │   └── elastictranscoder_pipeline/
│   │           │       └── require_encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── require_encryption.tf
│   │           │               └── test.yml
│   │           ├── emr/
│   │           │   └── emr_cluster/
│   │           │       └── logging/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── logging.tf
│   │           │               ├── terraform12/
│   │           │               │   └── logging.tf
│   │           │               └── test.yml
│   │           ├── glue/
│   │           │   └── glue_connection/
│   │           │       └── connection_properties/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── connection_properties.tf
│   │           │               └── test.yml
│   │           ├── iam/
│   │           │   ├── iam_group_membership/
│   │           │   │   └── group_and_users/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── group_and_users.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── group_and_users.tf
│   │           │   │           └── test.yml
│   │           │   ├── iam_policy/
│   │           │   │   ├── policy_action_wildcard/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_action_wildcard.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_action_wildcard.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── policy_notaction/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_notaction.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_notaction.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── policy_notresource/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_notresource.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_notresource.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── policy_resource_wildcard/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── policy_resource_wildcard.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── policy_resource_wildcard.tf
│   │           │   │           └── test.yml
│   │           │   ├── iam_role/
│   │           │   │   ├── assume_role_policy_action_wildcard/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── assume_role_policy_action_wildcard.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── assume_role_policy_action_wildcard.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── assume_role_policy_notaction/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── assume_role_policy_notaction.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── assume_role_policy_notaction.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── assume_role_policy_notprincipal/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── assume_role_policy_notprincipal.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── assume_role_policy_notprincipal.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── assume_role_policy_version/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── assume_role_policy_version.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── assume_role_policy_version.tf
│   │           │   │           └── test.yml
│   │           │   ├── iam_role_policy/
│   │           │   │   ├── role_policy_action_wildcard/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_action_wildcard.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_action_wildcard.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── role_policy_notaction/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_notaction.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_notaction.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── role_policy_notresource/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_notresource.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_notresource.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── role_policy_resource_wildcard/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── policy_resource_wildcard.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── policy_resource_wildcard.tf
│   │           │   │           └── test.yml
│   │           │   ├── iam_user_policy/
│   │           │   │   └── exists/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── resource_exists.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── resource_exists.tf
│   │           │   │           └── test.yml
│   │           │   └── iam_user_policy_attachment/
│   │           │       └── exists/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── resource_exists.tf
│   │           │               ├── terraform12/
│   │           │               │   └── resource_exists.tf
│   │           │               └── test.yml
│   │           ├── iot/
│   │           │   └── iot_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── kinesis/
│   │           │   └── kinesis_stream/
│   │           │       ├── kinesis_stream_encryption/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── encryption.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── encryption.tf
│   │           │       │       └── test.yml
│   │           │       └── kinesis_stream_kms_key/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── kms_key.tf
│   │           │               ├── terraform12/
│   │           │               │   └── kms_key.tf
│   │           │               └── test.yml
│   │           ├── kinesis_firehouse/
│   │           │   └── kinesis_firehose_delivery_stream/
│   │           │       └── encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── encryption.tf
│   │           │               ├── terraform12/
│   │           │               │   └── encryption.tf
│   │           │               └── test.yml
│   │           ├── kms/
│   │           │   └── kms_key/
│   │           │       ├── rotation/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── rotation.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── rotation.tf
│   │           │       │       └── test.yml
│   │           │       └── wildcard_policy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_policy.tf
│   │           │               └── test.yml
│   │           ├── lambda/
│   │           │   ├── lambda_function/
│   │           │   │   ├── encryption/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── encryption.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── encryption.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── environment_variables_aws_secrets/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── environment_variables_aws_secrets.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── environment_variables_aws_secrets.tf
│   │           │   │           └── test.yml
│   │           │   └── lambda_permission/
│   │           │       ├── action/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── action.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── action.tf
│   │           │       │       └── test.yml
│   │           │       └── principal_wildcard/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── principal_wildcard.tf
│   │           │               ├── terraform12/
│   │           │               │   └── principal_wildcard.tf
│   │           │               └── test.yml
│   │           ├── mediastore/
│   │           │   └── media_store_container_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── neptune/
│   │           │   └── neptune_cluster/
│   │           │       └── encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── encryption.tf
│   │           │               └── test.yml
│   │           ├── opsworks/
│   │           │   └── opsworks_application/
│   │           │       └── require_ssl/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── require_ssl.tf
│   │           │               └── test.yml
│   │           ├── rds/
│   │           │   ├── db_instance/
│   │           │   │   ├── encryption/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── storage_encryption.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── storage_encryption.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── publicly_accessible/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── publicly_accessible.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── publicly_accessible.tf
│   │           │   │           └── test.yml
│   │           │   └── rds_cluster/
│   │           │       └── encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── storage_encryption.tf
│   │           │               └── test.yml
│   │           ├── redshift/
│   │           │   ├── redshift_cluster/
│   │           │   │   ├── encrypted/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── encrypted.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── enhanced_vpc_routing/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── enhanced_vpc_routing.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── enhanced_vpc_routing.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── kms_key_id/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── kms_key_id.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── kms_key_id.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── logging/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── logging.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── logging.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── publicly_accessible/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── publicly_accessible.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── publicly_accessible.tf
│   │           │   │           └── test.yml
│   │           │   └── redshift_parameter_group/
│   │           │       ├── require_ssl/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── require_ssl.tf
│   │           │       │       └── test.yml
│   │           │       └── user_logging/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── user_logging.tf
│   │           │               └── test.yml
│   │           ├── s3/
│   │           │   ├── s3_bucket/
│   │           │   │   ├── acl_not_public/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── acl_not_public.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── acl_not_public.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── server_side_encryption_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── server_side_encryption_enabled.tf
│   │           │   │           └── test.yml
│   │           │   ├── s3_bucket_object/
│   │           │   │   └── encryption_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── encryption_enabled.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── encryption_enabled.tf
│   │           │   │           └── test.yml
│   │           │   └── s3_bucket_policy/
│   │           │       ├── policy_statement_action_wildcard/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_action_wildcard.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_action_wildcard.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_notaction/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_notprincipal/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_principal_wildcard/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_principal_wildcard.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_principal_wildcard.tf
│   │           │       │       └── test.yml
│   │           │       └── policy_statement_secure_transport/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── policy_statement_secure_transport.tf
│   │           │               ├── terraform12/
│   │           │               │   └── policy_statement_secure_transport.tf
│   │           │               └── test.yml
│   │           ├── sagemaker/
│   │           │   ├── sagemaker_endpoint_configuration/
│   │           │   │   └── kms_key/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── kms_key.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── kms_key.tf
│   │           │   │           └── test.yml
│   │           │   └── sagemaker_notebook_instance/
│   │           │       └── kms_key/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── kms_key.tf
│   │           │               ├── terraform12/
│   │           │               │   └── kms_key.tf
│   │           │               └── test.yml
│   │           ├── ses/
│   │           │   └── ses_identity_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── shared/
│   │           │   ├── exists.todo.txt
│   │           │   ├── https.todo.txt
│   │           │   ├── kms_key.todo.txt
│   │           │   ├── policy.todo.txt
│   │           │   ├── policy_version.todo.txt
│   │           │   └── require_ssl.todo.txt
│   │           ├── sns/
│   │           │   ├── shared/
│   │           │   │   └── wildcard_principal/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform12/
│   │           │   │           │   ├── sns_topic_policy_wildcard_principal.tf
│   │           │   │           │   └── sns_topic_wildcard_principal.tf
│   │           │   │           └── test.yml
│   │           │   └── sns_topic_policy/
│   │           │       ├── topic_policy_statement_notaction/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       └── test.yml
│   │           │       ├── topic_policy_statement_notprincipal/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       └── test.yml
│   │           │       └── topic_policy_statement_principal_wildcard-copy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── policy_statement_principal_wildcard-copy.tf
│   │           │               ├── terraform12/
│   │           │               │   └── policy_statement_principal_wildcard-copy.tf
│   │           │               └── test.yml
│   │           ├── sqs/
│   │           │   ├── shared/
│   │           │   │   └── wildcard_principal/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform12/
│   │           │   │           │   ├── sqs_queue_policy_wildcard_principal.tf
│   │           │   │           │   └── sqs_queue_wildcard_principal.tf
│   │           │   │           └── test.yml
│   │           │   ├── sqs_queue/
│   │           │   │   └── encryption/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── encryption.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── encryption.tf
│   │           │   │           └── test.yml
│   │           │   └── sqs_queue_policy/
│   │           │       ├── policy_statement_action_wildcard/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_action_wildcard.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_action_wildcard.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_notaction/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_notprincipal/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_principal_wildcard/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_principal_wildcard.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_principal_wildcard.tf
│   │           │       │       └── test.yml
│   │           │       └── policy_version/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── policy_version.tf
│   │           │               ├── terraform12/
│   │           │               │   └── policy_version.tf
│   │           │               └── test.yml
│   │           ├── vpc/
│   │           │   ├── security_group/
│   │           │   │   ├── egress_all_protocols/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── egress_all_protocols.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── egress_all_protocols.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── egress_port_range/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── egress_port_range.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── egress_port_range.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── ingress_all_protocols/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── ingress_all_protocols.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── ingress_all_protocols.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── ingress_port_range/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── ingress_port_range.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── ingress_port_range.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── missing_egress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── missing_egress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── missing_egress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── non_32_ingress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── non_32_ingress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── non_32_ingress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── rdp_world_ingress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── rdp_world_ingress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── rdp_world_ingress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── ssh_world_ingress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── ssh_world_ingress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── ssh_world_ingress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── world_egress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── world_egress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── world_egress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── world_ingress/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── world_ingress.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── world_ingress.tf
│   │           │   │           └── test.yml
│   │           │   └── subnet/
│   │           │       └── map_public_ip_on_launch/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── map_public_ip_on_launch.tf
│   │           │               ├── terraform12/
│   │           │               │   └── map_public_ip_on_launch.tf
│   │           │               └── test.yml
│   │           └── waf/
│   │               └── waf_web_acl/
│   │                   └── default_action_type/
│   │                       ├── rule.yml
│   │                       └── tests/
│   │                           ├── terraform11/
│   │                           │   └── default_action_type.tf
│   │                           ├── terraform12/
│   │                           │   └── default_action_type.tf
│   │                           └── test.yml
│   ├── builtin_test.go
│   ├── options.go
│   ├── options_test.go
│   ├── report_writer.go
│   ├── report_writer_test.go
│   ├── terraform_test.go
│   └── testdata/
│       ├── builtin/
│       │   └── terraform12/
│       │       └── test.tf
│       ├── dirtest/
│       │   ├── a.yml
│       │   └── b.yml
│       ├── exclude-list
│       ├── profile-exceptions.yml
│       ├── profile.yml
│       ├── smoketest_exceptions.tf
│       ├── smoketest_tf11.tf
│       ├── smoketest_tf12.tf
│       ├── syntax-errors.yml
│       └── terraform.yml
├── docs/
│   ├── README.md
│   ├── conditions.md
│   ├── coverpage.md
│   ├── css/
│   │   └── style.css
│   ├── design.md
│   ├── development.md
│   ├── example-rules.md
│   ├── faq.md
│   ├── github_workflow.md
│   ├── index.html
│   ├── install.md
│   ├── operations.md
│   ├── output.md
│   ├── profiles.md
│   ├── rule_development.md
│   ├── rules.md
│   ├── running.md
│   ├── sidebar.md
│   ├── terraform.md
│   ├── tests.md
│   ├── value_from.md
│   └── yaml.md
├── example-files/
│   ├── config/
│   │   ├── cloudfront.tf
│   │   ├── elb.tf
│   │   ├── generic.config
│   │   ├── iam.tf
│   │   ├── my-pod.yml
│   │   ├── network-policy-1.yml
│   │   ├── network-policy-2.yml
│   │   ├── no-containers.yml
│   │   ├── not-a-pod.yml
│   │   ├── pod-nginx.yml
│   │   ├── pod-redis.yml
│   │   ├── policy.yml
│   │   ├── provider.tf
│   │   ├── s3-bucket-policy.tf
│   │   ├── s3-encryption.tf
│   │   ├── s3.tf
│   │   ├── search-debug.tf
│   │   ├── security_group.tf
│   │   ├── service-account.yml
│   │   ├── sns.tf
│   │   ├── sqs.tf
│   │   ├── terraform.tf
│   │   ├── variables.tf
│   │   ├── volumes.tf
│   │   └── web-and-helper.yml
│   ├── demo-resources/
│   │   └── s3-bucket.tf
│   └── rules/
│       ├── alias.yml
│       ├── generic-json.yml
│       ├── generic-yaml.yml
│       ├── iam-policies.yml
│       ├── iam-restricted.yml
│       ├── kubernetes.yml
│       ├── lint-rules-with-error.yml
│       ├── no-iam-actions.yml
│       ├── s3-encryption.yml
│       ├── terraform-more.yml
│       ├── terraform.yml
│       └── variables.tf
├── go.mod
├── go.sum
└── linter/
    ├── common.go
    ├── common_test.go
    ├── csv_resource_loader.go
    ├── csv_resource_loader_test.go
    ├── file_linter.go
    ├── file_linter_test.go
    ├── helpers_test.go
    ├── json_resource_loader.go
    ├── json_resource_loader_test.go
    ├── kubernetes.go
    ├── kubernetes_test.go
    ├── linter.go
    ├── linter_test.go
    ├── resource_linter.go
    ├── resource_linter_test.go
    ├── rules_resource_loader.go
    ├── rules_resource_loader_test.go
    ├── schema.go
    ├── terraform.go
    ├── terraform_interpolate.go
    ├── terraform_interpolate_test.go
    ├── terraform_test.go
    ├── terraform_v12.go
    ├── terraform_v12_test.go
    ├── testdata/
    │   ├── data/
    │   │   ├── bucket_name
    │   │   ├── multi_line_content
    │   │   ├── reference_relative.tf
    │   │   ├── template_file_example_basic
    │   │   ├── template_file_example_conditional
    │   │   └── template_file_example_for_loop
    │   ├── resources/
    │   │   ├── batch_privileged.tf
    │   │   ├── cloudfront_access_logs.tf
    │   │   ├── defines_variables.tf
    │   │   ├── dms_endpoint_encryption.tf
    │   │   ├── dynamic_block.tf
    │   │   ├── ec2_public.tf
    │   │   ├── elasticache_encryption_rest.tf
    │   │   ├── elasticache_encryption_transit.tf
    │   │   ├── embedded_yaml.yml
    │   │   ├── empty_document.yml
    │   │   ├── emr_cluster_logs.tf
    │   │   ├── explicit_chars.tf
    │   │   ├── generic.config
    │   │   ├── invalid.yml
    │   │   ├── kinesis_kms_stream.tf
    │   │   ├── kms_key_rotation.tf
    │   │   ├── missing_kind.yml
    │   │   ├── multi_level.tf
    │   │   ├── multiple_blocks_same.tf
    │   │   ├── multiple_pods.yml
    │   │   ├── neptune_db_encryption.tf
    │   │   ├── nullable_value.tf
    │   │   ├── pod.yml
    │   │   ├── policy_with_expression.tf
    │   │   ├── policy_with_variables.tf
    │   │   ├── rds_publicly_available.tf
    │   │   ├── reference_file.tf
    │   │   ├── reference_file_multi_line.tf
    │   │   ├── reference_variables.tf
    │   │   ├── sagemaker_endpoint_encryption.tf
    │   │   ├── sagemaker_notebook_encryption.tf
    │   │   ├── tagging.tf
    │   │   ├── template_file_function_basic.tf
    │   │   ├── template_file_function_conditional.tf
    │   │   ├── template_file_function_for_loop.tf
    │   │   ├── terraform_data.tf
    │   │   ├── terraform_inner_objects.tf
    │   │   ├── terraform_instance.tf
    │   │   ├── terraform_module.tf
    │   │   ├── terraform_policy.tf
    │   │   ├── terraform_policy_empty.tf
    │   │   ├── terraform_policy_invalid_json.tf
    │   │   ├── terraform_provider.tf
    │   │   ├── terraform_syntax_error.tf
    │   │   ├── tf12_for_loop.tf
    │   │   ├── tf12_resource_dependency.tf
    │   │   ├── users.csv
    │   │   ├── users.json
    │   │   ├── uses_local_variables.tf
    │   │   ├── uses_tf12_variables.tf
    │   │   └── uses_variables.tf
    │   └── rules/
    │       ├── aggregate.yml
    │       ├── bad-format.yml
    │       ├── batch_definition.yml
    │       ├── cloudfront_access_logs.yml
    │       ├── dms_endpoint_encryption.yml
    │       ├── dynamic_block.yml
    │       ├── ec2_public.yml
    │       ├── elasticache_encryption_rest.yml
    │       ├── elasticache_encryption_transit.yml
    │       ├── emr_cluster_logs.yml
    │       ├── exclude_resource.yml
    │       ├── explicit_chars.yml
    │       ├── generic-csv.yml
    │       ├── generic-json.yml
    │       ├── generic-yaml.yml
    │       ├── kinesis_kms_stream.yml
    │       ├── kms_key_rotation.yml
    │       ├── kubernetes.yml
    │       ├── neptune_db_encryption.yml
    │       ├── nullable_value.yml
    │       ├── policy_variable.yml
    │       ├── rds_publicly_available.yml
    │       ├── rules.yml
    │       ├── sagemaker_endpoint_encryption.yml
    │       ├── sagemaker_notebook_encryption.yml
    │       ├── tagging.yml
    │       ├── terraform_bucket.yml
    │       ├── terraform_data.yml
    │       ├── terraform_instance.yml
    │       ├── terraform_module.yml
    │       ├── terraform_policy.yml
    │       ├── terraform_provider.yml
    │       ├── terraform_v12_variables.yml
    │       ├── tf12_for_loop.yml
    │       └── unknown.yml
    ├── tf12parser/
    │   ├── README.md
    │   ├── attribute.go
    │   ├── block.go
    │   ├── parser.go
    │   ├── parser_test.go
    │   ├── range.go
    │   └── schema.go
    ├── yaml_resource_loader.go
    └── yaml_resource_loader_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .devcontainer/Dockerfile
================================================
FROM stelligent/vscode-remote-config-lint:latest


================================================
FILE: .devcontainer/build/Dockerfile
================================================
FROM ubuntu:latest

# Avoid warnings by switching to noninteractive
ENV DEBIAN_FRONTEND=noninteractive

# Install packages
RUN apt-get update \
		# Install apt-utils and suppress package configuration warning
		&& apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \
		# Install build tools
		&& apt-get install -y \
		bc \
		unzip \
		wget \
		git \
		g++ \
		gcc \
		libc6-dev \
		make \
		pkg-config \
		ca-certificates \
		gnupg-agent \
		# Cleanup apt lists
		&& rm -rf /var/lib/apt/lists/*

# Install Go
ENV GOLANG_VERSION 1.13.7
RUN set -eux; \
	goRelArch='linux-amd64'; \
	goRelSha256='b3dd4bd781a0271b33168e627f7f43886b4c5d1c794a4015abf34e99c6526ca3'; \
	url="https://golang.org/dl/go${GOLANG_VERSION}.${goRelArch}.tar.gz"; \
	wget -O go.tgz "$url"; \
	echo "${goRelSha256} *go.tgz" | sha256sum -c -; \
	tar -C /usr/local -xzf go.tgz; \
	rm go.tgz
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
ENV GO111MODULE=on

# Install VS Code Go Dependencies
RUN go get -x -d github.com/stamblerre/gocode \
	&& go build -o gocode-gomod github.com/stamblerre/gocode \
	&& mv gocode-gomod $GOPATH/bin/ \
	&& go get -u -v \
	golang.org/x/tools/gopls \
	github.com/mdempsky/gocode \
	# Workaround for https://github.com/uudashr/gopkgs/issues/25
	github.com/uudashr/gopkgs/v2/cmd/gopkgs \
	github.com/sqs/goreturns \
	golang.org/x/lint/golint \
	github.com/ramya-rao-a/go-outline \
	&& go get github.com/go-delve/delve/cmd/dlv

# Create a non-root user
ARG USERNAME=config-lint-dev
ARG USER_UID=1000
ARG USER_GID=$USER_UID
RUN groupadd --gid $USER_GID $USERNAME \
  && useradd --uid $USER_UID --gid $USER_GID --shell /bin/bash -m $USERNAME

# Prompt gpg window inside container for signing commits & setup folder permissions for non-root user
RUN echo 'export GPG_TTY="$(tty)"' >> /home/$USERNAME/.bashrc \
  && mkdir /home/$USERNAME/.gnupg \
  && chown -R $USERNAME:$USERNAME /home/$USERNAME/.gnupg

# Persist bash history between runs
RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
    && mkdir /commandhistory \
    && touch /commandhistory/.bash_history \
    && chown -R $USERNAME /commandhistory \
    && echo $SNIPPET >> "/home/$USERNAME/.bashrc"

# Add non-root user to $GOPATH
RUN chown -R $USERNAME $GOPATH \
		# Add write permission for /go/pkg
		&& chmod -R a+w /go/pkg

# Install Terraform
ARG TERRAFORM_VERSION=0.12.20
RUN cd /tmp \
	&& wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
	&& unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
	&& mv terraform /usr/local/bin/ \
	&& rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip

# Enter container as non-root user
USER $USERNAME


================================================
FILE: .devcontainer/build/dockerhub.sh
================================================
#!/bin/bash -ex

set +x
if [[ -z ${DOCKER_ORG} ]];
then
  echo DOCKER_ORG must be set in the environment
  exit 1
fi
if [[ -z ${GITHUB_SHA} ]];
then
  echo GITHUB_SHA must be set in the environment
  exit 1
fi
set -x

COMMIT_HASH=${GITHUB_SHA:0:8}

# publish vscode-remote docker image to DockerHub, https://hub.docker.com/r/stelligent/vscode-remote-config-lint
docker build -t $DOCKER_ORG/vscode-remote-config-lint:${COMMIT_HASH} --file .devcontainer/build/Dockerfile .
docker tag $DOCKER_ORG/vscode-remote-config-lint:${COMMIT_HASH} $DOCKER_ORG/vscode-remote-config-lint:latest
docker push $DOCKER_ORG/vscode-remote-config-lint:${COMMIT_HASH}
docker push $DOCKER_ORG/vscode-remote-config-lint:latest

================================================
FILE: .devcontainer/devcontainer.json
================================================
{
	"name": "config-lint Development",
	"dockerFile": "Dockerfile",
	"appPort": 9000,
	"remote.containers.workspaceMountConsistency": "consistent",
	"mounts": [
		// Bash History
		"source=config-lint-bash_history,target=/commandhistory,type=volume"
	],
	"runArgs": [
		// SSH
		"-v", "${localEnv:HOME}/.ssh:/home/config-lint-dev/.ssh:ro",
		// GPG
		"-v", "${localEnv:HOME}/.gnupg/private-keys-v1.d:/home/config-lint-dev/.gnupg/private-keys-v1.d:ro",
		"-v", "${localEnv:HOME}/.gnupg/pubring.kbx:/home/config-lint-dev/.gnupg/pubring.kbx:ro",
		"-v", "${localEnv:HOME}/.gnupg/trustdb.gpg:/home/config-lint-dev/.gnupg/trustdb.gpg:ro"
	],
	"extensions": [
		// General
		"CoenraadS.bracket-pair-colorizer",
		"fabiospampinato.vscode-diff",
		"mrmlnc.vscode-duplicate",
		"ms-azuretools.vscode-docker",
		"wayou.vscode-todo-highlight",
		// Go
		"ms-vscode.go",
		// Terraform
		"mauve.terraform",
		// JSON
		"mohsen1.prettify-json",
		// YAML
		"redhat.vscode-yaml"
	],
	"settings": {
		// Bracket Pair Colorizer
		"bracketPairColorizer.forceUniqueOpeningColor": false,
		"bracketPairColorizer.colorMode": "Consecutive",
		"bracketPairColorizer.highlightActiveScope": true,
		"bracketPairColorizer.activeScopeCSS": [
			"borderStyle : solid",
			"borderWidth : 1px",
			"borderColor : {color}; opacity: 0.5",
			"backgroundColor : {color}"
		],
		"editor.matchBrackets": "never",
		"bracketPairColorizer.showBracketsInGutter": true,
		// Go
		"go.gopath": "/go",
		"go.inferGopath": true,
		"go.useLanguageServer": true,
		"[go]": {
				"editor.insertSpaces": true,
				"editor.tabSize": 4,
				"editor.formatOnSave": true,
				"editor.codeActionsOnSave": {
						"source.organizeImports": true
				}
		},
		"gopls": {
				"usePlaceholders": true
		},
		// Terraform
		"[terraform]": {
			"editor.formatOnSave": true
		},
		"terraform.languageServer": {
			"enabled": false,
			"args": []
		},
		"terraform.indexing": {
			"enabled": false,
			"liveIndexing": false
		},
		// YAML
		"[yaml]": {
			"editor.insertSpaces": true,
			"editor.tabSize": 2
		},
		"yaml.format.enable": true,
		"yaml.format.singleQuote": true,
		"yaml.format.bracketSpacing": true,
		"yaml.format.printWidth": 120,
		"yaml.format.proseWrap": "always",
		// TODO
		"todohighlight.isEnable": true,
		"todohighlight.isCaseSensitive": false
	},
	"postCreateCommand": "make deps"
}


================================================
FILE: .dockerhub/Dockerfile
================================================
FROM scratch
COPY config-lint /
ENTRYPOINT ["/config-lint"] 


================================================
FILE: .github/workflows/build.yml
================================================
name: Build

on:
  push:
    branches-ignore:
      - 'master'
    tags-ignore:
      - '**'
    paths-ignore:
      - 'docs/**'
      - '**.md'
  pull_request:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - 
        name: checkout
        uses: actions/checkout@master
      -
        name: setup go
        uses: actions/setup-go@v1
        with:
          go-version: '1.13'
      -
        name: dependencies
        run: |
          go mod download
      -  
        name: build
        run: |
          export GOPATH=/home/runner/go
          export PATH="$PATH:$GOPATH/bin"
          make build
      -  
        name: test
        run: |
          export GOPATH=/home/runner/go
          export PATH="$PATH:$GOPATH/bin"
          make test
          make smoke-test


================================================
FILE: .github/workflows/build_and_deploy.yml
================================================
name: Build & Deploy

on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      -
        name: checkout
        uses: actions/checkout@master
      -
        name: setup go
        uses: actions/setup-go@v1
        with:
          go-version: '1.13'
      -
        name: dependencies
        run: |
          go mod download
      -
        name: docker login
        env:
          DOCKER_USER: ${{ secrets.docker_user }}
          DOCKER_PASSWORD: ${{ secrets.docker_password }}
        run: |
          echo $DOCKER_PASSWORD | docker login -u $DOCKER_USER --password-stdin
      -
        name: build
        run: |
          export GOPATH=/home/runner/go
          export PATH="$PATH:$GOPATH/bin"
          make build
      -  
        name: test
        run: |
          export GOPATH=/home/runner/go
          export PATH="$PATH:$GOPATH/bin"
          make test
          make smoke-test
      - 
        name: release
        uses: goreleaser/goreleaser-action@v1
        with:
          args: release --skip-validate
        env:
          GITHUB_TOKEN: ${{ secrets.gh_actions_token }}


================================================
FILE: .github/workflows/bump_version.yml
================================================
# Push with a commit message containing `#major` to bump major version
# and update the major version number here.

# MAJOR Version: 1.x

name: Bump Version

on:
  push:
    branches:
      - master
    tags-ignore:
      - '**'
    paths-ignore:
      - 'docs/**'
      - '**.md'
      - '.devcontainer/**'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
      with:
        fetch-depth: '0'
    - name: Bump version and push tag
      uses: anothrNick/github-tag-action@1.19.0
      env:
        GITHUB_TOKEN: ${{ secrets.gh_actions_token }}
        WITH_V: true
        DEFAULT_BUMP: minor


================================================
FILE: .github/workflows/vscode_remote_development.yml
================================================
name: VS Code DockerHub Build & Push

on:
  push:
    branches:
      - 'master'
    paths:
      - '.devcontainer/**'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      -
        name: checkout
        uses: actions/checkout@master
      -
        name: docker login
        env:
          DOCKER_USER: ${{ secrets.docker_user }}
          DOCKER_PASSWORD: ${{ secrets.docker_password }}
        run: |
          echo $DOCKER_PASSWORD | docker login -u $DOCKER_USER --password-stdin
      -
        name: Build & Push to DockerHub
        env:
          DOCKER_ORG: stelligent
        run: bash ./.devcontainer/build/dockerhub.sh


================================================
FILE: .gitignore
================================================
# Local dev
dist/
.bundle
.DS_Store
.vscode/**/*
.release/
.idea/
.DS_Store
.test/
*/coverage.out
*/*packr.go
**/*.log
**/*.pem
**/*.retry
**/*.sw*
**/local.json

# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

# Dependency directories (remove the comment below to include it)
vendor/


================================================
FILE: .goreleaser.yml
================================================
builds:
-
  main: ./cli
  env:
    - CGO_ENABLED=0
  goos:
   - linux
   - darwin
   - windows
  goarch:
    - 386
    - amd64
    - arm
    - arm64
archives:
  - id: archive
    name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'
    replacements:
      darwin: Darwin
      linux: Linux
      windows: Windows
      386: i386
      amd64: x86_64
    format_overrides:
      - goos: windows
        format: zip
checksum:
  name_template: 'checksums.txt'
snapshot:
  name_template: "{{ .Tag }}-next"
changelog:
  sort: asc
  filters:
    exclude:
    - '^docs:'
    - '^test:'
brews:
  -
    github:
      owner: stelligent
      name: homebrew-tap
    commit_author:
      name: goreleaserbot
      email: goreleaser@stelligent.com
    folder: Formula
    homepage:  https://github.com/stelligent/config-lint
    description: Validate configuration files using rules specified in YAML
    test: |
      system "#{bin}/config-lint -version"
dockers:
  - 
    dockerfile: .dockerhub/Dockerfile
    image_templates:
    - "stelligent/config-lint:{{ .Tag }}"
    - "stelligent/config-lint:latest"


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to config-lint

Help wanted! We'd love your contributions to config-lint. Please review the following guidelines before contributing. Also, feel free to propose changes to these guidelines by updating this file and submitting a pull request.

- [I have a question](#questions)
- [I found a bug](#bugs)
- [I have a feature request](#features)
- [I have a contribution to share](#process)

## <a name="questions"></a> Have a Question?

Please don't open a GitHub issue for questions about how to use config-lint, as the goal is to use issues for managing bugs and feature requests.

Until we have a better way to communicate with users, please use the [Stelligent contact page](https://stelligent.com/contact/) if you have any questions.

## <a name="bugs"></a> Found a Bug?

If you've identified a bug in config-lint, please [submit an issue](#issue) to our GitHub repo:
[stelligent/config-lint](https://github.com/stelligent/config-lint/issues/new). Please also feel free to submit a [Pull Request](#pr) with a fix for the bug!

## <a name="features"></a> Have a Feature Request?

All feature requests should start with [submitting an issue](#issue) documenting the user story and acceptance criteria. Again, feel free to submit a [Pull Request](#pr) with a proposed implementation of the feature.

## <a name="process"></a> Ready to Contribute!

### <a name="issue"></a> Create an issue

Before submitting a new issue, please search the issues to make sure there isn't a similar issue doesn't already exist.

Assuming no existing issues exist, please ensure you include the following bits of information when submitting the issue to ensure we can quickly reproduce your issue:

- The version of config-lint.
- The platform (Linux, OS X, Windows).
- The complete rule file(s) used if not using a built-in ruleset.
- The complete code the rule is running against.
- The complete command that was executed.
- Any output from the command.
- Details of the expected results and how they differed from the actual results.

We may have additional questions and will communicate through the GitHub issue, so please respond back to our questions to help reproduce and resolve the issue as quickly as possible.

New issues can be created with in our [GitHub
repo](https://github.com/stelligent/config-lint/issues/new).

### <a name="pr"></a>Pull Requests

Pull requests should target the `master` branch. Ensure you have a successful build for your branch. Please also reference the issue from the description of the pull request using [special keyword
syntax](https://help.github.com/articles/closing-issues-via-commit-messages/) to auto close the issue when the PR is merged. For example, include the phrase `fixes #14` in the PR description to have issue #14 auto close.

### <a name="style"></a> Styleguide

When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. 

Here are a few points to keep in mind:

- Please run `make lint` before committing to ensure code aligns
  with go standards.
- Please run `make cyclo` before committing to ensure cyclomatic complexity is lower than 15.
- Pleae ensure any new code is well tested and running `make test` is successful.
- Dependencies are managed with [go modules](https://blog.golang.org/using-go-modules) and dependency requirements are defined in [go.mod](go.mod)

### License

By contributing your code, you agree to license your contribution under the
terms of the [MIT License](LICENSE).

All files are released with the MIT license.

================================================
FILE: LICENSE.md
================================================
MIT License

Copyright (c) 2018-2020 Stelligent
Portions copyright 2019 Liam Galvin (https://github.com/liamg/tfsec)


Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
# Versioning based on latest git tag.
VERSION := $(shell git tag -l --sort=creatordate | grep "^v[0-9]*.[0-9]*.[0-9]*$$" | tail -1)
BUILD_DIR = .release
GOLDFLAGS = "-X main.version=$(VERSION)"

CLI_FILES = $(shell find cli linter assertion -name \*.go)

default: all

devdeps:
	@echo "=== dev dependencies ==="
	@go get "github.com/gobuffalo/packr/..."
	@go get -u golang.org/x/lint/golint
	@go get "github.com/fzipp/gocyclo"

deps:
	@echo "=== dependencies ==="
	go mod download

gen:
	@echo "=== generating ==="
	@go get "github.com/gobuffalo/packr/..."
	@go generate ./...

lint: gen
	@echo "=== linting ==="
	@go vet ./...
	@go get -u golang.org/x/lint/golint
	@golint $(go list ./... | grep -v /vendor/)

cyclo:
	@echo "=== cyclomatic complexity ==="
	@go get "github.com/fzipp/gocyclo"
	@gocyclo -over 15 assertion linter cli || echo "WARNING: cyclomatic complexity is high"

test: lint cyclo
	@echo "=== testing ==="
	@go test -v ./...

testtf: lint cyclo
	@echo "=== testing Terraform Built In Rules ==="
	@go test -v ./cli/... -run TestTerraformBuiltInRules

$(BUILD_DIR)/config-lint: $(CLI_FILES)
	@echo "=== building config-lint - $@ ==="
	mkdir -p $(BUILD_DIR)
	GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags=$(GOLDFLAGS) -o $(BUILD_DIR)/config-lint cli/*.go

build: gen $(BUILD_DIR)/config-lint

all: clean deps test build smoke-test
dev: deps devdeps

clean:
	@echo "=== cleaning ==="
	rm -rf $(BUILD_DIR)
	rm -rf vendor
	rm -f cli/*-packr.go

cover-assertion:
	@cd assertion && go test -coverprofile=coverage.out && go tool cover -html=coverage.out

cover-linter:
	@cd linter && go test -coverprofile=coverage.out && go tool cover -html=coverage.out

cover-cli:
	@cd cli && go test -coverprofile=coverage.out && go tool cover -html=coverage.out

smoke-test:
	@$(BUILD_DIR)/config-lint -terraform cli/testdata/smoketest_tf12.tf
	@$(BUILD_DIR)/config-lint -tfparser tf11 -terraform cli/testdata/smoketest_tf11.tf
	@$(BUILD_DIR)/config-lint -tfparser tf11 -terraform -profile cli/testdata/profile-exceptions.yml cli/testdata/smoketest_exceptions.tf


================================================
FILE: README.md
================================================
[![Latest Release](https://img.shields.io/github/v/release/stelligent/config-lint?color=%233D9970)](https://img.shields.io/github/v/release/stelligent/config-lint?color=%233D9970)
[![Build & Deploy](https://github.com/stelligent/config-lint/workflows/Build%20%26%20Deploy/badge.svg)](https://github.com/stelligent/config-lint/workflows/Build%20%26%20Deploy/badge.svg)
[![Go Report Card](https://goreportcard.com/badge/github.com/stelligent/config-lint)](https://goreportcard.com/report/github.com/stelligent/config-lint)

# 🔍 config-lint 🔎

A command line tool to validate configuration files using rules specified in YAML. The configuration files can be one of several formats: Terraform, JSON, YAML, with support for Kubernetes. There are built-in rules provided for Terraform, and custom files can be used for other formats.

📓 [Documentation](https://stelligent.github.io/config-lint)

👷 [Contributing](CONTRIBUTING.md)

🐛 [Issues & Bugs](https://github.com/stelligent/config-lint/issues)

## Blog Posts

✏️ [config-lint: Up and Running](https://stelligent.com/2020/04/17/config-lint-up-and-running/)


✏️ [Development Acceleration Through VS Code Remote Containers](https://stelligent.com/2020/04/10/development-acceleration-through-vs-code-remote-containers-setting-up-a-foundational-configuration/)

## Quick Start

Install the latest version of config-lint on macOS using [Homebrew](https://brew.sh/):

``` bash
brew tap stelligent/tap
brew install config-lint
```

Or manually on Linux:

``` bash
curl -L https://github.com/stelligent/config-lint/releases/latest/download/config-lint_Linux_x86_64.tar.gz | tar xz -C /usr/local/bin config-lint
chmod +rx /usr/local/bin/config-lint
```

Run the built-in ruleset against your Terraform files. For instance if you want to run config-lint against our [example files](example-files/):

``` bash
config-lint -terraform example-files/config
```

You will see failure and warning violations in the output like this:
``` bash
[
  {
    "AssertionMessage": "viewer_certificate[].cloudfront_default_certificate | [0] should be 'false', not ''",
    "Category": "resource",
    "CreatedAt": "2020-04-15T19:24:33Z",
    "Filename": "example-files/config/cloudfront.tf",
    "LineNumber": 10,
    "ResourceID": "s3_distribution",
    "ResourceType": "aws_cloudfront_distribution",
    "RuleID": "CLOUDFRONT_MINIMUM_SSL",
    "RuleMessage": "CloudFront Distribution must use TLS 1.2",
    "Status": "FAILURE"
  },
  ...
```

You can find more install options in our [installation guide](/docs/install.md).

================================================
FILE: assertion/compare.go
================================================
package assertion

import (
	"strconv"
	"time"
)

func intCompare(n1 int, n2 int) int {
	if n1 < n2 {
		return -1
	}
	if n1 > n2 {
		return 1
	}
	return 0
}

func daysOld(data interface{}) int {
	if stringValue, ok := data.(string); ok {
		layout := "2006-01-02T15:04:05Z"
		t, err := time.Parse(layout, stringValue)
		if err != nil {
			return 0
		}
		days := int(time.Since(t).Hours() / 24.0)
		Debugf("Date: %v Days ago: %d\n", data, days)
		return days
	}
	return 0
}

func compare(data interface{}, value string, valueType string) int {
	switch valueType {
	case "size":
		n, _ := strconv.Atoi(value)
		l := 0
		switch v := data.(type) {
		case []interface{}:
			l = len(v)
		case map[string]interface{}:
			l = len(v)
		}
		return intCompare(l, n)
	case "integer":
		switch v := data.(type) {
		case float64:
			n1 := int(v)
			n2, _ := strconv.Atoi(value)
			return intCompare(n1, n2)
		case int:
			n2, _ := strconv.Atoi(value)
			return intCompare(v, n2)
		case string:
			n1, _ := strconv.Atoi(v)
			n2, _ := strconv.Atoi(value)
			return intCompare(n1, n2)
		}
		return 0
	case "age":
		n, _ := strconv.Atoi(value)
		return intCompare(daysOld(data), n)
	default:
		tmp, _ := JSONStringify(data)
		s := unquoted(tmp)
		if s > value {
			return 1
		}
		if s < value {
			return -1
		}
		return 0
	}
}


================================================
FILE: assertion/compare_test.go
================================================
package assertion

import (
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func TestDaysOldForToday(t *testing.T) {
	now := time.Now().Format("2006-01-02T15:04:05Z")
	assert.Equal(t, 0, daysOld(now), "Expecting daysOld to return 0")
}

func TestDaysOldFor90DaysAgo(t *testing.T) {
	then := time.Now().Add(-time.Duration(90) * time.Hour * 24).Format("2006-01-02T15:04:05Z")
	assert.Equal(t, 90, daysOld(then), "Expecting daysOld to return 90")
}


================================================
FILE: assertion/contains.go
================================================
package assertion

import (
	"strings"
)

func interfaceListContains(v []interface{}, key, value string) (MatchResult, error) {
	for _, element := range v {
		if stringElement, isString := element.(string); isString {
			if stringElement == value {
				return matches()
			}
			if strings.Contains(stringElement, value) {
				return matches()
			}
		}
	}
	return doesNotMatch("%v does not contain %v", key, value)
}

func stringListContains(v []string, key, value string) (MatchResult, error) {
	for _, stringElement := range v {
		if stringElement == value {
			return matches()
		}
		if strings.Contains(stringElement, value) {
			return matches()
		}
	}
	return doesNotMatch("%v does not contain %v", key, value)
}

func stringContains(v string, key, value string) (MatchResult, error) {
	if strings.Contains(v, value) {
		return matches()
	}
	return doesNotMatch("%v does not contain %v", key, value)
}

func defaultContains(data interface{}, key, value string) (MatchResult, error) {
	searchResult, err := JSONStringify(data)
	if err != nil {
		return matchError(err)
	}
	if strings.Contains(searchResult, value) {
		return matches()
	}
	return doesNotMatch("%v does not contain %v", key, value)
}

func contains(data interface{}, key, value string) (MatchResult, error) {
	switch v := data.(type) {
	case []interface{}:
		return interfaceListContains(v, key, value)
	case []string:
		return stringListContains(v, key, value)
	case string:
		return stringContains(v, key, value)
	default:
		return defaultContains(v, key, value)
	}
}

func doesNotContain(data interface{}, key, value string) (MatchResult, error) {
	m, err := contains(data, key, value)
	if err != nil {
		return matchError(err)
	}
	if m.Match {
		return doesNotMatch("%v should not contain %v", key, value)
	}
	return matches()
}

func startsWith(data interface{}, key, prefix string) (MatchResult, error) {
	switch v := data.(type) {
	case string:
		if strings.HasPrefix(v, prefix) {
			return matches()
		}
		return doesNotMatch("%v does not start with %v", key, prefix)
	default:
		return doesNotMatch("%v is not a string %v", key, prefix)
	}
}

func endsWith(data interface{}, key, suffix string) (MatchResult, error) {
	switch v := data.(type) {
	case string:
		if strings.HasSuffix(v, suffix) {
			return matches()
		}
		return doesNotMatch("%v does not end with %v", key, suffix)
	default:
		return doesNotMatch("%v is not a string %v", key, suffix)
	}
}


================================================
FILE: assertion/contains_test.go
================================================
package assertion

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

// The non error cases are covered in match_test

func TestContainsWithNonJSONType(t *testing.T) {
	var complexNumber complex128
	_, err := contains(complexNumber, "foo", "1")
	if err == nil {
		t.Errorf("Expecting contains to return an error for non JSON encodable data")
	}
}

func TestDoesNotContainWithNonJSONType(t *testing.T) {
	var complexNumber complex128
	_, err := doesNotContain(complexNumber, "foo", "1")
	if err == nil {
		t.Errorf("Expecting doesNotContain to return an error for non JSON encodable data")
	}
}

func TestContainsWithString(t *testing.T) {
	s := "s3:Get*"
	match, err := contains(s, "Action", "*")
	assert.Nil(t, err, "Expecting no error from contains")
	assert.True(t, match.Match, "Expecting match for string")
}

func TestContainsWithSliceOfStrings(t *testing.T) {
	s := []string{"s3:Get*"}
	match, err := contains(s, "Action", "*")
	assert.Nil(t, err, "Expecting no error from contains")
	assert.True(t, match.Match, "Expecting match for string")
}


================================================
FILE: assertion/expression.go
================================================
package assertion

func searchAndMatch(expression Expression, resource Resource) (MatchResult, error) {
	v, err := SearchData(expression.Key, resource.Properties)
	if err != nil {
		return matchError(err)
	}
	match, err := isMatch(v, expression)
	Debugf("ResourceID: %s Type: %s %v\n",
		resource.ID,
		resource.Type,
		match)
	return match, err
}

func orExpression(expressions []Expression, resource Resource) (MatchResult, error) {
	for _, childExpression := range expressions {
		match, err := booleanExpression(childExpression, resource)
		if err != nil {
			return matchError(err)
		}
		if match.Match {
			return matches()
		}
	}
	return doesNotMatch("Or expression fails") // TODO needs more information
}

func xorExpression(expressions []Expression, resource Resource) (MatchResult, error) {
	matchCount := 0
	for _, childExpression := range expressions {
		match, err := booleanExpression(childExpression, resource)
		if err != nil {
			return matchError(err)
		}
		if match.Match {
			matchCount++
		}
	}
	if matchCount == 1 {
		return matches()
	}
	return doesNotMatch("Xor expression fails") // TODO needs more information
}

func andExpression(expressions []Expression, resource Resource) (MatchResult, error) {
	for _, childExpression := range expressions {
		match, err := booleanExpression(childExpression, resource)
		if err != nil {
			return matchError(err)
		}
		if !match.Match {
			return doesNotMatch("And expression fails: %s", match.Message)
		}
	}
	return matches()
}

func notExpression(expressions []Expression, resource Resource) (MatchResult, error) {
	// more than one child filter treated as not any
	for _, childExpression := range expressions {
		match, err := booleanExpression(childExpression, resource)
		if err != nil {
			return matchError(err)
		}
		if match.Match {
			return doesNotMatch("Not expression fails") // TODO needs more information
		}
	}
	return matches()
}

func collectResources(key string, resource Resource) ([]Resource, error) {
	resources := make([]Resource, 0)
	value, err := SearchData(key, resource.Properties)
	if err != nil {
		return resources, err
	}
	if collection, ok := value.([]interface{}); ok {
		for _, properties := range collection {
			collectionResource := Resource{
				ID:         resource.ID,
				Type:       resource.Type,
				Properties: properties,
				Filename:   resource.Filename,
			}
			resources = append(resources, collectionResource)
		}
	}
	return resources, nil
}

func everyExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {
	resources, err := collectResources(collectionExpression.Key, resource)
	if err != nil {
		return matchError(err)
	}
	for _, collectionResource := range resources {
		match, err := andExpression(collectionExpression.Expressions, collectionResource)
		if err != nil {
			return matchError(err)
		}
		if !match.Match {
			// at least one element is false, so entire expression is false
			return doesNotMatch("Every expression fails: %s", match.Message)
		}
	}
	// every element passes, so entire expression is true
	return matches()
}

func someExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {
	resources, err := collectResources(collectionExpression.Key, resource)
	if err != nil {
		return matchError(err)
	}
	for _, collectionResource := range resources {
		match, err := andExpression(collectionExpression.Expressions, collectionResource)
		if err != nil {
			return matchError(err)
		}
		// at least one element passes, so entire expression is true
		if match.Match {
			return matches()
		}
	}
	// no element passes, so entire expression is false
	return doesNotMatch("Some expression fails") // TODO needs more information
}

func noneExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {
	resources, err := collectResources(collectionExpression.Key, resource)
	if err != nil {
		return matchError(err)
	}
	for _, collectionResource := range resources {
		match, err := andExpression(collectionExpression.Expressions, collectionResource)
		if err != nil {
			return matchError(err)
		}
		// at least one element passes, so entire expression is false
		if match.Match {
			return doesNotMatch("None expression fails: %s", match.Message)
		}
	}
	// no element passes, so entire expression is true
	return matches()
}

func exactlyOneExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {
	resources, err := collectResources(collectionExpression.Key, resource)
	if err != nil {
		return matchError(err)
	}
	matchCount := 0
	for _, collectionResource := range resources {
		match, err := andExpression(collectionExpression.Expressions, collectionResource)
		if err != nil {
			return matchError(err)
		}
		if match.Match {
			matchCount++
		}
	}
	if matchCount == 1 {
		return matches()
	}
	return doesNotMatch("ExactlyOne expression fails")
}

func booleanExpression(expression Expression, resource Resource) (MatchResult, error) {
	if expression.Or != nil && len(expression.Or) > 0 {
		return orExpression(expression.Or, resource)
	}
	if expression.Xor != nil && len(expression.Xor) > 0 {
		return xorExpression(expression.Xor, resource)
	}
	if expression.And != nil && len(expression.And) > 0 {
		return andExpression(expression.And, resource)
	}
	if expression.Not != nil && len(expression.Not) > 0 {
		return notExpression(expression.Not, resource)
	}
	if expression.Every.Key != "" {
		return everyExpression(expression.Every, resource)
	}
	if expression.Some.Key != "" {
		return someExpression(expression.Some, resource)
	}
	if expression.None.Key != "" {
		return noneExpression(expression.None, resource)
	}
	if expression.ExactlyOne.Key != "" {
		return exactlyOneExpression(expression.ExactlyOne, resource)
	}
	return searchAndMatch(expression, resource)
}

// ExcludeResource when resource.ID included in list of exceptions
func ExcludeResource(rule Rule, resource Resource) bool {
	for _, id := range rule.Except {
		if id == resource.ID {
			return true
		}
	}
	return false
}

// FilterResourceExceptions filters out resources that should not be validated
func FilterResourceExceptions(rule Rule, resources []Resource) []Resource {
	if rule.Except == nil || len(rule.Except) == 0 {
		return resources
	}
	filtered := make([]Resource, 0)
	for _, resource := range resources {
		if ExcludeResource(rule, resource) {
			filtered = append(filtered, resource)
		}
	}
	return filtered
}

// CheckExpression validates a single Resource using a single Expression
func CheckExpression(rule Rule, expression Expression, resource Resource) (Result, error) {
	result := Result{
		Status:  "OK",
		Message: "",
	}
	match, err := booleanExpression(expression, resource)
	if err != nil {
		DebugJSON("Error: ", err)
		result.Status = "FAILURE"
		result.Message = err.Error()
		return result, err
	}
	if !match.Match {
		if rule.Severity == "" {
			result.Status = "FAILURE"
		} else {
			result.Status = rule.Severity
		}
		result.Message = match.Message
	}
	return result, nil
}


================================================
FILE: assertion/expression_test.go
================================================
package assertion

import (
	"encoding/json"
	"testing"
)

type ExpressionTestCase struct {
	Rule           Rule
	Resource       Resource
	ExpectedStatus string
}

func TestCheckExpression(t *testing.T) {

	simpleTestResource := Resource{
		ID:   "a_test_resource",
		Type: "aws_instance",
		Properties: map[string]interface{}{
			"instance_type": "t2.micro",
			"ami":           "ami-f2d3638a",
		},
		Filename: "test.tf",
	}
	resourceWithTags := Resource{
		ID:   "another_test_resource",
		Type: "aws_instance",
		Properties: map[string]interface{}{
			"instance_type": "t2.micro",
			"ami":           "ami-f2d3638a",
			"tags": map[string]interface{}{
				"Environment": "Development",
				"Project":     "Web",
			},
		},
		Filename: "test.tf",
	}
	resourceWithRootVolume := Resource{
		ID:   "another_test_resource",
		Type: "aws_instance",
		Properties: map[string]interface{}{
			"instance_type": "t2.micro",
			"ami":           "ami-f2d3638a",
			"root_block_device": map[string]interface{}{
				"volume_size": "1000",
			},
		},
		Filename: "test.tf",
	}

	testCases := map[string]ExpressionTestCase{
		"testEq": {
			Rule{
				ID:       "test1",
				Message:  "test rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Key:   "instance_type",
						Op:    "eq",
						Value: "t2.micro",
					},
				},
			},
			simpleTestResource,
			"OK",
		},
		"testOr": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Or: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "t2.micro",
							},
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "m4.large",
							},
						},
					},
				},
			},
			simpleTestResource,
			"OK",
		},
		"testOrFails": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Or: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "t2.nano",
							},
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "m4.large",
							},
						},
					},
				},
			},
			simpleTestResource,
			"FAILURE",
		},
		"testXor": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Xor: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "t2.micro",
							},
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "m4.large",
							},
						},
					},
				},
			},
			simpleTestResource,
			"OK",
		},
		"testXorFails": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Xor: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "t2.micro",
							},
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "t2.micro",
							},
						},
					},
				},
			},
			simpleTestResource,
			"FAILURE",
		},
		"testXorFailsAgain": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Xor: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "m3.large",
							},
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "c4.large",
							},
						},
					},
				},
			},
			simpleTestResource,
			"FAILURE",
		},
		"testAnd": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						And: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "t2.micro",
							},
							Expression{
								Key:   "ami",
								Op:    "eq",
								Value: "ami-f2d3638a",
							},
						},
					},
				},
			},
			simpleTestResource,
			"OK",
		},
		"testAndFails": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						And: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "m3.medium",
							},
							Expression{
								Key:   "ami",
								Op:    "eq",
								Value: "ami-f2d3638a",
							},
						},
					},
				},
			},
			simpleTestResource,
			"FAILURE",
		},
		"testNot": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Not: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "c4.large",
							},
						},
					},
				},
			},
			simpleTestResource,
			"OK",
		},
		"testNotFails": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Not: []Expression{
							Expression{
								Key:   "instance_type",
								Op:    "eq",
								Value: "t2.micro",
							},
						},
					},
				},
			},
			simpleTestResource,
			"FAILURE",
		},
		"testNestedNot": {
			Rule{
				ID:       "TEST1",
				Message:  "Test Rule",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Not: []Expression{
							Expression{
								Or: []Expression{
									Expression{
										Key:   "instance_type",
										Op:    "eq",
										Value: "t2.micro",
									},
									Expression{
										Key:   "instance_type",
										Op:    "eq",
										Value: "m3.medium",
									},
								},
							},
						},
					},
				},
			},
			simpleTestResource,
			"FAILURE",
		},
		"testSizeFails": {
			Rule{
				ID:       "TESTCOUNT",
				Message:  "Test Resource Count Fails",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Key:       "tags",
						ValueType: "size",
						Op:        "eq",
						Value:     "3",
					},
				},
			},
			resourceWithTags,
			"FAILURE",
		},
		"testSizeOK": {
			Rule{
				ID:       "TESTCOUNT",
				Message:  "Test Resource Count OK",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Key:       "tags",
						ValueType: "size",
						Op:        "eq",
						Value:     "2",
					},
				},
			},
			resourceWithTags,
			"OK",
		},
		"testIntegerFails": {
			Rule{
				ID:       "TESTCOUNT",
				Message:  "Test integer Fails",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Key:       "root_block_device.volume_size",
						ValueType: "integer",
						Op:        "le",
						Value:     "500",
					},
				},
			},
			resourceWithRootVolume,
			"FAILURE",
		},
		"testIntegerOK": {
			Rule{
				ID:       "TESTCOUNT",
				Message:  "Test integer OK",
				Severity: "FAILURE",
				Resource: "aws_instance",
				Assertions: []Expression{
					Expression{
						Key:       "root_block_device.volume_size",
						ValueType: "integer",
						Op:        "le",
						Value:     "2000",
					},
				},
			},
			resourceWithRootVolume,
			"OK",
		},
	}

	for k, tc := range testCases {
		expressionResult, err := CheckExpression(tc.Rule, tc.Rule.Assertions[0], tc.Resource)
		FailTestIfError(err, "TestSimple", t)
		if expressionResult.Status != tc.ExpectedStatus {
			t.Errorf("%s Failed Expected '%s' to be '%s'", k, expressionResult.Status, tc.ExpectedStatus)
		}
	}
}

func TestNestedBooleans(t *testing.T) {
	rule := Rule{
		ID:       "TEST1",
		Message:  "Do not allow access to port 22 from 0.0.0.0/0",
		Severity: "NOT_COMPLIANT",
		Resource: "aws_instance",
		Assertions: []Expression{
			Expression{
				Not: []Expression{
					Expression{
						And: []Expression{
							Expression{
								Key:   "ipPermissions[].fromPort[]",
								Op:    "contains",
								Value: "22",
							},
							Expression{
								Key:   "ipPermissions[].ipRanges[]",
								Op:    "contains",
								Value: "0.0.0.0/0",
							},
						},
					},
				},
			},
		},
	}
	resource := Resource{
		ID:         "a_test_resource",
		Type:       "aws_instance",
		Properties: map[string]interface{}{},
		Filename:   "test.tf",
	}
	resourceJSON := `{
            "description": "2017-12-03T03:14:29.856Z",
            "groupName": "test-8246",
            "ipPermissions": [
                {
                    "fromPort": "22",
                    "ipProtocol": "tcp",
                    "toPort": "22",
                    "ipv4Ranges": [
                        {
                            "cidrIp": "0.0.0.0/0"
                        }
                    ],
                    "ipRanges": [
                        "0.0.0.0/0"
                    ]
                }
            ]
        }`
	err := json.Unmarshal([]byte(resourceJSON), &resource.Properties)
	if err != nil {
		t.Error("Error parsing resource JSON")
	}
	expressionResult, err := CheckExpression(rule, rule.Assertions[0], resource)
	FailTestIfError(err, "TestNestedBoolean", t)
	if expressionResult.Status != "NOT_COMPLIANT" {
		t.Error("Expecting nested boolean to return NOT_COMPLIANT")
	}
}

func TestExceptions(t *testing.T) {
	rule := Rule{
		ID:     "EXCEPT",
		Except: []string{"200", "300"},
	}
	resources := []Resource{
		Resource{ID: "100"},
		Resource{ID: "200"},
		Resource{ID: "300"},
		Resource{ID: "400"},
	}
	filteredResources := FilterResourceExceptions(rule, resources)
	if len(filteredResources) != 2 {
		t.Error("Expecting exceptions to be removed from resource list")
	}
}

func TestNoExceptions(t *testing.T) {
	rule := Rule{
		ID:     "EXCEPT",
		Except: []string{},
	}
	resources := []Resource{
		Resource{ID: "100"},
		Resource{ID: "200"},
		Resource{ID: "300"},
		Resource{ID: "400"},
	}
	filteredResources := FilterResourceExceptions(rule, resources)
	if len(filteredResources) != 4 {
		t.Error("Expecting no exceptions to return all resources")
	}
}

func TestUsingFixtures(t *testing.T) {
	fixtureFilenames := []string{
		"./testdata/collection-assertions.yaml",
		"./testdata/has-properties.yaml",
		"./testdata/conditions.yaml",
		"./testdata/default-severity.yaml",
	}

	for _, filename := range fixtureFilenames {
		RunTestCasesFromFixture(filename, t)
	}
}


================================================
FILE: assertion/has_properties.go
================================================
package assertion

import (
	"strings"
)

func hasProperties(data interface{}, list string) (MatchResult, error) {
	for _, key := range strings.Split(list, ",") {
		if m, ok := data.(map[string]interface{}); ok {
			if _, ok := m[key]; !ok {
				return doesNotMatch("should have property %v", key)
			}
		}
	}
	return matches()
}


================================================
FILE: assertion/helper_test.go
================================================
package assertion

import (
	"github.com/ghodss/yaml"
	"io/ioutil"
	"testing"
)

type (
	// FixtureTestCases is used to read a set of test cases from a YAML file
	FixtureTestCases struct {
		Description string
		TestCases   []FixtureTestCase `json:"test_cases"`
	}

	// FixtureTestCase describes a single test case
	FixtureTestCase struct {
		Name     string
		Rule     Rule
		Resource Resource
		Result   string
	}
)

// FailTestIfError is a helper to check err and call test Error if it is not nil
func FailTestIfError(err error, message string, t *testing.T) {
	if err != nil {
		t.Error(message + ":" + err.Error())
	}
}

// LoadTestCasesFromFixture reads YAML data describing test cases
func LoadTestCasesFromFixture(filename string, t *testing.T) FixtureTestCases {
	var testCases FixtureTestCases
	content, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Errorf("Unable to read fixture file: %s", filename)
		return testCases
	}
	err = yaml.Unmarshal(content, &testCases)
	if err != nil {
		t.Errorf("Unable to parse fixture file: %s", filename)
		return testCases
	}
	return testCases
}

// RunTestCasesFromFixture loads a YAML file describing test cases and runs them
func RunTestCasesFromFixture(filename string, t *testing.T) {
	fixture := LoadTestCasesFromFixture(filename, t)
	for _, testCase := range fixture.TestCases {
		status, _, err := CheckRule(testCase.Rule, testCase.Resource, mockExternalRuleInvoker())
		FailTestIfError(err, testCase.Name, t)
		if status != testCase.Result {
			t.Errorf("Test case %s returned %s expecting %s", testCase.Name, status, testCase.Result)
		}
	}
}


================================================
FILE: assertion/invoke.go
================================================
package assertion

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

// InvokeViolation has message describing a single validation error
type InvokeViolation struct {
	Message string
}

// InvokeResponse contains a collection of validation errors
type InvokeResponse struct {
	Violations []InvokeViolation
}

// StandardExternalRuleInvoker implements an external HTTP or HTTPS call
type StandardExternalRuleInvoker struct {
}

func makeViolation(rule Rule, resource Resource, message string) Violation {
	return Violation{
		RuleID:           rule.ID,
		Status:           rule.Severity,
		ResourceID:       resource.ID,
		ResourceType:     resource.Type,
		Category:         resource.Category,
		Filename:         resource.Filename,
		RuleMessage:      rule.Message,
		AssertionMessage: message,
		CreatedAt:        currentTime(),
	}
}

func makeViolations(rule Rule, resource Resource, message string) []Violation {
	v := makeViolation(rule, resource, message)
	return []Violation{v}
}

// Invoke an external API to validate a Resource
func (e StandardExternalRuleInvoker) Invoke(rule Rule, resource Resource) (string, []Violation, error) {
	status := "OK"
	violations := make([]Violation, 0)
	var payload interface{}
	payload = resource
	if rule.Invoke.Payload != "" {
		p, err := SearchData(rule.Invoke.Payload, resource.Properties)
		if err != nil {
			return status, violations, err
		}
		payload = p
	}
	payloadJSON, err := JSONStringify(payload)
	if err != nil {
		violations := makeViolations(rule, resource, fmt.Sprintf("Unable to create JSON payload: %s", err.Error()))
		return rule.Severity, violations, err
	}
	Debugf("Invoke %s on %s\n", rule.Invoke.URL, payloadJSON)
	httpResponse, err := http.Post(rule.Invoke.URL, "application/json", bytes.NewBuffer([]byte(payloadJSON)))
	if err != nil {
		violations := makeViolations(rule, resource, fmt.Sprintf("Invoke failed: %s", err.Error()))
		return rule.Severity, violations, err
	}
	if httpResponse.StatusCode != 200 {
		violations := makeViolations(rule, resource, fmt.Sprintf("Invoke failed, StatusCode: %d", httpResponse.StatusCode))
		return rule.Severity, violations, nil
	}
	defer httpResponse.Body.Close()
	body, err := ioutil.ReadAll(httpResponse.Body)
	if err != nil {
		violations := makeViolations(rule, resource, "Invoke response cannot be read")
		return rule.Severity, violations, nil
	}
	Debugf("Invoke body: %s\n", string(body))
	var invokeResponse InvokeResponse
	err = json.Unmarshal(body, &invokeResponse)
	if err != nil {
		violations := makeViolations(rule, resource, "Invoke response cannot be parsed")
		return rule.Severity, violations, nil
	}
	for _, violation := range invokeResponse.Violations {
		status = rule.Severity
		v := makeViolation(rule, resource, violation.Message)
		violations = append(violations, v)
	}
	return status, violations, nil
}


================================================
FILE: assertion/invoke_test.go
================================================
package assertion

import (
	"encoding/json"
	"fmt"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestInvokeOK(t *testing.T) {

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "{}")
	}))
	defer ts.Close()

	i := StandardExternalRuleInvoker{}
	rule := Rule{
		Invoke: InvokeRuleAPI{
			URL: ts.URL,
		},
	}
	resource := Resource{}
	status, violations, err := i.Invoke(rule, resource)
	assert.Equal(t, "OK", status, "Expecting Invoke to return 'OK'")
	assert.Equal(t, 0, len(violations), "Expecting Invoke to return no violations")
	assert.Nil(t, err, "Expecting Invoke to not return an error")
}

func TestInvokeWithViolations(t *testing.T) {
	response := InvokeResponse{
		Violations: []InvokeViolation{
			InvokeViolation{Message: "Something is not right"},
		},
	}
	jsonData, err := json.Marshal(response)
	assert.Nil(t, err, "Failed to marshal test response")
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, string(jsonData))
	}))
	defer ts.Close()

	i := StandardExternalRuleInvoker{}
	rule := Rule{
		Severity: "FAILURE",
		Invoke: InvokeRuleAPI{
			URL: ts.URL,
		},
	}
	resource := Resource{}
	status, violations, err := i.Invoke(rule, resource)
	assert.Equal(t, "FAILURE", status, "Expecting Invoke to return 'FAILURE'")
	assert.Equal(t, 1, len(violations), "Expecting Invoke to return 1 violation")
	assert.Nil(t, err, "Expecting Invoke to not return an error")
}

func TestInvokeSendsMetadata(t *testing.T) {

	var invokedResource Resource
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := ioutil.ReadAll(r.Body)
		_ = json.Unmarshal(body, &invokedResource)
		fmt.Fprintln(w, "{}")
	}))
	defer ts.Close()

	i := StandardExternalRuleInvoker{}
	rule := Rule{
		Invoke: InvokeRuleAPI{
			URL: ts.URL,
		},
	}
	resource := Resource{
		Filename: "example.tf",
	}
	i.Invoke(rule, resource)
	assert.Equal(t, resource.Filename, invokedResource.Filename, "Expecting filename metadata in request body")
}


================================================
FILE: assertion/ip_operations.go
================================================
package assertion

import (
	"fmt"
	"math"
	"net"
	"strconv"
	"strings"
)

var rfc1918PrivateCIDRs = []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}

func getIPObject(addressString string) (net.IP, error) {
	if !strings.Contains(addressString, "/") {
		addressString = fmt.Sprintf("%s/32", addressString)
	}
	ipAddress, _, err := net.ParseCIDR(addressString)
	if err != nil {
		return nil, err
	}
	return ipAddress, nil
}

func isSubnet(ipAddressStr string, supernet string) bool {
	ipAddress, parseError := getIPObject(ipAddressStr)
	if parseError != nil {
		Debugf("%v", parseError)
		return false
	}
	_, superNetwork, err := net.ParseCIDR(supernet)
	if err != nil {
		Debugf("error parsing supernet: %v", err)
	}
	return superNetwork.Contains(ipAddress)
}

func isPrivateIP(ipAddressStr string) bool {
	for _, cidr := range rfc1918PrivateCIDRs {
		if isSubnet(ipAddressStr, cidr) {
			return true
		}
	}
	return false
}

func maxHostCount(ruleCidr string, hostLimitStr string) bool {
	if !strings.Contains(ruleCidr, "/") {
		ruleCidr = fmt.Sprintf("%s/32", ruleCidr)
	}
	hostLimit, convErr := strconv.Atoi(hostLimitStr)
	if convErr != nil {
		Debugf("error converting %v to int", hostLimitStr)
		hostLimit = 0
	}
	_, network, err := net.ParseCIDR(ruleCidr)
	if err != nil {
		Debugf("error parsing ruleCidr: %v", ruleCidr)
		return false
	}
	netmaskOnes, _ := network.Mask.Size()
	return hostCountByNetmaskOnes(netmaskOnes) <= hostLimit
}

func hostCountByNetmaskOnes(netmaskOnes int) int {
	return int(math.Pow(float64(2), float64(32-netmaskOnes)))
}


================================================
FILE: assertion/ip_operations_test.go
================================================
package assertion

import (
	"testing"
)

var ipTests = []struct {
	value          string
	supernet       string
	expectedResult bool
}{
	{"1.1.1.1", "10.0.0.0/8", false},
	{"1.1.1.1/32", "10.0.0.0/8", false},
	{"10.1.0.0/16", "10.0.0.0/8", true},
	{"10.1.1.1/32", "10.0.0.0/8", true},
	{"10.1.1.1", "10.0.0.0/8", true},
}

func TestIsSubnet(t *testing.T) {
	for _, input := range ipTests {
		t.Run(input.value, func(t *testing.T) {
			result := isSubnet(input.value, input.supernet)
			if result != input.expectedResult {
				t.Errorf("got %v, want %v", result, input.expectedResult)
			}
		})
	}
}

var privateIPTests = []struct {
	value          string
	expectedResult bool
}{
	{"1.1.1.1", false},
	{"1.1.1.1/32", false},
	{"10.1.0.0/16", true},
	{"10.1.1.1/32", true},
	{"10.1.1.1", true},
	{"172.16.0.0/12", true},
	{"172.0.0.0/8", false},
	{"172.16.1.1", true},
	{"172.15.1.1", false},
	{"192.168.1.1", true},
	{"52.1.1.1", false},
	{"sg-1234567", false},
}

func TestIsPrivateIp(t *testing.T) {
	for _, input := range privateIPTests {
		t.Run(input.value, func(t *testing.T) {
			result := isPrivateIP(input.value)
			if result != input.expectedResult {
				t.Errorf("got %v, want %v", result, input.expectedResult)
			}
		})
	}
}

var maxHostCountTests = []struct {
	value          string
	max            string
	expectedResult bool
}{
	{"10.0.0.0/8", "1000", false},
	{"10.0.0.0/23", "500", false},
	{"10.1.0.0/16", "65600", true},
	{"10.1.1.1/32", "2", true},
	{"10.1.1.1/32", "1", true},
	{"10.1.1.1", "1", true},
	{"sg-1234567", "0", false},
}

func TestMaxHostCount(t *testing.T) {
	for _, input := range maxHostCountTests {
		t.Run(input.value, func(t *testing.T) {
			result := maxHostCount(input.value, input.max)
			if result != input.expectedResult {
				t.Errorf("got %v, want %v", result, input.expectedResult)
			}
		})
	}
}


================================================
FILE: assertion/log.go
================================================
package assertion

import "fmt"

var (
	isDebug = false
)

// SetDebug turns verbose logging on or off
func SetDebug(b bool) {
	isDebug = b
}

// Debugf prints a formatted string when verbose logging is turned on
func Debugf(format string, args ...interface{}) {
	if isDebug == false {
		return
	}
	fmt.Printf(format, args...)
}

func DebugJSON(title string, object interface{}) {
	if isDebug == false {
		return
	}
	s, _ := JSONStringify(object)
	fmt.Println(title)
	fmt.Println(s)
}


================================================
FILE: assertion/match.go
================================================
package assertion

import (
	"fmt"
	"regexp"
	"strings"
)

func matches() (MatchResult, error) {
	return MatchResult{Match: true, Message: ""}, nil
}

func doesNotMatch(format string, args ...interface{}) (MatchResult, error) {
	return MatchResult{
		Match:   false,
		Message: fmt.Sprintf(format, args...),
	}, nil
}

func matchError(err error) (MatchResult, error) {
	return MatchResult{
		Match:   false,
		Message: err.Error(),
	}, err
}

func isMatch(data interface{}, expression Expression) (MatchResult, error) {
	// FIXME eliminate searchResult this when all operations converted to use data
	// individual ops can call JSONStringify as needed
	searchResult, err := JSONStringify(data)
	if err != nil {
		return matchError(err)
	}
	searchResult = unquoted(searchResult)
	key := expression.Key
	op := expression.Op
	value := expression.Value
	valueType := expression.ValueType

	switch op {
	case "eq":
		if compare(data, value, valueType) == 0 {
			return matches()
		}
		return doesNotMatch("%v(%v) should be equal to %v", key, searchResult, value)
	case "ne":
		if compare(data, value, valueType) != 0 {
			return matches()
		}
		return doesNotMatch("%v(%v) should not be equal to %v", key, searchResult, value)
	case "lt":
		if compare(data, value, valueType) < 0 {
			return matches()
		}
		return doesNotMatch("%v(%v) should be less than %v", key, searchResult, value)
	case "le":
		if compare(data, value, valueType) <= 0 {
			return matches()
		}
		return doesNotMatch("%v(%v) should be less than or equal to %v", key, searchResult, value)
	case "gt":
		if compare(data, value, valueType) > 0 {
			return matches()
		}
		return doesNotMatch("%v(%v) should be greater than %v", key, searchResult, value)
	case "ge":
		if compare(data, value, valueType) >= 0 {
			return matches()
		}
		return doesNotMatch("%v(%v) should be greater than or equal to %v", key, searchResult, value)
	case "in":
		for _, v := range strings.Split(value, ",") {
			if v == searchResult {
				return matches()
			}
		}
		return doesNotMatch("%v(%v) should be in %v", key, searchResult, value)
	case "not-in":
		for _, v := range strings.Split(value, ",") {
			if v == searchResult {
				return doesNotMatch("%v(%v) should not be in %v", key, searchResult, value)
			}
		}
		return matches()
	case "absent":
		if isAbsent(searchResult) {
			return matches()
		}
		return doesNotMatch("%v should be absent", key)
	case "present":
		if isPresent(searchResult) {
			return matches()
		}
		return doesNotMatch("%v should be present", key)
	case "null":
		if data == nil {
			return matches()
		}
		return doesNotMatch("%v should be null", key)
	case "not-null":
		if data != nil {
			return matches()
		}
		return doesNotMatch("%v should not be null", key)
	case "empty":
		if isEmpty(data) {
			return matches()
		}
		return doesNotMatch("%v should be empty", key)
	case "not-empty":
		if !isEmpty(data) {
			return matches()
		}
		return doesNotMatch("%v should not be empty", key)
	case "is-array":
		if isArray(data) {
			return matches()
		}
		return doesNotMatch("%v should be an array", key)
	case "is-not-array":
		if !isArray(data) {
			return matches()
		}
		return doesNotMatch("%v should not be an array", key)
	case "intersect":
		if jsonListsIntersect(searchResult, value) {
			return matches()
		}
		return doesNotMatch("%v should intersect with %v", key, value)
	case "contains":
		return contains(data, key, value)
	case "not-contains":
		return doesNotContain(data, key, value)
	case "does-not-contain":
		return doesNotContain(data, key, value)
	case "starts-with":
		return startsWith(data, key, value)
	case "ends-with":
		return endsWith(data, key, value)
	case "regex":
		re, err := regexp.Compile(value)
		if err != nil {
			return matchError(err)
		}
		if re.MatchString(searchResult) {
			return matches()
		}
		return doesNotMatch("%v(%v) should match %v", key, searchResult, value)
	case "has-properties":
		return hasProperties(data, value)
	case "is-true":
		if searchResult == "true" {
			return matches()
		}
		return doesNotMatch("%v should be 'true', not '%v'", key, value)
	case "is-false":
		if searchResult == "false" {
			return matches()
		}
		return doesNotMatch("%v should be 'false', not '%v'", key, value)
	case "is-subnet":
		isSubnet := isSubnet(searchResult, value)
		if isSubnet {
			return matches()
		}
		return doesNotMatch("%v should be a subnet of %v", searchResult, value)
	case "is-private-ip":
		isPrivate := isPrivateIP(searchResult)
		if isPrivate {
			return matches()
		}
		return doesNotMatch("%v should be a private ip", searchResult)
	case "max-host-count":
		hostCountWithinLimit := maxHostCount(searchResult, value)
		if hostCountWithinLimit {
			return matches()
		}
		return doesNotMatch("%v should be less than or equal to %v", searchResult, value)
	}
	return doesNotMatch("unknown op %v", op)
}


================================================
FILE: assertion/match_test.go
================================================
package assertion

import (
	"encoding/json"
	"fmt"
	"testing"
)

type MatchTestCase struct {
	SearchResult   interface{}
	Op             string
	Value          string
	ValueType      string
	ExpectedResult bool
}

func getQuotesRight(jsonString string) string {
	if len(jsonString) == 0 {
		return jsonString
	}
	if jsonString[0] != '[' {
		jsonString = quoted(jsonString)
	}
	return jsonString
}

func unmarshal(s string) (interface{}, error) {
	var searchResult interface{}
	jsonString := getQuotesRight(s)
	if len(jsonString) > 0 {
		err := json.Unmarshal([]byte(jsonString), &searchResult)
		if err != nil {
			return "", err
		}
	}
	return searchResult, nil
}

func TestIsMatch(t *testing.T) {

	sliceOfTags := []interface{}{"Foo", "Bar"}
	emptySlice := []interface{}{}
	anotherSlice := []interface{}{"One", "Two"}
	stringSlice := []string{"One", "Two"}

	testCases := map[string]MatchTestCase{
		"eqTrue":                         {"Foo", "eq", "Foo", "", true},
		"eqFalse":                        {"Foo", "eq", "Bar", "", false},
		"eqIntegerTrue":                  {22, "eq", "22", "integer", true},
		"eqIntegerFalse":                 {80, "eq", "22", "integer", false},
		"neFalse":                        {"Foo", "ne", "Foo", "", false},
		"neTrue":                         {"Foo", "ne", "Bar", "", true},
		"inTrue":                         {"Foo", "in", "Foo,Bar,Baz", "", true},
		"inFalse":                        {"Foo", "in", "Bar,Baz", "", false},
		"notInFalse":                     {"Foo", "not-in", "Foo,Bar,Baz", "", false},
		"notInTrue":                      {"Foo", "not-in", "Bar,Baz", "", true},
		"absentFalse":                    {"Foo", "absent", "", "", false},
		"absentTrueForEmptyString":       {"", "absent", "", "", true},
		"absentTrueForNull":              {"null", "absent", "", "", true},
		"absentTrueForEmptyArray":        {"[]", "absent", "", "", true},
		"presentTrue":                    {sliceOfTags, "present", "", "", true},
		"presentStringTrue":              {"Foo", "present", "", "", true},
		"presentFalseForNil":             {nil, "present", "", "", false},
		"presentFalseForEmptyString":     {"", "present", "", "", false},
		"presentFalseForNull":            {"null", "present", "", "", false},
		"presentFalseForEmptyArray":      {"[]", "present", "", "", false},
		"containsTrueForString":          {"Foo", "contains", "oo", "", true},
		"containsFalseForString":         {"Foo", "contains", "aa", "", false},
		"containsTrueForSlice":           {sliceOfTags, "contains", "Bar", "", true},
		"containsFalseForSubstring":      {sliceOfTags, "contains", "abc", "", false},
		"containsTrueForSliceOfStrings":  {stringSlice, "contains", "One", "", true},
		"containsFalseForSliceOfStrings": {stringSlice, "contains", "Three", "", false},
		"containsTrueForInt":             {1, "contains", "1", "", true},
		"containsFalseForInt":            {1, "contains", "One", "", false},
		"notContainsFalseForString":      {"Foo", "does-not-contain", "oo", "", false},
		"notContainsTrueForString":       {"Foo", "does-not-contain", "aa", "", true},
		"notContainsFalseForSlice":       {sliceOfTags, "does-not-contain", "Bar", "", false},
		"notContainsTrueForSubstring":    {sliceOfTags, "does-not-contain", "abc", "", true},
		"regexTrueForEndOfString":        {"Foo", "regex", "o$", "", true},
		"regexFalseForEndOfString":       {"Bar", "regex", "o$", "", false},
		"regExTrueForBeginningOfString":  {"Foo", "regex", "^F", "", true},
		"regExFalseForBeginningOfString": {"Foo", "regex", "^B", "", false},
		"reqExFalseForEntireString":      {"Foo", "regex", "^Bar$", "", false},
		"regExIgnoreCaseTrue":            {"HTTPS", "regex", "(?i)https", "", true},
		"regexIgnoreCaseFalse":           {"HTTP", "regex", "(?i)https", "", false},
		"ltTrue":                         {"a", "lt", "b", "", true},
		"ltFalse":                        {"a", "lt", "a", "", false},
		"leTrue":                         {"a", "le", "a", "", true},
		"leFalse":                        {"b", "le", "a", "", false},
		"gtTrue":                         {"b", "gt", "a", "", true},
		"gtFalse":                        {"b", "gt", "b", "", false},
		"geTrue":                         {"b", "ge", "b", "", true},
		"geFalse":                        {"b", "ge", "c", "", false},
		"nullTrue":                       {"", "null", "", "", true},
		"nullFalse":                      {"1", "null", "", "", false},
		"notNullFalse":                   {"", "not-null", "", "", false},
		"notNullTrue":                    {"1", "not-null", "", "", true},
		"emptyTrueForEmptyString":        {"", "empty", "", "", true},
		"emptyFalseForString":            {"Foo", "empty", "", "", false},
		"emptyTrueForEmptySlice":         {emptySlice, "empty", "", "", true},
		"emptyFalseForSlice":             {sliceOfTags, "empty", "", "", false},
		"notEmptyFalseForEmptyString":    {"", "not-empty", "", "", false},
		"notEmptyTrueForString":          {"Foo", "not-empty", "", "", true},
		"notEmptyFalseForEmptySlice":     {emptySlice, "not-empty", "", "", false},
		"notEmptyTrueForSlice":           {sliceOfTags, "not-empty", "", "", true},
		"intersectTrue":                  {"[\"one\",\"two\"]", "intersect", "[\"two\",\"three\"]", "", true},
		"intersectFalse":                 {"[\"one\",\"two\"]", "intersect", "[\"three\",\"four\"]", "", false},
		"eqSizeTrue":                     {anotherSlice, "eq", "2", "size", true},
		"eqSizeFalse":                    {anotherSlice, "eq", "3", "size", false},
		"isTrue":                         {"true", "is-true", "", "", true},
		"isNotTrue":                      {"false", "is-true", "", "", false},
		"isFalse":                        {"false", "is-false", "", "", true},
		"isNotFalse":                     {"100", "is-false", "", "", false},
		"startsWithTrue":                 {"FooBar", "starts-with", "Foo", "", true},
		"startsWithFalse":                {"FooBar", "starts-with", "Bar", "", false},
		"startsWithNonString":            {1, "starts-with", "Foo", "", false},
		"endsWithTrue":                   {"FooBar", "ends-with", "Bar", "", true},
		"endsWithFalse":                  {"FooBar", "ends-with", "Foo", "", false},
		"endsartWithNonString":           {1, "ends-with", "Foo", "", false},
		"isArrayTrue":                    {sliceOfTags, "is-array", "", "", true},
		"isArrayFalse":                   {"Foo", "is-array", "", "", false},
		"isNotArrayTrue":                 {sliceOfTags, "is-not-array", "", "", false},
		"isNotArrayFalse":                {"Foo", "is-not-array", "", "", true},
	}
	for k, tc := range testCases {
		var m MatchResult
		var err error
		expression := Expression{
			Key:       "key",
			Op:        tc.Op,
			Value:     tc.Value,
			ValueType: tc.ValueType,
		}
		if s, isString := tc.SearchResult.(string); isString {
			searchResult, err := unmarshal(s)
			if err != nil {
				fmt.Println(err)
				t.Errorf("Unable to parse %s\n", tc.SearchResult)
			}
			m, err = isMatch(searchResult, expression)
		} else {
			m, err = isMatch(tc.SearchResult, expression)
		}
		if err != nil {
			t.Errorf("%s Failed with error: %s", k, err.Error())
		}
		if m.Match != tc.ExpectedResult {
			t.Errorf("%s Failed Expected '%s' %s '%s' to be %t", k, tc.SearchResult, tc.Op, tc.Value, tc.ExpectedResult)
		}
	}
}


================================================
FILE: assertion/rules.go
================================================
package assertion

import (
	"errors"
	"github.com/ghodss/yaml"
)

// ParseRules converts YAML string content to a Result
func ParseRules(rules string) (RuleSet, error) {
	r := RuleSet{}
	err := yaml.Unmarshal([]byte(rules), &r)
	return r, err
}

// FilterRulesByTag selects a subset of rules based on a tag
func FilterRulesByTag(rules []Rule, tags []string) []Rule {
	filteredRules := make([]Rule, 0)
	for _, rule := range rules {
		if tags == nil || listsIntersect(tags, rule.Tags) {
			filteredRules = append(filteredRules, rule)
		}
	}
	return filteredRules
}

// FilterRulesByID selectes a subset of rules based on ID
func FilterRulesByID(rules []Rule, ruleIDs []string, ignoreRuleIDs []string) []Rule {
	if len(ruleIDs) == 0 && len(ignoreRuleIDs) == 0 {
		return rules
	}
	filteredRules := make([]Rule, 0)
	for _, rule := range rules {
		include := false
		for _, id := range ruleIDs {
			if id == rule.ID {
				include = true
			}
		}
		if len(ignoreRuleIDs) > 0 {
			include = true
			for _, id := range ignoreRuleIDs {
				if id == rule.ID {
					include = false
				}
			}
		}
		if include {
			filteredRules = append(filteredRules, rule)
		}
	}
	return filteredRules
}

func uniqueRules(list []Rule) []Rule {
	rules := make([]Rule, 0)
	keys := make(map[string]bool, 0)
	for _, rule := range list {
		if _, ok := keys[rule.ID]; !ok {
			keys[rule.ID] = true
			rules = append(rules, rule)
		}
	}
	return rules
}

// FilterRulesByTagAndID filters by both tag and id
func FilterRulesByTagAndID(rules []Rule, tags []string, ruleIds []string, ignoreRuleIds []string) []Rule {
	if len(tags) == 0 && len(ruleIds) == 0 && len(ignoreRuleIds) == 0 {
		return rules
	}
	if len(tags) == 0 {
		return FilterRulesByID(rules, ruleIds, ignoreRuleIds)
	}
	if len(ruleIds) == 0 {
		return FilterRulesByTag(rules, tags)
	}
	return uniqueRules(append(FilterRulesByID(rules, ruleIds, ignoreRuleIds), FilterRulesByTag(rules, tags)...))
}

// ResolveRules loads any dynamic values for a collection or rules
func ResolveRules(rules []Rule, valueSource ValueSource) ([]Rule, []Violation) {
	resolvedRules := []Rule{}
	violations := []Violation{}
	for _, rule := range rules {
		r, vs := ResolveRule(rule, valueSource)
		resolvedRules = append(resolvedRules, r)
		violations = append(violations, vs...)
	}
	return resolvedRules, violations
}

// ResolveRule loads any dynamic values for a single Rule
func ResolveRule(rule Rule, valueSource ValueSource) (Rule, []Violation) {
	resolvedRule := rule
	resolvedRule.Assertions = []Expression{}
	violations := []Violation{}
	for _, assertion := range rule.Assertions {
		value, err := valueSource.GetValue(assertion)
		if err != nil {
			Debugf("ResolveRule error: %s\n", err.Error())
			violations = append(violations, Violation{
				Category:         "load",
				RuleID:           "RULE_RESOLVE",
				ResourceID:       rule.ID,
				ResourceType:     "rule",
				Status:           "FAILURE",
				RuleMessage:      "Unable to resolve value in rule",
				AssertionMessage: err.Error(),
				CreatedAt:        currentTime(),
			})
		}
		resolvedAssertion := assertion
		resolvedAssertion.Value = value
		resolvedAssertion.ValueFrom = ValueFrom{}
		resolvedRule.Assertions = append(resolvedRule.Assertions, resolvedAssertion)
	}
	return resolvedRule, violations
}

// CheckRule returns a list of violations for a single Rule applied to a single Resource
func CheckRule(rule Rule, resource Resource, e ExternalRuleInvoker) (string, []Violation, error) {
	returnStatus := "OK"
	violations := make([]Violation, 0)
	if ExcludeResource(rule, resource) {
		Debugf("Ignoring resource: %s", resource.ID)
		return returnStatus, violations, nil
	}
	if rule.Invoke.URL != "" {
		return e.Invoke(rule, resource)
	}
	match, err := andExpression(rule.Conditions, resource)
	if err != nil {
		return "FAILURE", violations, err
	}
	if !match.Match {
		return returnStatus, violations, nil
	}
	for _, ruleAssertion := range rule.Assertions {
		Debugf("Checking Category: %s, Type: %s, Id: %s\n", resource.Category, resource.Type, resource.ID)
		expressionResult, err := CheckExpression(rule, ruleAssertion, resource)
		if err != nil {
			return "FAILURE", violations, err
		}
		if expressionResult.Status != "OK" {
			// If the rule has category (e.g. Terraform rules), then return violations for that category only.
			// If the rule has no category it will be applied to all resources as normal.
			if rule.Category != "" && rule.Category != resource.Category {
			    break
			}
			returnStatus = expressionResult.Status
			v := Violation{
				RuleID:           rule.ID,
				ResourceID:       resource.ID,
				ResourceType:     resource.Type,
				Category:         resource.Category,
				Status:           expressionResult.Status,
				RuleMessage:      rule.Message,
				AssertionMessage: expressionResult.Message,
				Filename:         resource.Filename,
				LineNumber:       resource.LineNumber,
				CreatedAt:        currentTime(),
			}
			violations = append(violations, v)
		}
	}
	return returnStatus, violations, nil
}

// Join two RuleSets together
func JoinRuleSets(firstSet RuleSet, secondSet RuleSet) (RuleSet, error) {
	// if one of the sets is empty, return the other
	// if both are empty, an empty set is returned
	if len(firstSet.Rules) == 0 {
		return secondSet, nil
	} else if len(secondSet.Rules) == 0 {
		return firstSet, nil
	}

	// RuleSets must match Type and Version
	// Description will be taken from the first given rule set
	if firstSet.Type != secondSet.Type || firstSet.Version != secondSet.Version {
		return firstSet, errors.New("RuleSet Type and Version must match")
	} else {
		joinedSet := RuleSet{}
		joinedSet.Type = firstSet.Type
		joinedSet.Description = firstSet.Description
		joinedSet.Files = append(firstSet.Files, secondSet.Files...)
		joinedSet.Rules = append(firstSet.Rules, secondSet.Rules...)
		joinedSet.Version = firstSet.Version
		joinedSet.Resources = append(firstSet.Resources, secondSet.Resources...)
		joinedSet.Columns = append(firstSet.Columns, secondSet.Columns...)
		return joinedSet, nil
	}
}


================================================
FILE: assertion/rules_test.go
================================================
package assertion

import (
	"errors"
	"testing"
)

// TestValueSource provides test values

type TestValueSource struct{}

func (t TestValueSource) GetValue(expression Expression) (string, error) {
	if expression.Value != "" {
		return expression.Value, nil
	}
	return "m3.medium", nil
}

func testValueSource() ValueSource {
	return TestValueSource{}
}

// TestValueSourceWithError simulates errors for value provider

type TestValueSourceWithError struct{}

func (t TestValueSourceWithError) GetValue(expression Expression) (string, error) {
	return "", errors.New("GET_VALUE_ERROR")
}

func testValueSourceWithError() ValueSource {
	return TestValueSourceWithError{}
}

// MockExternalRuleInvoker simulates invocation of external endpoints to get values

type MockExternalRuleInvoker int

func mockExternalRuleInvoker() *MockExternalRuleInvoker {
	var m MockExternalRuleInvoker
	return &m
}

func (e *MockExternalRuleInvoker) Invoke(Rule, Resource) (string, []Violation, error) {
	*e++
	noViolations := make([]Violation, 0)
	return "OK", noViolations, nil
}

var content = `Rules:
  - id: TEST1
    message: Test message
    resource: aws_instance
    severity: WARNING
    assertions:
      - key: instance_type
        op: in
        value: t2.micro
    tags:
      - ec2
  - id: TEST2
    message: Test message
    resource: aws_s3_bucket
    severity: WARNING
    assertions:
      - key: name
        op: eq
        value: bucket1
    tags:
      - s3
  - id: TEST3
    message: Test message
    resource: aws_ebs_volume
    severity: WARNING
    assertions:
      - key: size
        op: le
        value: 1000
        value_type: integer
    tags:
      - ebs
`

func MustParseRules(content string, t *testing.T) RuleSet {
	r, err := ParseRules(content)
	if err != nil {
		t.Error("Unable to parse:" + content)
	}
	return r
}

func TestParseRules(t *testing.T) {
	r := MustParseRules(content, t)
	if len(r.Rules) != 3 {
		t.Error("Expected to parse 3 rules")
	}
}

type FilterTestCase struct {
	Tags          []string
	Ids           []string
	IgnoreIds     []string
	ExpectedRules []string
}

func TestFilterRules(t *testing.T) {

	var emptyTags []string
	var emptyIds []string

	testCases := map[string]FilterTestCase{
		"allRules": FilterTestCase{emptyTags, emptyIds, emptyIds, []string{"TEST1", "TEST2", "TEST3"}},
		"tags":     FilterTestCase{[]string{"s3"}, emptyIds, emptyIds, []string{"TEST2"}},
		"rules":    FilterTestCase{emptyTags, []string{"TEST1"}, emptyIds, []string{"TEST1"}},
		"both":     FilterTestCase{[]string{"s3"}, []string{"TEST1"}, emptyIds, []string{"TEST1", "TEST2"}},
		"overlap":  FilterTestCase{[]string{"s3"}, []string{"TEST2"}, emptyIds, []string{"TEST2"}},
		"exclude":  FilterTestCase{emptyTags, emptyIds, []string{"TEST1"}, []string{"TEST2", "TEST3"}},
	}
	for k, tc := range testCases {
		r := FilterRulesByTagAndID(MustParseRules(content, t).Rules, tc.Tags, tc.Ids, tc.IgnoreIds)
		if len(r) != len(tc.ExpectedRules) {
			t.Errorf("Expected %s to include %d rules not %d\n", k, len(tc.ExpectedRules), len(r))
		}
	}
}

func TestFilterRulesByTagAndID(t *testing.T) {
	tags := []string{"s3"}
	ids := []string{"TEST3"}
	r := FilterRulesByTagAndID(MustParseRules(content, t).Rules, tags, ids, []string{})
	if len(r) != 2 {
		t.Error("Expected filterRulesByTag to return 2 rules")
	}
	for _, rule := range r {
		if rule.ID != "TEST2" && rule.ID != "TEST3" {
			t.Error("Expected filterRulesByTagAndID to select correct rules")
		}
	}
}

var ruleWithMultipleFilters = `Rules:
  - id: TEST1
    message: Test message
    resource: aws_instance
    severity: FAILURE
    assertions:
      - key: instance_type
        op: eq
        value: t2.micro
      - key: ami
        op: eq
        value: ami-000000
`

func TestRuleWithMultipleFilter(t *testing.T) {
	rules := MustParseRules(ruleWithMultipleFilters, t)
	resource := Resource{
		ID:         "a_test_resource",
		Type:       "aws_instance",
		Properties: map[string]interface{}{"instance_type": "t2.micro", "ami": "ami-000000"},
		Filename:   "test.tf",
	}
	status, violations, err := CheckRule(rules.Rules[0], resource, mockExternalRuleInvoker())
	if err != nil {
		t.Error("Error in CheckRule:" + err.Error())
	}
	if status != "OK" {
		t.Error("Expecting multiple rule to match")
	}
	if len(violations) != 0 {
		t.Error("Expecting multiple rule to have zero violations")
	}
}

func TestMultipleFiltersWithSingleFailure(t *testing.T) {
	rules := MustParseRules(ruleWithMultipleFilters, t)
	resource := Resource{
		ID:         "a_test_resource",
		Type:       "aws_instance",
		Properties: map[string]interface{}{"instance_type": "t2.micro", "ami": "ami-111111"},
		Filename:   "test.tf",
	}
	status, violations, err := CheckRule(rules.Rules[0], resource, mockExternalRuleInvoker())
	if err != nil {
		t.Error("Error in CheckRule:" + err.Error())
	}
	if status != "FAILURE" {
		t.Error("Expecting multiple rule to return FAILURE")
	}
	if len(violations) != 1 {
		t.Error("Expecting multiple rule to have one violation")
	}
}

func TestMultipleFiltersWithMultipleFailures(t *testing.T) {
	rules := MustParseRules(ruleWithMultipleFilters, t)
	resource := Resource{
		ID:         "a_test_resource",
		Type:       "aws_instance",
		Properties: map[string]interface{}{"instance_type": "c3.medium", "ami": "ami-111111"},
		Filename:   "test.tf",
	}
	status, violations, err := CheckRule(rules.Rules[0], resource, mockExternalRuleInvoker())
	if err != nil {
		t.Error("Error in CheckRule:" + err.Error())
	}
	if status != "FAILURE" {
		t.Error("Expecting multiple rule to return FAILURE")
	}
	if len(violations) != 2 {
		t.Error("Expecting multiple rule to have two violations")
	}
}

var ruleWithValueFrom = `Rules:
  - id: FROM1
    message: Test value_from
    severity: FAILURE
    resource: aws_instance
    assertions:
      - key: instance_type
        op: in
        value_from:
          bucket: config-rules-for-lambda
          key: instance-types
`

func TestValueFrom(t *testing.T) {
	rules := MustParseRules(ruleWithValueFrom, t)
	resource := Resource{
		ID:         "a_test_resource",
		Type:       "aws_instance",
		Properties: map[string]interface{}{"instance_type": "m3.medium"},
		Filename:   "test.tf",
	}
	resolved, violations := ResolveRules(rules.Rules, testValueSource())
	if len(violations) != 0 {
		t.Errorf("Expecting ResolveRules to return 0 violations: %v", violations)
	}
	status, violations, err := CheckRule(resolved[0], resource, mockExternalRuleInvoker())
	if err != nil {
		t.Error("Error in CheckRule:" + err.Error())
	}
	if status != "OK" {
		t.Error("Expecting value_from to match")
	}
	if len(violations) != 0 {
		t.Error("Expecting value_from test to have 0 violations")
	}
}

func TestResolveRuleError(t *testing.T) {
	rules := MustParseRules(ruleWithValueFrom, t)
	_, violations := ResolveRules(rules.Rules, testValueSourceWithError())
	if len(violations) != 1 {
		t.Errorf("Expecting ResolveRules to return 1 violations: %v", violations)
	} else {
		ruleID := violations[0].RuleID
		if ruleID != "RULE_RESOLVE" {
			t.Errorf("Expected RULE_RESOLVE violation, not %s", ruleID)
		}
	}
}

var ruleWithInvoke = `Rules:
  - id: FROM1
    message: Test value_from
    severity: FAILURE
    resource: aws_instance
    invoke:
      url: http://localhost
`

func TestInvokeRule(t *testing.T) {
	rules := MustParseRules(ruleWithInvoke, t)
	resource := Resource{
		ID:         "a_test_resource",
		Type:       "aws_instance",
		Properties: map[string]interface{}{"instance_type": "m3.medium"},
		Filename:   "test.tf",
	}
	resolved, _ := ResolveRules(rules.Rules, testValueSource())
	counter := mockExternalRuleInvoker()
	CheckRule(resolved[0], resource, counter)
	if *counter != 1 {
		t.Error("Expecting external rule engine to be invoked")
	}
}


================================================
FILE: assertion/search.go
================================================
package assertion

import (
	"github.com/jmespath/go-jmespath"
)

// SearchData applies a JMESPath to a JSON object
func SearchData(expression string, data interface{}) (interface{}, error) {
	if len(expression) == 0 {
		return "null", nil
	}

	return jmespath.Search(expression, data)
}


================================================
FILE: assertion/testdata/collection-assertions.yaml
================================================
---
description: Test collection assertions
test_cases:

  - name: every_OK
    rule:
      id:       COLLECTION
      message:  Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - every:
            key: "keys(@)"
            expressions:
              - key: "@"
                op: in
                value: Foo,Bar
    resource:
      id: collection_id
      type: example
      properties:
        Foo:
          - A
          - B
          - C
        Bar:
          - D
          - E
    result: OK

  - name: every_FAILURE
    rule:
      id:       COLLECTION
      message:  Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - every:
            key: "keys(@)"
            expressions:
              - key: "@"
                op: in
                value: Foo,Bar
    resource:
      id: collection_id
      type: example
      properties:
        Foo:
          - A
          - B
          - C
        Bar:
          - D
          - E
        Baz:
          - F
    result: FAILURE

  - name: every_multiple_assertions_FAILURE
    rule:
      id:       COLLECTION
      message:  Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - every:
            key: locations
            expressions:
              - key: city
                op: present
              - key: state
                op: present
    resource:
      id: collection_id
      type: example
      properties:
        locations:
          - city: Seattle
            state: WA
          - city: San Francisco
    result: FAILURE

  - name: some_OK
    rule:
      id:       COLLECTION
      message:  Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - some:
            key: "keys(@)"
            expressions:
              - key: "@"
                op: in
                value: Foo,Bar
    resource:
      id: collection_id
      type: example
      properties:
        Foo:
          - A
          - B
          - C
        Baz:
          - D
          - E
    result: OK

  - name: some_FAILURE
    rule:
      id:       COLLECTION
      message:  Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - some:
            key: "keys(@)"
            expressions:
              - key: "@"
                op: in
                value: Foo,Bar
    resource:
      id: collection_id
      type: example
      properties:
        Baz:
          - A
    result: FAILURE

  - name: none_OK
    rule:
      id:       COLLECTION
      message:  Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - none:
            key: "keys(@)"
            expressions:
              - key: "@"
                op: in
                value: Foo,Bar
    resource:
      id: collection_id
      type: example
      properties:
        Baz:
          - A
          - B
    result: OK

  - name: none_with_multiple_assertions_OK
    rule:
      id: COLLECTION
      message: Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - none:
            key: "ipPermissions[]"
            expressions:
              - key: "fromPort"
                op: eq
                value: 22
                value_type: integer
              - key: "ipRanges[]"
                op: contains
                value: 0.0.0.0/0
    resource:
      id: collection_id
      type: sample
      properties:
        ipPermissions:
          - fromPort: 80
            ipRanges:
              - 0.0.0.0/0
    result: OK

  - name: none_FAILURE
    rule:
      id:       COLLECTION
      message:  Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - none:
            key: "keys(@)"
            expressions:
              - key: "@"
                op: in
                value: Foo,Bar
    resource:
      id: collection_id
      type: example
      properties:
        Foo:
          - A
        Bar:
          - B
    result: FAILURE

  - name: none_with_multiple_assertions_FAILURE
    rule:
      id: COLLECTION
      message: Invalid key
      severity: FAILURE
      resource: sample
      assertions:
        - none:
            key: "ipPermissions[]"
            expressions:
              - key: "fromPort"
                op: eq
                value: 22
                value_type: integer
              - key: "ipRanges[]"
                op: contains
                value: 0.0.0.0/0
    resource:
      id: collection_id
      type: sample
      properties:
        ipPermissions:
          - fromPort: 22
            ipRanges:
              - 0.0.0.0/0
    result: FAILURE

  - name: one_OK
    rule:
      id:       COLLECTION
      message:  Duplicate names
      severity: FAILURE
      resource: example
      assertions:
        - exactly-one:
            key: "tags[]"
            expressions:
              - key: name
                op: eq
                value: A
    resource:
      id: collection_id
      type: example
      properties:
        tags:
          - name: A
          - name: B
    result: OK

  - name: one_FAILURE
    rule:
      id:       COLLECTION
      message:  Duplicate names
      severity: FAILURE
      resource: example
      assertions:
        - exactly-one:
            key: "tags[]"
            expressions:
              - key: name
                op: eq
                value: B
    resource:
      id: collection_id
      type: example
      properties:
        tags:
          - name: A
          - name: B
          - name: B
    result: FAILURE



================================================
FILE: assertion/testdata/conditions.yaml
================================================
---
description: Test conditions
test_cases:

  - name: conditions_false
    rule:
      id: CONDITIONS_1
      message: Missing properties
      severity: FAILURE
      resource: sample
      conditions:
        - key: example.name
          eq: first
      assertions:
        - key: example
          op: has-properties
          value: name,id
    resource:
      id: p1
      type: sample
      properties:
        example:
          name: first
          id: 1
    result: OK

  - name: conditions_ignore
    rule:
      id: PROPERTIES_2
      message: Ignore using condition
      severity: FAILURE
      resource: sample
      conditions:
        - key: example.name
          op: eq
          value: second
      assertions:
        - key: example
          op: has-properties
          value: name,id,description
    resource:
      id: p1
      type: sample
      properties:
        example:
          name: first
          id: 1
    result: OK

  - name: conditions_FAILURE
    rule:
      id: PROPERTIES_2
      message: Missing properties
      severity: FAILURE
      resource: sample
      conditions:
        - key: example.name
          op: eq
          value: first
      assertions:
        - key: example
          op: has-properties
          value: name,id,description
    resource:
      id: p1
      type: sample
      properties:
        example:
          name: first
          id: 1
    result: FAILURE


================================================
FILE: assertion/testdata/default-severity.yaml
================================================
---
description: Test uses default severity
test_cases:

  - name: default-severity-FAILURE
    rule:
      id: PROPERTIES_1
      message: Missing properties
      resource: sample
      assertions:
        - key: example
          op: has-properties
          value: name,id
    resource:
      id: p1
      type: sample
      properties:
        example:
          name: first
    result: FAILURE


================================================
FILE: assertion/testdata/has-properties.yaml
================================================
---
description: Test has-properties operator
test_cases:

  - name: has-properties_OK
    rule:
      id: PROPERTIES_1
      message: Missing properties
      severity: FAILURE
      resource: sample
      assertions:
        - key: example
          op: has-properties
          value: name,id
    resource:
      id: p1
      type: sample
      properties:
        example:
          name: first
          id: 1
    result: OK

  - name: has-properties_FAILURE
    rule:
      id: PROPERTIES_2
      message: Missing properties
      severity: FAILURE
      resource: sample
      assertions:
        - key: example
          op: has-properties
          value: name,id,description
    resource:
      id: p1
      type: sample
      properties:
        example:
          name: first
          id: 1
    result: FAILURE


================================================
FILE: assertion/types.go
================================================
package assertion

type (

	// Resource describes a resource to be linted
	Resource struct {
		ID         string `cty:"aws_instance"`
		Type       string
		Category   string // default is "resource", can be "data", "provider" for Terraform
		Properties interface{}
		Filename   string
		LineNumber int
	}

	// RuleSet describes a collection of rules for a Linter
	RuleSet struct {
		Type        string
		Description string
		Files       []string
		Rules       []Rule
		Version     string
		Resources   []ResourceConfig
		Columns     []ColumnConfig
		Source      string
	}

	// Rule is part of a RuleSet
	Rule struct {
		ID              string
		Message         string
		Severity        string
		Resource        string
		Resources       []string
		ExceptResources []string `json:"except_resources"`
		Category        string   // default is "resource", can be "data", "provider", "module" for Terraform
		Conditions      []Expression
		Assertions      []Expression
		Except          []string
		Tags            []string
		Invoke          InvokeRuleAPI
	}

	// Expression expression for a Rule
	Expression struct {
		Key        string
		Op         string
		Value      string
		ValueType  string    `json:"value_type"`
		ValueFrom  ValueFrom `json:"value_from"`
		Or         []Expression
		Xor        []Expression
		And        []Expression
		Not        []Expression
		Every      CollectionExpression
		Some       CollectionExpression
		None       CollectionExpression
		ExactlyOne CollectionExpression `json:"exactly-one"`
	}

	// CollectionExpression assertion for every element of a collection
	CollectionExpression struct {
		Key         string
		Expressions []Expression
	}

	// ValueFrom describes a external source for values
	ValueFrom struct {
		URL      string
		Variable string
	}

	// InvokeRuleAPI describes an external API for linting a resource
	InvokeRuleAPI struct {
		URL     string
		Payload string
	}

	// ResourceConfig describes how to discover resouces in a YAML file
	ResourceConfig struct {
		ID   string
		Type string
		Key  string
	}

	// ColumnConfig describes how to discover resources in a CSV file
	ColumnConfig struct {
		Name string
	}

	// ValidationReport summarizes validation for resources using rules
	ValidationReport struct {
		FilesScanned     []string
		Violations       []Violation
		ResourcesScanned []ScannedResource
	}

	// Violation has details for a failed assertion
	Violation struct {
		RuleID           string
		ResourceID       string
		ResourceType     string
		Category         string
		Status           string
		RuleMessage      string
		AssertionMessage string
		Filename         string
		LineNumber       int
		CreatedAt        string
	}

	// ScannedResource has details for each resource scanned
	ScannedResource struct {
		ResourceID   string
		ResourceType string
		RuleID       string
		Status       string
		Filename     string
		LineNumber   int
	}

	// ValueSource interface to fetch dynamic values
	ValueSource interface {
		GetValue(Expression) (string, error)
	}

	// ExternalRuleInvoker defines an interface for invoking an external API
	ExternalRuleInvoker interface {
		Invoke(Rule, Resource) (string, []Violation, error)
	}

	// MatchResult has a true/false result, but also includes a message for better reporting
	MatchResult struct {
		Match   bool
		Message string
	}

	// Result returns a status, along with a message
	Result struct {
		Status  string
		Message string
	}
)


================================================
FILE: assertion/util.go
================================================
package assertion

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"time"
)

func unquoted(s string) string {
	if s[0] == '"' {
		return s[1 : len(s)-1]
	}
	return s
}

func quoted(s string) string {
	return fmt.Sprintf("\"%s\"", s)
}

func isAbsent(s string) bool {
	if s == "" || s == "null" || s == "[]" {
		return true
	}
	return false
}

func isPresent(s string) bool {
	return !isAbsent(s)
}

func isEmpty(data interface{}) bool {
	switch v := data.(type) {
	case nil:
		return true
	case string:
		return len(v) == 0
	case []interface{}:
		return len(v) == 0
	case []map[string]interface{}:
		return len(v) == 0
	default:
		Debugf("isEmpty default: %v %T\n", data, data)
		return false
	}
}

func isArray(data interface{}) bool {
	switch data.(type) {
	case nil:
		return false
	case string:
		return false
	case []interface{}:
		return true
	case []map[string]interface{}:
		return true
	default:
		return false
	}
}

func listsIntersect(list1 []string, list2 []string) bool {
	for _, a := range list1 {
		for _, b := range list2 {
			if a == b {
				return true
			}
		}
	}
	return false
}

func jsonListsIntersect(s1 string, s2 string) bool {
	var a1 []string
	var a2 []string
	err := json.Unmarshal([]byte(s1), &a1)
	if err != nil {
		return false
	}
	err = json.Unmarshal([]byte(s2), &a2)
	if err != nil {
		return false
	}
	return listsIntersect(a1, a2)
}

// ShouldIncludeFile return true if a filename matches one of a list of patterns
func ShouldIncludeFile(patterns []string, filename string) (bool, error) {
	if filename == "-" { // always permit stdin
		return true, nil
	}
	for _, pattern := range patterns {
		_, file := filepath.Split(filename)
		matched, err := filepath.Match(pattern, file)
		if err != nil {
			return false, err
		}
		if matched {
			return true, nil
		}
	}
	return false, nil
}

// FilterResourcesByType filters a list of resources that match a single resource type
func FilterResourcesByType(resources []Resource, resourceType string, resourceCategory string) []Resource {
	if resourceType == "*" {
		return resources
	}
	filtered := make([]Resource, 0)
	for _, resource := range resources {
		if resource.Type == resourceType && categoryMatches(resourceCategory, resource.Category) {
			filtered = append(filtered, resource)
		}
	}
	return filtered
}

// FilterResourcesByTypes filters a list of resources that match a slice of resource types
func FilterResourcesByTypes(resources []Resource, resourceTypes []string, resourceCategory string) []Resource {
	filtered := make([]Resource, 0)
	for _, resource := range resources {
		if SliceContains(resourceTypes, resource.Type) && categoryMatches(resourceCategory, resource.Category) {
			filtered = append(filtered, resource)
		}
	}
	return filtered
}

func categoryMatches(c1, c2 string) bool {
	if c1 == "" || c1 == "*" {
		return true
	}
	return c1 == c2
}

// JSONStringify converts a JSON object into an indented string suitable for printing
func JSONStringify(data interface{}) (string, error) {
	b, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func currentTime() string {
	return time.Now().UTC().Format(time.RFC3339)
}

func SliceContains(list []string, value string) bool {
	for _, item := range list {
		if item == value {
			return true
		}
	}
	return false
}

// Exclude resources
func ExcludeResourceTypes(resources []Resource, resourceTypes []string, resourceCategory string) []Resource {
	filtered := make([]Resource, 0)
	for _, resource := range resources {
		if !SliceContains(resourceTypes, resource.Type) && categoryMatches(resourceCategory, resource.Category) {
			filtered = append(filtered, resource)
		}
	}
	return filtered
}

// FilterResourcesForRule returns resources applicable to the given rule
func FilterResourcesForRule(resources []Resource, rule Rule) []Resource {
	if len(rule.Resources) > 0 {
		Debugf("filtering rule resources on Resources slice")
		return FilterResourcesByTypes(resources, rule.Resources, rule.Category)
	}
	if rule.Resource != "" && rule.Resource != "*" {
		Debugf("filtering rule resources on Resource string")
		return FilterResourcesByType(resources, rule.Resource, rule.Category)
	}
	if len(rule.ExceptResources) > 0 {
		Debugf("filtering rule resources on ExceptResources slice")
		return ExcludeResourceTypes(resources, rule.ExceptResources, rule.Category)
	}
	// default is to match all resources
	return resources
}


================================================
FILE: assertion/util_test.go
================================================
package assertion

import (
	"strings"
	"testing"
)

func TestUnquotedWithoutQuotes(t *testing.T) {
	if unquoted("Foo") != "Foo" {
		t.Errorf("Unquoted for not quoted string fails")
	}
}

func TestUnquotedWithQuotes(t *testing.T) {
	if unquoted("\"Foo\"") != "Foo" {
		t.Errorf("Unquoted for quoted string fails")
	}
}

func TestIsAbsentEmptyString(t *testing.T) {
	if isAbsent("") != true {
		t.Errorf("isAbsent for empty string fails")
	}
}

func TestIsAbsentEmptyArray(t *testing.T) {
	if isAbsent("[]") != true {
		t.Errorf("isAbsent for empty array fails")
	}
}

func TestIsAbsentNull(t *testing.T) {
	if isAbsent("null") != true {
		t.Errorf("isAbsent for null fails")
	}
}

func TestIsAbsentFalse(t *testing.T) {
	if isAbsent("something") != false {
		t.Errorf("isAbsent for value fails")
	}
}

func TestIntersectTrue(t *testing.T) {
	a := []string{"foo", "bar"}
	b := []string{"bar", "baz"}
	if listsIntersect(a, b) != true {
		t.Errorf("listsIntersect should return true fails")
	}
}

func TestIntersectFalse(t *testing.T) {
	a := []string{"foo", "bar"}
	b := []string{"baz"}
	if listsIntersect(a, b) != false {
		t.Errorf("listsIntersect should return false fails")
	}
}

func TestJSONListsIntersectTrue(t *testing.T) {
	s1 := "[ \"foo\", \"bar\" ]"
	s2 := "[ \"baz\", \"bar\" ]"
	if jsonListsIntersect(s1, s2) != true {
		t.Errorf("JSONIntersect should return true")
	}
}

func TestShouldIncludeFile(t *testing.T) {
	patterns := []string{"*.tf", "*.yml"}
	include, err := ShouldIncludeFile(patterns, "instance.tf")
	if err != nil {
		t.Errorf("ShouldIncludeFile generated an unexpected error: %v", err)
	}
	if !include {
		t.Errorf("ShouldIncludeFile failed to include file with matching pattern")
	}
}

func TestShouldNotIncludeFile(t *testing.T) {
	patterns := []string{"*.tf", "*.yml"}
	include, err := ShouldIncludeFile(patterns, "instance.config")
	if err != nil {
		t.Errorf("ShouldIncludeFile generated an unexpected error: %v", err)
	}
	if include {
		t.Errorf("ShouldIncludeFile failed to exclude file with no matching pattern")
	}
}

func TestFilterShouldIncludeResources(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance"},
		Resource{Type: "volume"},
	}
	filtered := FilterResourcesByType(resources, "instance", "*")
	if len(filtered) != 1 {
		t.Errorf("FilterResourcesByType expected to match one resource")
	}
}

func TestFilterShouldExcludeResources(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance"},
		Resource{Type: "volume"},
	}
	filtered := FilterResourcesByType(resources, "database", "*")
	if len(filtered) != 0 {
		t.Errorf("FilterResourcesByType expected to match no resources")
	}
}

func TestFilterShouldIncludeAllResources(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance"},
		Resource{Type: "volume"},
	}
	filtered := FilterResourcesByType(resources, "*", "*")
	if len(filtered) != len(resources) {
		t.Errorf("FilterResourcesByType expected to include all resources")
	}
}

func TestFilterShouldMatchCategoryForResources(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance", Category: "resource"},
		Resource{Type: "template_file", Category: "data"},
	}
	filtered := FilterResourcesByType(resources, "template_file", "data")
	if len(filtered) != 1 {
		t.Errorf("FilterResourcesByType expected to match one resource")
	}
}

func TestSliceContainsTrue(t *testing.T) {
	test := []string{"x", "y", "z"}
	isPresent := SliceContains(test, "x")
	if isPresent != true {
		t.Errorf("SliceContains expected to return true when a value is present")
	}
}

func TestSliceContainsFalse(t *testing.T) {
	test := []string{"x", "y", "z"}
	isPresent := SliceContains(test, "a")
	if isPresent != false {
		t.Errorf("SliceContains expected to return false when a value is not present")
	}
}

func TestFilterPluralShouldMatchMultipleResources(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance", Category: "resource"},
		Resource{Type: "bucket", Category: "resource"},
	}
	filtered := FilterResourcesByTypes(resources, []string{"instance", "bucket"}, "resource")
	if len(filtered) != 2 {
		t.Errorf("FilterResourcesByTypes expected to match multiple types")
	}
}

func TestFilterPluralShouldNotHaveUnlistedResources(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance", Category: "resource"},
		Resource{Type: "bucket", Category: "resource"},
	}
	resourceTypes := []string{"instance"}
	filtered := FilterResourcesByTypes(resources, resourceTypes, "resource")
	if len(filtered) != 1 {
		t.Errorf("FilterResourcesByTypes expected to match only %s", strings.Join(resourceTypes, ", "))
	}
}

func TestFilterResourcesForRuleSlice(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance", Category: "resource"},
		Resource{Type: "bucket", Category: "resource"},
	}
	rule := Rule{
		Resources: []string{
			"instance",
			"bucket",
		},
	}
	filtered := FilterResourcesForRule(resources, rule)
	if len(filtered) != 2 {
		t.Errorf("FilterResourcesForRule expected to return both resource types")
	}
}

func TestFilterResourcesForRuleString(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance", Category: "resource"},
		Resource{Type: "bucket", Category: "resource"},
	}
	rule := Rule{
		Resource: "instance",
	}
	filtered := FilterResourcesForRule(resources, rule)
	if len(filtered) != 1 {
		t.Errorf("FilterResourcesForRule only expected to return one type")
	}
}

func TestFilterResourcesForWildcard(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance", Category: "resource"},
		Resource{Type: "bucket", Category: "resource"},
	}
	rule := Rule{
		Resource: "*",
	}
	filtered := FilterResourcesForRule(resources, rule)
	if len(filtered) != 2 {
		t.Errorf("FilterResourcesForRule expected all resources to match")
	}
}

func TestFilterResourcesForDefault(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance", Category: "resource"},
		Resource{Type: "bucket", Category: "resource"},
	}
	rule := Rule{}
	filtered := FilterResourcesForRule(resources, rule)
	if len(filtered) != 2 {
		t.Errorf("FilterResourcesForRule expected all resources to match")
	}
}

func TestFilterExcludeResourcesForRuleString(t *testing.T) {
	resources := []Resource{
		Resource{Type: "instance", Category: "resource"},
		Resource{Type: "bucket", Category: "resource"},
	}
	rule := Rule{
		ExceptResources: []string{
			"instance",
			"security_group",
		},
	}
	filtered := FilterResourcesForRule(resources, rule)
	if len(filtered) != 1 {
		t.Errorf("FilterResourcesForRule expected to return one type")
	}
	if len(filtered) > 0 && filtered[0].Type != "bucket" {
		t.Errorf("FilterResourcesForRule expected to return bucket")
	}
}


================================================
FILE: assertion/value.go
================================================
package assertion

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// StandardValueSource can fetch values from external sources
type StandardValueSource struct {
	Variables map[string]string
}

// GetValue looks up external values when an Expression includes a ValueFrom attribute
func (v StandardValueSource) GetValue(expression Expression) (string, error) {
	if expression.ValueFrom.URL != "" {
		Debugf("Getting value_from %s\n", expression.ValueFrom.URL)
		parsedURL, err := url.Parse(expression.ValueFrom.URL)
		if err != nil {
			return "", err
		}
		switch strings.ToLower(parsedURL.Scheme) {
		case "s3":
			return v.GetValueFromS3(parsedURL.Host, parsedURL.Path)
		case "http":
			return v.GetValueFromHTTP(expression.ValueFrom.URL)
		case "https":
			return v.GetValueFromHTTP(expression.ValueFrom.URL)
		default:
			return "", fmt.Errorf("Unsupported protocol for value_from: %s", parsedURL.Scheme)
		}
	}
	if expression.ValueFrom.Variable != "" {
		if value, ok := v.Variables[expression.ValueFrom.Variable]; ok {
			Debugf("Getting value_from variable %s: %s\n", expression.ValueFrom.Variable, value)
			return value, nil
		}
		Debugf("Getting value_from variable %s not found\n", expression.ValueFrom.Variable)
		return expression.ValueFrom.Variable, nil // or should this throw an error?
	}
	return expression.Value, nil
}

// GetValueFromS3 looks up external values for an Expression when the S3 protocol is specified
func (v StandardValueSource) GetValueFromS3(bucket string, key string) (string, error) {
	region, err := getBucketRegion(bucket)
	if err != nil {
		message := fmt.Sprintf("Cannot get region for bucket %s: %s", bucket, err.Error())
		return "", errors.New(message)
	}

	config := &aws.Config{Region: aws.String(region)}
	awsSession := session.New()
	s3Client := s3.New(awsSession, config)
	response, err := s3Client.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
	})
	if err != nil {
		message := fmt.Sprintf("Cannot read bucket %s key %s: %s", bucket, key, err.Error())
		return "", errors.New(message)
	}
	buf := new(bytes.Buffer)
	buf.ReadFrom(response.Body)
	value := strings.TrimSpace(buf.String())
	Debugf("Value from bucket %s key %s in region %s: %s\n", bucket, key, region, value)
	return value, nil
}

func getBucketRegion(bucket string) (string, error) {
	awsSession := session.New()
	s3Client := s3.New(awsSession)
	location, err := s3Client.GetBucketLocation(&s3.GetBucketLocationInput{
		Bucket: aws.String(bucket),
	})
	if err != nil {
		return "us-east-1", err
	}
	if location.LocationConstraint == nil {
		// default region is us-east-1
		return "us-east-1", nil
	}
	return *location.LocationConstraint, nil
}

// GetValueFromHTTP looks up external value for an Expression when the HTTP protocol is specified
func (v StandardValueSource) GetValueFromHTTP(url string) (string, error) {
	httpResponse, err := http.Get(url)
	if err != nil {
		return "", err
	}
	if httpResponse.StatusCode != 200 {
		return "", err
	}
	defer httpResponse.Body.Close()
	body, err := ioutil.ReadAll(httpResponse.Body)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(body)), nil
}


================================================
FILE: assertion/value_test.go
================================================
package assertion

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestCommandLineVariable(t *testing.T) {
	s := StandardValueSource{
		Variables: map[string]string{"foo": "bar"},
	}
	e := Expression{
		ValueFrom: ValueFrom{Variable: "foo"},
	}
	v, err := s.GetValue(e)
	if err != nil {
		t.Errorf("Expected GetValue to return without error: %v\n", err.Error())
	}
	if v != "bar" {
		t.Errorf("Expected GetValue to find variable 'foo' with value 'bar', not '%s'\n", v)
	}
}

func TestValueFromHttp(t *testing.T) {
	cidrBlock := "0.0.0.0/0"
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, cidrBlock)
	}))
	defer ts.Close()
	s := StandardValueSource{}
	e := Expression{
		ValueFrom: ValueFrom{URL: ts.URL},
	}
	v, err := s.GetValue(e)
	assert.Nil(t, err, "Expecting GetValue to not return an error")
	assert.Equal(t, cidrBlock, v, "Expecting CIDR value to be returned")
}


================================================
FILE: cli/app.go
================================================
package main

//go:generate packr -v

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/gobuffalo/packr"
	"github.com/stelligent/config-lint/assertion"
	"github.com/stelligent/config-lint/linter"
)

var version string

type (
	// LinterOptions for applying rules
	LinterOptions struct {
		Tags             []string
		RuleIDs          []string
		IgnoreRuleIDs    []string
		QueryExpression  string
		SearchExpression string
		ExcludePatterns  []string
		Variables        map[string]string
		TerraformParser  string
	}

	// ProfileOptions for default options from a project file
	ProfileOptions struct {
		Rules                []string
		IDs                  []string
		IgnoreIDs            []string `json:"ignore_ids"`
		Tags                 []string
		Query                string
		Files                []string
		Terraform            bool
		Exceptions           []RuleException
		Variables            map[string]string
		ExcludePatterns      []string `json:"exclude"`
		ExcludeFromFilenames []string `json:"exclude_from"`
	}

	// RuleException optional list allowing a project to ignore specific rules for specific resources
	RuleException struct {
		RuleID           string
		ResourceCategory string
		ResourceType     string
		ResourceID       string
		Comments         string
	}

	// CommandLineOptions for collecting options from the command line
	CommandLineOptions struct {
		RulesFilenames        arrayFlags
		ExcludePatterns       arrayFlags
		ExcludeFromFilenames  arrayFlags
		Variables             arrayFlags
		TerraformParser       *string
		ProfileFilename       *string
		TerraformBuiltInRules *bool
		Tags                  *string
		Ids                   *string
		IgnoreIds             *string
		QueryExpression       *string
		VerboseReport         *bool
		SearchExpression      *string
		Validate              *bool
		Version               *bool
		Debug                 *bool
		Args                  []string
	}

	// ReportWriter formats and displays a ValidationReport
	ReportWriter interface {
		WriteReport(assertion.ValidationReport, LinterOptions)
	}

	// DefaultReportWriter writes the report to Stdout
	DefaultReportWriter struct {
		Writer io.Writer
	}
)

func main() {

	commandLineOptions := getCommandLineOptions()

	if *commandLineOptions.Version == true {
		fmt.Println(version)
		return
	}

	if *commandLineOptions.Debug == true {
		assertion.SetDebug(true)
	}

	if *commandLineOptions.Validate {
		exitCode, err := validateRules(commandLineOptions.Args, DefaultReportWriter{Writer: os.Stdout})
		if err != nil {
			fmt.Println(err.Error())
		}
		os.Exit(exitCode)
	}

	profileOptions, err := loadProfile(*commandLineOptions.ProfileFilename)
	if err != nil {
		fmt.Printf("Error loading profile: %v\n", err)
		os.Exit(-1)
	}

	rulesFilenames := loadFilenames(commandLineOptions.RulesFilenames, profileOptions.Rules)
	configFilenames := defaultToCurrentDirectory(loadFilenames(commandLineOptions.Args, profileOptions.Files))
	useTerraformBuiltInRules := *commandLineOptions.TerraformBuiltInRules || profileOptions.Terraform

	if err != nil {
		fmt.Printf("Unable to load exclude patterns: %s\n", err)
		os.Exit(-1)
	}

	linterOptions, err := getLinterOptions(commandLineOptions, profileOptions)
	if err != nil {
		fmt.Printf("Failed to parse options: %v\n", err)
		os.Exit(-1)
	}

	ruleSets, err := loadRuleSets(rulesFilenames)
	if err != nil {
		fmt.Printf("Failed to load rules: %v\n", err)
		os.Exit(-1)
	}
	// Same rule set applies to both TerraformBuiltInRules and Terraform11BuiltInRules
	// loadBuiltInRuleSet can be called recursively against a directory, as done here,
	// or can be called against a single file, as done with lint-rule.yml
	if useTerraformBuiltInRules {
		builtInRuleSet, err := loadBuiltInRuleSet("terraform/")
		if err != nil {
			fmt.Printf("Failed to load built-in rules for Terraform: %v\n", err)
			os.Exit(-1)
		}
		ruleSets = append(ruleSets, builtInRuleSet)
	}
	if len(ruleSets) == 0 {
		fmt.Println("No rules")
		os.Exit(-1)
	}

	ruleSets = addExceptions(ruleSets, profileOptions.Exceptions)

	os.Exit(applyRules(ruleSets, configFilenames, linterOptions, DefaultReportWriter{Writer: os.Stdout}))
}

func addExceptions(ruleSets []assertion.RuleSet, exceptions []RuleException) []assertion.RuleSet {
	sets := []assertion.RuleSet{}
	for _, ruleSet := range ruleSets {
		sets = append(sets, addExceptionsToRuleSet(ruleSet, exceptions))
	}
	return sets
}

func addExceptionsToRuleSet(ruleSet assertion.RuleSet, exceptions []RuleException) assertion.RuleSet {
	rules := []assertion.Rule{}
	for _, rule := range ruleSet.Rules {
		for _, e := range exceptions {
			if rule.ID == e.RuleID && resourceMatch(rule, e) && categoryMatch(rule, e) {
				rule.Except = append(rule.Except, e.ResourceID)
			}
		}
		rules = append(rules, rule)
	}
	ruleSet.Rules = rules
	return ruleSet
}

func resourceMatch(rule assertion.Rule, exception RuleException) bool {
	if assertion.SliceContains(rule.Resources, exception.ResourceType) || rule.Resource == exception.ResourceType {
		return true
	}
	return false
}

func categoryMatch(rule assertion.Rule, exception RuleException) bool {
	return rule.Category == exception.ResourceCategory || exception.ResourceCategory == "resources" || rule.Category == ""
}

func validateRules(filenames []string, w ReportWriter) (int, error) {
	builtInRuleSet, err := loadBuiltInRuleSet("lint-rules.yml")
	if err != nil {
		return -1, err
	}
	ruleSets := []assertion.RuleSet{builtInRuleSet}
	linterOptions := LinterOptions{
		QueryExpression: "Violations[]",
	}
	return applyRules(ruleSets, filenames, linterOptions, w), nil
}

func loadRuleSets(args arrayFlags) ([]assertion.RuleSet, error) {
	rulesFilenames := yamlFilesOnly(getFilenames(args))
	ruleSets := []assertion.RuleSet{}
	for _, rulesFilename := range rulesFilenames {
		rulesContent, err := ioutil.ReadFile(rulesFilename)
		if err != nil {
			return ruleSets, err
		}
		ruleSet, err := assertion.ParseRules(string(rulesContent))
		if err != nil {
			return ruleSets, err
		}
		ruleSet.Source = rulesFilename
		ruleSets = append(ruleSets, ruleSet)
	}
	return ruleSets, nil
}

func isYamlFile(filename string) bool {
	configPatterns := []string{"*yml", "*.yaml"}
	match, _ := assertion.ShouldIncludeFile(configPatterns, filename)
	return match

}

func yamlFilesOnly(filenames []string) []string {
	configFiles := []string{}
	for _, filename := range filenames {
		match := isYamlFile(filename)
		if match {
			configFiles = append(configFiles, filename)
		}
	}
	return configFiles
}

// Takes a name of a rule YAML file or a directory containing YAML rules
// Returns a RuleSet of all rules in that file or directory
func loadBuiltInRuleSet(filename string) (assertion.RuleSet, error) {
	ruleSet := assertion.RuleSet{}
	box := packr.NewBox("./assets")
	assertion.Debugf("Looking for file %v in Box: %v\n", filename, box)

	var err error
	if isYamlFile(filename) && box.Has(filename) {
		ruleSet, err = addRuleSet(ruleSet, box, filename)
		if err != nil {
			assertion.Debugf("Failed to add RuleSet: %v\n", err)
			return assertion.RuleSet{}, err // returns empty rule set
		}
	} else if strings.HasSuffix(filename, "/") {
		filesInBox := box.List()
		if len(filesInBox) > 0 {
			// Get each file in that box
			for _, fileInBox := range filesInBox {
				// Check if file is YAML and starts with the folder name
				assertion.Debugf("Box File: %v\n", fileInBox)
				if isYamlFile(fileInBox) && strings.HasPrefix(fileInBox, filename) {
					assertion.Debugf("Adding rule set: %v\n", fileInBox)
					ruleSet, err = addRuleSet(ruleSet, box, fileInBox)
					if err != nil {
						assertion.Debugf("Failed to add RuleSet: %v\n", err)
						return assertion.RuleSet{}, err // returns empty rule set
					}
				}
			}
		}
	} else {
		return assertion.RuleSet{}, errors.New("File or directory doesnt exist")
	}

	return ruleSet, nil
}

func addRuleSet(ruleSet assertion.RuleSet, box packr.Box, filename string) (assertion.RuleSet, error) {
	// Get RuleSet from file
	newRuleSet, err := getRuleSet(box, filename)
	if err != nil {
		assertion.Debugf("Failed to get RuleSet: %v\n", err)
		return assertion.RuleSet{}, err // returns empty rule set
	}

	// Join with existing rule sets
	ruleSet, err = assertion.JoinRuleSets(ruleSet, newRuleSet)
	if err != nil {
		assertion.Debugf("Failed to join RuleSets: %v\n", err)
		return assertion.RuleSet{}, err // returns empty rule set
	}

	return ruleSet, nil
}

// Given a packr box and rule file in that box,
// build and return a RuleSet
func getRuleSet(box packr.Box, name string) (assertion.RuleSet, error) {
	rulesContent, err := box.FindString(name)
	if err != nil {
		assertion.Debugf("Failed to find filename string in box: %v\n", err)
		return assertion.RuleSet{}, err
	}
	ruleSet, err := assertion.ParseRules(string(rulesContent))
	if err != nil {
		assertion.Debugf("Failed to parse rules from file: %v\n", err)
		return assertion.RuleSet{}, err
	}
	return ruleSet, nil
}

func applyRules(ruleSets []assertion.RuleSet, args arrayFlags, options LinterOptions, w ReportWriter) int {

	report := assertion.ValidationReport{
		Violations:       []assertion.Violation{},
		FilesScanned:     []string{},
		ResourcesScanned: []assertion.ScannedResource{},
	}

	tfParser := options.TerraformParser
	filenames := excludeFilenames(getFilenames(args), options.ExcludePatterns)
	vs := assertion.StandardValueSource{Variables: options.Variables}

	for _, ruleSet := range ruleSets {
		l, err := linter.NewLinter(ruleSet, vs, filenames, tfParser)
		if err != nil {
			fmt.Println(err)
			return -1
		}
		if l != nil {
			if options.SearchExpression != "" {
				l.Search(ruleSet, options.SearchExpression, os.Stdout)
			} else {
				options := linter.Options{
					Tags:          options.Tags,
					RuleIDs:       options.RuleIDs,
					IgnoreRuleIDs: options.IgnoreRuleIDs,
				}
				r, err := l.Validate(ruleSet, options)
				if err != nil {
					fmt.Println("Validate failed:", err)
				}
				report = linter.CombineValidationReports(report, r)
			}
		}
	}
	w.WriteReport(report, options)
	return generateExitCode(report)
}

func printReport(w io.Writer, report assertion.ValidationReport, queryExpression string) error {
	jsonData, err := json.MarshalIndent(report, "", "  ")
	if err != nil {
		return err
	}
	if queryExpression != "" {
		var data interface{}
		err = yaml.Unmarshal(jsonData, &data)
		if err != nil {
			return err
		}
		v, err := assertion.SearchData(queryExpression, data)
		if err != nil {
			return err
		}
		s, err := assertion.JSONStringify(v)
		if err == nil && s != "null" {
			fmt.Fprintln(w, s)
		}
	} else {
		fmt.Fprintln(w, string(jsonData))
	}
	return nil
}

type arrayFlags []string

func (i *arrayFlags) String() string {
	if i != nil {
		return strings.Join(*i, ",")
	}
	return ""
}

func (i *arrayFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}

func generateExitCode(report assertion.ValidationReport) int {
	for _, v := range report.Violations {
		if v.Status == "FAILURE" {
			return -1
		}
	}
	return 0
}

func loadFilenames(commandLineFilenames []string, profileFilenames []string) []string {
	if len(commandLineFilenames) > 0 {
		return commandLineFilenames
	}
	if len(profileFilenames) > 0 {
		return profileFilenames
	}
	return []string{}
}

func defaultToCurrentDirectory(filenames []string) []string {
	if len(filenames) == 0 {
		return []string{"."}
	}
	return filenames
}

func excludeFilenames(filenames []string, excludePatterns []string) []string {
	assertion.Debugf("Exclude patterns: %v\n", excludePatterns)
	filteredFilenames := []string{}
	for _, filename := range filenames {
		if !excludeFilename(filename, excludePatterns) {
			filteredFilenames = append(filteredFilenames, filename)
		}
	}
	return filteredFilenames
}

func excludeFilename(filename string, excludePatterns []string) bool {
	for _, pattern := range excludePatterns {
		match, _ := filepath.Match(pattern, filename)
		if match {
			assertion.Debugf("Excluding file: %s using pattern: %s\n", filename, pattern)
			return true
		}
	}
	return false
}

func getFilenames(args []string) []string {
	filenames := []string{}
	for _, arg := range args {
		if arg == "-" {
			filenames = append(filenames, arg)
			continue
		}
		fi, err := os.Stat(arg)
		if err != nil {
			// append as is, error reported later when file cannot be opened
			filenames = append(filenames, arg)
			continue
		}
		mode := fi.Mode()
		if mode.IsDir() {
			filenames = append(filenames, getFilesInDirectory(arg)...)
		} else {
			filenames = append(filenames, arg)
		}
	}
	return filenames
}

func getFilesInDirectory(root string) []string {
	directoryFiles := []string{}
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			fmt.Printf("Error processing %s: %s\n", path, err)
			return err
		}
		if !info.IsDir() {
			directoryFiles = append(directoryFiles, path)
		}
		return nil
	})
	if err != nil {
		fmt.Printf("Error walking directory %s: %s\n", root, err)
	}
	return directoryFiles
}


================================================
FILE: cli/app_test.go
================================================
package main

import (
	"bytes"
	"testing"

	"github.com/gobuffalo/packr"
	"github.com/stelligent/config-lint/assertion"
	"github.com/stelligent/config-lint/linter"
	"github.com/stretchr/testify/assert"
)

func TestLoadTerraformRules(t *testing.T) {
	_, err := loadBuiltInRuleSet("terraform/")
	if err != nil {
		t.Errorf("Cannot load built-in Terraform rules")
	}
}

func TestLoadValidateRules(t *testing.T) {
	_, err := loadBuiltInRuleSet("lint-rules.yml")
	if err != nil {
		t.Errorf("Cannot load built-in rules for -validate option")
	}
}

func TestExcludeAll(t *testing.T) {
	filenames := []string{"file1.tf", "file2.tf", "file3.tf"}
	patterns := []string{"*.tf"}
	filtered := excludeFilenames(filenames, patterns)
	if len(filtered) != 0 {
		t.Errorf("Expecting all files to be excluded, but files are %v", filtered)
	}
}

func TestExcludeSubdirectory(t *testing.T) {
	filenames := []string{"file1.tf", "foo/bar/secrets/database.yml"}
	patterns := []string{"foo/bar/secrets/*"}
	filtered := excludeFilenames(filenames, patterns)
	if len(filtered) != 1 {
		t.Errorf("Expecting secrets subdirectory to be excluded, but files are %v", filtered)
	}
}

func TestExcludeOnePattern(t *testing.T) {
	filenames := []string{"file1.tf", "file2.tf", "file3.tf"}
	patterns := []string{"*1.tf"}
	filtered := excludeFilenames(filenames, patterns)
	if len(filtered) != 2 {
		t.Errorf("Expecting one file to be excluded, but files are %v", filtered)
	}
}

func TestExcludeMultiplePattern(t *testing.T) {
	filenames := []string{"file1.tf", "file2.tf", "file3.tf"}
	patterns := []string{"*1.tf", "*2.tf"}
	filtered := excludeFilenames(filenames, patterns)
	if len(filtered) != 1 {
		t.Errorf("Expecting two files to be excluded, but files are %v", filtered)
	}
}

func TestExcludeFrom(t *testing.T) {
	excludeFromFilenames := []string{"./testdata/exclude-list"}
	patterns, err := loadExcludePatterns([]string{}, excludeFromFilenames)
	if err != nil {
		t.Errorf("Expecting loadExcludePatterns returned error: %s", err.Error())
	}
	if len(patterns) != 2 {
		t.Errorf("Expecting to load 2 patterns from excludeFromFilenames, not %v", patterns)
	}
	if patterns[0] != "*1.tf" {
		t.Errorf("Expecting first pattern from file to be '*1.tf', not '%s'", patterns[0])
	}
	if patterns[1] != "*2.tf" {
		t.Errorf("Expecting second pattern from file to be '*2.tf', not '%s'", patterns[1])
	}
}

func TestProfileExceptions(t *testing.T) {
	filenames := []string{"./testdata/terraform.yml"}
	ruleSets, err := loadRuleSets(filenames)
	if err != nil {
		t.Errorf("Expecting loadRuleSets to not return error: %s", err.Error())
	}
	profileExceptions := []RuleException{
		{
			RuleID:           "RULE_1",
			ResourceCategory: "resource",
			ResourceType:     "aws_instance",
			Comments:         "Testing",
			ResourceID:       "my-special-resource",
		},
	}
	ruleSets = addExceptions(ruleSets, profileExceptions)
	ruleExceptions := ruleSets[0].Rules[0].Except
	if len(ruleExceptions) != 1 {
		t.Errorf("Expecting Rule.Except to have one ID: %v", ruleExceptions)
		return
	}
	id := ruleExceptions[0]
	if id != "my-special-resource" {
		t.Errorf("Unexpected ResourceID found in Except: %s", id)
	}
}

func TestBuiltRules(t *testing.T) {
	ruleSet, err := loadBuiltInRuleSet("lint-rules.yml")
	if err != nil {
		t.Errorf("Expecting loadBuiltInRuleSet to not return error: %s", err.Error())
	}
	vs := assertion.StandardValueSource{}

	// Get all rule files from the assets box
	box := packr.NewBox("./assets")
	allFilenames := box.List()
	var filenames []string
	for _, filename := range allFilenames {
		if isYamlFile(filename) && !isTestCase(filename) {
			filenames = append(filenames, "assets/"+filename)
		}
	}
	l, err := linter.NewLinter(ruleSet, vs, filenames, "")
	if err != nil {
		t.Errorf("Expecting NewLinter to not return error: %s", err.Error())
	}
	options := linter.Options{}
	report, err := l.Validate(ruleSet, options)
	if err != nil {
		t.Errorf("Expecting Validate to not return error: %s", err.Error())
	}
	if len(report.Violations) != 0 {
		t.Errorf("Expecting Validate for built in rules to not report any violations: %v", report.Violations)
	}
}

func TestPrintReport(t *testing.T) {
	r := assertion.ValidationReport{}
	var b bytes.Buffer
	err := printReport(&b, r, "")
	assert.Nil(t, err, "Expecting printReport to run without error")
	assert.Contains(t, b.String(), "FilesScanned\": null")
	assert.Contains(t, b.String(), "ResourcesScanned\": null")
	assert.Contains(t, b.String(), "Violations\": null")
}

func TestPrintReportWithQueryString(t *testing.T) {
	r := assertion.ValidationReport{
		Violations: []assertion.Violation{
			assertion.Violation{RuleMessage: "Houston, we have a problem"},
		},
	}
	var b bytes.Buffer
	err := printReport(&b, r, "Violations[]")
	assert.Nil(t, err, "Expecting printReport to run without error")
	assert.Contains(t, b.String(), "RuleMessage")
	assert.NotContains(t, b.String(), "Violations")
	assert.NotContains(t, b.String(), "FilesScanned")
	assert.NotContains(t, b.String(), "ResourcesScanned")
}

type MockReportWriter struct {
	Report assertion.ValidationReport
}

func (w MockReportWriter) WriteReport(r assertion.ValidationReport, o LinterOptions) {
	w.Report = r
}

func TestApplyRules(t *testing.T) {
	ruleSets := []assertion.RuleSet{
		assertion.RuleSet{
			Type: "JSON",
		},
	}
	args := arrayFlags{}
	options := LinterOptions{}
	w := MockReportWriter{}
	exitCode := applyRules(ruleSets, args, options, w)
	assert.Equal(t, exitCode, 0, "Expecting applyRules to return 0")
	assert.Empty(t, w.Report.Violations, "Expecting empty report")
}

func TestValidateRules(t *testing.T) {
	filenames := []string{"./testdata/has-properties.yml"}
	w := MockReportWriter{}
	validateRules(filenames, w)
	assert.Empty(t, w.Report.Violations, "Expecting empty report for validateRules")
}

func TestResourceMatch(t *testing.T) {
	testRule := []assertion.Rule{
		{
			ID:        "RULE_1",
			Category:  "resource",
			Resources: []string{"aws_instance", "aws_s3_bucket"},
		},
		{
			ID:       "RULE_2",
			Category: "resource",
			Resource: "aws_s3_bucket",
		},
	}
	profileExceptions := []RuleException{
		{
			RuleID:           "RULE_1",
			ResourceCategory: "resource",
			ResourceType:     "aws_instance",
			Comments:         "Testing",
			ResourceID:       "my-special-resource",
		},
		{
			RuleID:           "RULE_2",
			ResourceCategory: "resources",
			ResourceType:     "aws_s3_bucket",
			Comments:         "Testing",
			ResourceID:       "my-special-bucket",
		},
		{
			RuleID:           "RULE_2",
			ResourceCategory: "resources",
			ResourceType:     "aws_vpc",
			Comments:         "Should not match",
			ResourceID:       "my-vpc",
		},
	}

	assert.True(t, resourceMatch(testRule[0], profileExceptions[0]), "Expecting exception resource to be found in rule resources")
	assert.True(t, resourceMatch(testRule[1], profileExceptions[1]), "Expecting one to one match with exception resource and rule resource")
	assert.False(t, resourceMatch(testRule[1], profileExceptions[2]), "Expecting rule and exception to not match")
}

func TestLoadRuleSetsBadFilename(t *testing.T) {
	args := []string{"no-such-file.yml"}
	_, err := loadRuleSets(args)
	assert.NotNil(t, err, "LoadRuleSet with bad filename should return an error")
}

func TestLoadRuleSetsParseErrors(t *testing.T) {
	args := []string{"./testdata/syntax-errors.yml"}
	_, err := loadRuleSets(args)
	assert.NotNil(t, err, "Expecting rules file with syntax errors to fail")
	if err != nil {
		assert.Contains(t, err.Error(), "error unmarshaling JSON")
	}
}

func TestStdinFilename(t *testing.T) {
	filenames := getFilenames([]string{"-"})
	assert.Len(t, filenames, 1, "getFilenames should file 1 file")
	assert.Equal(t, filenames[0], "-", "getFilenames should allow - for stdin")
}

func TestGetFilenamesUsingDirectory(t *testing.T) {
	filenames := getFilenames([]string{"./testdata/dirtest"})
	assert.Len(t, filenames, 2)
	assert.Equal(t, "testdata/dirtest/a.yml", filenames[0])
	assert.Equal(t, "testdata/dirtest/b.yml", filenames[1])
}

func TestLoadFilenamesFromCommandLine(t *testing.T) {
	commandLineFilenames := []string{"command.yml"}
	profileFilenames := []string{"default.yml"}
	result := loadFilenames(commandLineFilenames, profileFilenames)
	assert.Equal(t, result, commandLineFilenames)
}

func TestLoadFilenamesFromProfile(t *testing.T) {
	commandLineFilenames := []string{}
	profileFilenames := []string{"default.yml"}
	result := loadFilenames(commandLineFilenames, profileFilenames)
	assert.Equal(t, result, profileFilenames)
}

func TestArrayFlags(t *testing.T) {
	var f arrayFlags
	assert.Equal(t, "", f.String(), "Default arrayFlags should return empty string")
	f.Set("first")
	f.Set("second")
	assert.Equal(t, arrayFlags{"first", "second"}, f, "Expecting arrayFlags to have two elements")
}

func TestLoadBuiltInRuleSetMissing(t *testing.T) {
	_, err := loadBuiltInRuleSet("missing.yml")
	assert.Contains(t, err.Error(), "File or directory doesnt exist", "loadBuiltInRuleSet should fail for missing file")
}


================================================
FILE: cli/assets/lint-rules.yml
================================================
---
version: 1
description: Rules for config-lint
type: LintRules
files:
  - "*.yml"
rules:

  - id: VALID_TYPE
    message: Not a valid linter type
    resource: LintRuleSet
    severity: FAILURE
    assertions:
      - key: type
        op: in
        value: Terraform,Terraform12,Kubernetes,LintRules,YAML,JSON,CSV

  - id: VALID_VERSION
    message: RuleSet must have a supported version
    resource: LintRuleSet
    severity: WARNING
    assertions:
      - key: version
        op: eq
        value: 1

  - id: HAS_RULES
    message: RuleSet needs at least one rule
    resource: LintRuleSet
    severity: WARNING
    assertions:
      - key: rules
        op: not-empty

  - id: YAML_RULES_HAVE_RESOURCES_SECTION
    message: RuleSet for YAML required resources section
    resource: LintRuleSet
    severity: FAILURE
    conditions:
      - key: type
        op: eq
        value: YAML
    assertions:
      - key: resources
        op: present

  - id: EVERY_RULE_HAS_ID
    message: Event rule in rule set must have an id
    resource: LintRuleSet
    severity: FAILURE
    assertions:
      - every:
          key: rules
          expressions:
            - key: id
              op: present

  - id: ID_PRESENT
    message: Rule must have an ID
    resource: LintRule
    severity: FAILURE
    assertions:
      - key: id
        op: present

  - id: RESOURCE_PRESENT
    message: Rule must have a resource, resources or except_resources attribute
    severity: FAILURE
    resource: LintRule
    assertions:
      - or:
        - key: resource
          op: present
        - key: resources
          op: present
        - key: except_resources
          op: present
    tags:
      - resource

  - id: ASSERTIONS_OR_INVOKE
    message: Rule must have assertions or invoke
    resource: LintRule
    severity: FAILURE
    assertions:
      - or:
          - key: assertions
            op: present
          - key: invoke
            op: present

  - id: VALID_EXPRESSION
    message: "These are mutually exclusive in the same expression: key,or,xor,and,not,every,some,none"
    resource: LintRule
    severity: FAILURE
    assertions:
      - every:
          key: "assertions[]"
          expressions:
            - xor:
              - key: "@"
                op: has-properties
                value: key,op
              - key: "@"
                op: has-properties
                value: or
              - key: "@"
                op: has-properties
                value: xor
              - key: "@"
                op: has-properties
                value: and
              - key: "@"
                op: has-properties
                value: not
              - key: "@"
                op: has-properties
                value: every
              - key: "@"
                op: has-properties
                value: some
              - key: "@"
                op: has-properties
                value: none
              - key: "@"
                op: has-properties
                value: exactly-one


================================================
FILE: cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: API_GW_DOMAIN_SECURITY_POLICY_TLS1_2
    message: API Gateway domain name must use TLS 1.2
    resource: aws_api_gateway_domain_name
    severity: FAILURE
    assertions:
      - key: security_policy
        op: eq
        value: "TLS_1_2"
    tags:
      - api_gateway

================================================
FILE: cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/tests/terraform12/security_policy.tf
================================================
# Test that an api_gateway_domain_name is using TLS 1.2
# https://www.terraform.io/docs/providers/aws/r/api_gateway_domain_name.html#security_policy

provider "aws" {
  region = "us-east-1"
}

# PASS: security_policy is set to TLS 1.2
resource "aws_api_gateway_domain_name" "api_gw_domain_using_tls1_2" {
  domain_name = "api.example.com"

  endpoint_configuration {
    types = ["REGIONAL"]
  }

  security_policy = "TLS_1_2"
}

# FAIL: security_policy is not defined
resource "aws_api_gateway_domain_name" "api_gw_security_policy_not_set" {
  domain_name = "api.example.com"

  endpoint_configuration {
    types = ["REGIONAL"]
  }
}

# FAIL: security_policy is set to TLS 1.0 
resource "aws_api_gateway_domain_name" "api_gw_domain_using_tls1_0" {
  domain_name = "api.example.com"

  endpoint_configuration {
    types = ["REGIONAL"]
  }

  security_policy = "TLS_1_0"
}


================================================
FILE: cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/tests/test.yml
================================================
---
version: 1
description: Terraform 11 and 12 tests
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
tests:
  -
    ruleId: API_GW_DOMAIN_SECURITY_POLICY_TLS1_2
    warnings: 0
    failures: 2
    tags:
      - "terraform12"

================================================
FILE: cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: BATCH_JOB_AWS_ENVIRONMENT_SECRETS
    message: Environment for batch jobs should not include AWS secrets
    resource: aws_batch_job_definition
    severity: FAILURE
    # This rule fails if it finds a regex match for either the Access Key ID and/or the Secret Access Key
    assertions:
      - not:
        - some:
            key: "container_properties.environment[].value"
            expressions:
              # Check if the string starts with any known 4 character ACCESS_KEY sequence
              # and is 20 capital alpha-numeric characters long in total
              - key: "@"
                op: regex
                value: "^(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}$"
        - some:
            key: "container_properties.environment[].value"
            expressions:
              - and:
                # Check if the string is exactly 40 characters long
                - key: "@"
                  op: regex
                  value: "^.{40}$"
                # Check if the string contains only alpha-numeric-slash-plus characters with at least 1 / or +
                - key: "@"
                  op: regex
                  value: "^[a-zA-Z0-9/+]+[/+]+[a-zA-Z0-9/+]+$"
    tags:
      - batch


================================================
FILE: cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/tests/terraform12/aws_secrets.tf
================================================
# Test that AWS secrets are not being used in batch environment variables
# https://www.terraform.io/docs/providers/aws/r/batch_job_definition.html#container_properties
# Reference API for container_properties spec: https://docs.aws.amazon.com/batch/latest/APIReference/API_RegisterJobDefinition.html

provider "aws" {
  region = "us-east-1"
}

# PASS: AWS secrets are not used in the env vars
resource "aws_batch_job_definition" "batch_job_without_secrets" {
  name = "tf_test_batch_job_definition"
  type = "container"

  container_properties = <<CONTAINER_PROPERTIES
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"},
        {"name": "VARNAMETWO", "value": "VARVALTWO"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
CONTAINER_PROPERTIES
}

# FAIL: AWS secret key in env vars
resource "aws_batch_job_definition" "batch_job_with_secret_key" {
  name = "tf_test_batch_job_definition"
  type = "container"

  container_properties = <<CONTAINER_PROPERTIES
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"},
        {"name": "VARNAMESECRET", "value": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
CONTAINER_PROPERTIES
}

# FAIL: AWS access key ID in env vars
resource "aws_batch_job_definition" "batch_job_with_secret_access_id" {
  name = "tf_test_batch_job_definition"
  type = "container"

  container_properties = <<CONTAINER_PROPERTIES
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"},
        {"name": "VARNAMESECRET", "value": "AKIAIOSFODNN7EXAMPLE"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
CONTAINER_PROPERTIES
}


================================================
FILE: cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/tests/test.yml
================================================
---
version: 1
description: Terraform 12 test
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
tests:
  -
    ruleId: BATCH_JOB_AWS_ENVIRONMENT_SECRETS
    warnings: 0
    failures: 2
    tags:
      - "terraform12"

================================================
FILE: cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: BATCH_DEFINITION_PRIVILEGED
    message: Batch Job Definition Container Properties should not have Privileged set to true
    resource: aws_batch_job_definition
    severity: WARNING
    assertions:
      - not:
          - key: container_properties.privileged
            op: is-true
    tags:
      - batch


================================================
FILE: cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/terraform11/container_properties_privileged.tf
================================================
# Pass
resource "aws_batch_job_definition" "container_properties_privileged_not_set" {
  name = "foo"
  type = "container"

  container_properties = <<EOF
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
EOF
}

# Pass
resource "aws_batch_job_definition" "container_properties_privileged_set_to_false" {
  name = "foo"
  type = "container"

  container_properties = <<EOF
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "privileged": "false",
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
EOF
}

# Warn
resource "aws_batch_job_definition" "container_properties_privileged_set_to_true" {
  name = "foo"
  type = "container"

  container_properties = <<EOF
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "privileged": "true",
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
EOF
}


================================================
FILE: cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/terraform12/container_properties_privileged.tf
================================================
# Pass
resource "aws_batch_job_definition" "container_properties_privileged_not_set" {
  name = "foo"
  type = "container"

  container_properties = <<EOF
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
EOF
}

# Pass
resource "aws_batch_job_definition" "container_properties_privileged_set_to_false" {
  name = "foo"
  type = "container"

  container_properties = <<EOF
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "privileged": "false",
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
EOF
}

# Warn
resource "aws_batch_job_definition" "container_properties_privileged_set_to_true" {
  name = "foo"
  type = "container"

  container_properties = <<EOF
{
    "command": ["ls", "-la"],
    "image": "busybox",
    "memory": 1024,
    "vcpus": 1,
    "privileged": "true",
    "volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
    "environment": [
        {"name": "VARNAME", "value": "VARVAL"}
    ],
    "mountPoints": [
        {
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
    ],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
EOF
}


================================================
FILE: cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/test.yml
================================================
---
version: 1
description: Terraform 11 and 12 tests
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
tests:
  -
    ruleId: BATCH_DEFINITION_PRIVILEGED
    warnings: 1
    failures: 0
    tags:
      - "terraform11"
      - "terraform12"


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: CLOUDFRONT_DISTRIBUTION_ORIGIN_POLICY
    message: CloudFront Distribution should use OAI or origin_protocol_policy should be https-only
    resource: aws_cloudfront_distribution
    severity: FAILURE
    assertions:
      - or:
        - key: "origin[].s3_origin_config[].origin_access_identity"
          op: present
        - or:
          - key: "origin[].custom_origin_config"
            op: absent
          - key: "origin[].custom_origin_config[].origin_protocol_policy"
            op: contains
            value: https-only
    tags:
      - cloudfront


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/terraform11/custom_origin_config.tf
================================================
# Pass
resource "aws_cloudfront_distribution" "custom_origin_config_not_set" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Pass
resource "aws_cloudfront_distribution" "custom_origin_config_set_to_https-only" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "https-only"
      origin_ssl_protocols   = ["TLSv1.2"]
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Fail
resource "aws_cloudfront_distribution" "custom_origin_config_set_to_http-only" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "http-only"
      origin_ssl_protocols   = ["TLSv1.2"]
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Fail
resource "aws_cloudfront_distribution" "custom_origin_config_set_to_match-viewer" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "match-viewer"
      origin_ssl_protocols   = ["TLSv1.2"]
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/terraform12/custom_origin_config.tf
================================================
# Test that a cloudfront_distribution resource is using OAI or origin_protocol_policy is using https-only
# https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#origin_access_identity
# https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#origin_protocol_policy

## Setup Helper
variable "test_domain_s3_location" {
  default = "http://foo.s3-website-us-east-1.amazonaws.com"
}

variable "test_origin_id" {
  default = "fooOrigin"
}

variable "test_logging_bucket" {
  default = "foologs.s3.amazonaws.com"
}

variable "test_logging_prefix" {
  default = "aws_cloudfront_distribution"
}

# PASS: OIA is used
resource "aws_cloudfront_distribution" "custom_origin_config_not_set" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# PASS: origin_protocol_policy is https-only
resource "aws_cloudfront_distribution" "custom_origin_config_set_to_https-only" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "https-only"
      origin_ssl_protocols   = ["TLSv1.2"]
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# FAIL: origin_protocol_policy is not https-only
resource "aws_cloudfront_distribution" "custom_origin_config_set_to_http-only" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "http-only"
      origin_ssl_protocols   = ["TLSv1.2"]
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# FAIL: origin_protocol_policy is not https-only
resource "aws_cloudfront_distribution" "custom_origin_config_set_to_match-viewer" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "match-viewer"
      origin_ssl_protocols   = ["TLSv1.2"]
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/test.yml
================================================
---
version: 1
description: Terraform 11 and 12 tests
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
tests:
  -
    ruleId: CLOUDFRONT_DISTRIBUTION_ORIGIN_POLICY
    warnings: 0
    failures: 2
    tags:
      - "terraform11"
      - "terraform12"


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: CLOUDFRONT_DISTRIBUTION_LOGGING
    message: CloudFront Distribution must configure logging
    resource: aws_cloudfront_distribution
    severity: FAILURE
    assertions:
      - key: logging_config
        op: present
    tags:
      - cloudfront


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/terraform11/logging_config.tf
================================================
# Pass
resource "aws_cloudfront_distribution" "logging_enabled" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Fail
resource "aws_cloudfront_distribution" "logging_disabled" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/terraform12/logging_config.tf
================================================
## Setup Helper
variable "test_domain_s3_location" {
  default = "http://foo.s3-website-us-east-1.amazonaws.com"
}

variable "test_origin_id" {
  default = "fooOrigin"
}

variable "test_logging_bucket" {
  default = "foologs.s3.amazonaws.com"
}

variable "test_logging_prefix" {
  default = "aws_cloudfront_distribution"
}

# Pass
resource "aws_cloudfront_distribution" "logging_enabled" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Fail
resource "aws_cloudfront_distribution" "logging_disabled" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/test.yml
================================================
---
version: 1
description: Terraform 11 and 12 tests
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
tests:
  -
    ruleId: CLOUDFRONT_DISTRIBUTION_LOGGING
    warnings: 0
    failures: 1
    tags:
      - "terraform11"
      - "terraform12"


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: CLOUDFRONT_MINIMUM_SSL
    message: CloudFront Distribution must use TLS 1.2
    resource: aws_cloudfront_distribution
    severity: FAILURE
    assertions:
      - key: viewer_certificate[].cloudfront_default_certificate | [0]
        op: is-false
      - key: viewer_certificate[].minimum_protocol_version | [0]
        op: starts-with
        value: "TLSv1.2"
    tags:
      - cloudfront


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/tests/terraform12/minimum_ssl_protocol.tf
================================================
# Test that a CloudFront distribution viewer_certificate is using TLS 1.2
# https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#viewer-certificate-arguments

provider "aws" {
  region = "us-east-1"
}

# PASS
resource "aws_cloudfront_distribution" "cf_using_tls_1_2" {
  origin {
    domain_name = "example.com"
    origin_id   = "s3ExampleOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  enabled             = true
  is_ipv6_enabled     = true
  comment             = "Some comment"
  default_root_object = "index.html"

  logging_config {
    include_cookies = false
    bucket          = "mylogs.s3.amazonaws.com"
    prefix          = "myprefix"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "s3ExampleOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = false
    minimum_protocol_version       = "TLSv1.2_2018"
  }
}

# FAIL: distribution has cloudfront_default_certificate enabled
resource "aws_cloudfront_distribution" "cf_using_tls_1_2_with_default_cert" {
  origin {
    domain_name = "example.com"
    origin_id   = "s3ExampleOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  enabled             = true
  is_ipv6_enabled     = true
  comment             = "Some comment"
  default_root_object = "index.html"

  logging_config {
    include_cookies = false
    bucket          = "mylogs.s3.amazonaws.com"
    prefix          = "myprefix"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "s3ExampleOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
    minimum_protocol_version       = "TLSv1.2_2018"
  }
}

# FAIL: distribution is not using TLS 1.2
resource "aws_cloudfront_distribution" "cf_using_not_using_tls_1_2" {
  origin {
    domain_name = "example.com"
    origin_id   = "s3ExampleOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  enabled             = true
  is_ipv6_enabled     = true
  comment             = "Some comment"
  default_root_object = "index.html"

  logging_config {
    include_cookies = false
    bucket          = "mylogs.s3.amazonaws.com"
    prefix          = "myprefix"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "s3ExampleOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = false
    minimum_protocol_version       = "TLSv1"
  }
}


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/tests/test.yml
================================================
---
version: 1
description: Terraform 11 and 12 tests
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
tests:
  -
    ruleId: CLOUDFRONT_MINIMUM_SSL
    warnings: 0
    failures: 2
    tags:
      - "terraform12"

================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: CLOUDFRONT_DISTRIBUTION_PROTOCOL
    message: CloudFront Distribution should not allow all protocols
    resource: aws_cloudfront_distribution
    severity: FAILURE
    assertions:
      - key: "default_cache_behavior[].viewer_protocol_policy"
        op: does-not-contain
        value: allow-all
      - key: "ordered_cache_behavior[].viewer_protocol_policy"
        op: does-not-contain
        value: allow-all
    tags:
      - cloudfront


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/terraform11/viewer_protocol_policy.tf
================================================
# Pass
resource "aws_cloudfront_distribution" "default_cache_behavior_viewer_protocol_policy_set_to_https-only" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "https-only"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Pass
resource "aws_cloudfront_distribution" "default_cache_behavior_viewer_protocol_policy_set_to_redirect-to-https" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Fail
resource "aws_cloudfront_distribution" "default_cache_behavior_viewer_protocol_policy_set_to_allow-all" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Pass
resource "aws_cloudfront_distribution" "ordered_cache_behavior_viewer_protocol_policy_set_to_https-only" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  ordered_cache_behavior {
    path_pattern     = "/foo/bar/*"
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false
      headers      = ["Origin"]

      cookies {
        forward = "none"
      }
    }

    min_ttl                = 0
    default_ttl            = 86400
    max_ttl                = 31536000
    compress               = true
    viewer_protocol_policy = "https-only"
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Pass
resource "aws_cloudfront_distribution" "ordered_cache_behavior_viewer_protocol_policy_set_to_redirect-to-https" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  ordered_cache_behavior {
    path_pattern     = "/foo/bar/*"
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false
      headers      = ["Origin"]

      cookies {
        forward = "none"
      }
    }

    min_ttl                = 0
    default_ttl            = 86400
    max_ttl                = 31536000
    compress               = true
    viewer_protocol_policy = "redirect-to-https"
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Fail
resource "aws_cloudfront_distribution" "ordered_cache_behavior_viewer_protocol_policy_set_to_allow-all" {
  enabled = true

  origin {
    domain_name = "http://foo.s3-website-us-east-1.amazonaws.com"
    origin_id   = "fooOrigin"

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = "foologs.s3.amazonaws.com"
    prefix          = "aws_cloudfront_distribution"
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  ordered_cache_behavior {
    path_pattern     = "/foo/bar/*"
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false
      headers      = ["Origin"]

      cookies {
        forward = "none"
      }
    }

    min_ttl                = 0
    default_ttl            = 86400
    max_ttl                = 31536000
    compress               = true
    viewer_protocol_policy = "allow-all"
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/terraform12/viewer_protocol_policy.tf
================================================
## Setup Helper
variable "test_domain_s3_location" {
  default = "http://foo.s3-website-us-east-1.amazonaws.com"
}

variable "test_origin_id" {
  default = "fooOrigin"
}

variable "test_logging_bucket" {
  default = "foologs.s3.amazonaws.com"
}

variable "test_logging_prefix" {
  default = "aws_cloudfront_distribution"
}

# Pass
resource "aws_cloudfront_distribution" "default_cache_behavior_viewer_protocol_policy_set_to_https-only" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "https-only"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Pass
resource "aws_cloudfront_distribution" "default_cache_behavior_viewer_protocol_policy_set_to_redirect-to-https" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Fail
resource "aws_cloudfront_distribution" "default_cache_behavior_viewer_protocol_policy_set_to_allow-all" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Pass
resource "aws_cloudfront_distribution" "ordered_cache_behavior_viewer_protocol_policy_set_to_https-only" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  ordered_cache_behavior {
    path_pattern     = "/foo/bar/*"
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false
      headers      = ["Origin"]

      cookies {
        forward = "none"
      }
    }

    min_ttl                = 0
    default_ttl            = 86400
    max_ttl                = 31536000
    compress               = true
    viewer_protocol_policy = "https-only"
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Pass
resource "aws_cloudfront_distribution" "ordered_cache_behavior_viewer_protocol_policy_set_to_redirect-to-https" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  ordered_cache_behavior {
    path_pattern     = "/foo/bar/*"
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false
      headers      = ["Origin"]

      cookies {
        forward = "none"
      }
    }

    min_ttl                = 0
    default_ttl            = 86400
    max_ttl                = 31536000
    compress               = true
    viewer_protocol_policy = "redirect-to-https"
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# Fail
resource "aws_cloudfront_distribution" "ordered_cache_behavior_viewer_protocol_policy_set_to_allow-all" {
  enabled = true

  origin {
    domain_name = var.test_domain_s3_location
    origin_id   = var.test_origin_id

    s3_origin_config {
      origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.test_logging_bucket
    prefix          = var.test_logging_prefix
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE"]
    }
  }

  ordered_cache_behavior {
    path_pattern     = "/foo/bar/*"
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "fooOrigin"

    forwarded_values {
      query_string = false
      headers      = ["Origin"]

      cookies {
        forward = "none"
      }
    }

    min_ttl                = 0
    default_ttl            = 86400
    max_ttl                = 31536000
    compress               = true
    viewer_protocol_policy = "allow-all"
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}


================================================
FILE: cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/test.yml
================================================
---
version: 1
description: Terraform 11 and 12 tests
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
tests:
  -
    ruleId: CLOUDFRONT_DISTRIBUTION_PROTOCOL
    warnings: 0
    failures: 2
    tags:
      - "terraform11"
      - "terraform12"


================================================
FILE: cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: CLOUDTRAIL_ENCRYPTION
    message: CloudTrail should specify a non-default KMS Key
    resource: aws_cloudtrail
    severity: WARNING
    assertions:
      - key: kms_key_id
        op: present
    tags:
      - cloudtrail


================================================
FILE: cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/terraform11/kms_key_id.tf
================================================
## Setup Helper
resource "aws_kms_key" "test_key" {
  enable_key_rotation = true
}

resource "aws_s3_bucket" "test_bucket" {
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        kms_master_key_id = "${aws_kms_key.test_key.arn}"
        sse_algorithm     = "aws:kms"
      }
    }
  }
}

# Pass
resource "aws_cloudtrail" "kms_key_id_is_set" {
  name           = "foo"
  s3_bucket_name = "${aws_s3_bucket.test_bucket.bucket}"
  kms_key_id     = "${aws_kms_key.test_key.arn}"
}

# Warn
resource "aws_cloudtrail" "kms_key_id_is_not_set" {
  name           = "foo"
  s3_bucket_name = "${aws_s3_bucket.test_bucket.bucket}"
}


================================================
FILE: cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/terraform12/kms_key_id.tf
================================================
## Setup Helper
variable "test_cloudtrail_name" {
  default = "foo"
}
resource "aws_kms_key" "test_key" {
  enable_key_rotation = true
}

resource "aws_s3_bucket" "test_bucket" {
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        kms_master_key_id = "${aws_kms_key.test_key.arn}"
        sse_algorithm     = "aws:kms"
      }
    }
  }
}

# Pass
resource "aws_cloudtrail" "kms_key_id_is_set" {
  name           = var.test_cloudtrail_name
  s3_bucket_name = aws_s3_bucket.test_bucket.bucket
  kms_key_id     = aws_kms_key.test_key.arn
}

# Warn
resource "aws_cloudtrail" "kms_key_id_is_not_set" {
  name           = var.test_cloudtrail_name
  s3_bucket_name = aws_s3_bucket.test_bucket.bucket
}


================================================
FILE: cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/test.yml
================================================
---
version: 1
description: Terraform 11 and 12 tests
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
tests:
  -
    ruleId: CLOUDTRAIL_ENCRYPTION
    warnings: 1
    failures: 0
    tags:
      - "terraform11"
      - "terraform12"


================================================
FILE: cli/assets/terraform/aws/cloudwatch/cloudwatch_log_destination_policy/wildcard_principal/rule.yml
================================================
---
version: 1
description: Terraform rules
type: Terraform
files:
  - "*.tf"
  - "*.tfvars"
rules:

  - id: CLOUDWATCH_WILDCARD_PRINCIPAL
    message: Cloudwatch destination policy allow policy should not use a wildcard princpal
    resource: aws_cloudwatch_log_destination_policy
    severity: FAILURE
    assertions:
      - none:
          key: access_policy.Statement
          expressions:
            - key: Effect
              op: eq
              value: Allow
            - key: Principal
              op: contains
              value: "*"
    tags:
      - cloudwatch
      - policy

================================================
FILE: cli/assets/terraform/aws/cloudwatch/cloudwatch_log_destination_policy/wildcard_principal/tests/terraform12/wildcard_principal.tf
================================================
# Test that CloudWatch log destination policy is not using a wildcard principal
# https://www.terraform.io/docs/providers/aws/r/cloudwatch_log_destination_policy.html#access_policy

provider "aws" {
  region = "us-east-1"
}

# PASS: Allow statement does not use a wildcard principal
resource "aws_cloudwatch_log_destination_policy" "cw_destination_no_wildcard" {
  destination_name = "cloudwatch_destination"
  access_policy    = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "cloudwatch:*",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::1234567890:user/foo"
                ]
            },
            "Effect": "Allow",
            "Resource": "arn:aws:logs:us-west-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*"
        }
    ]
}
EOF
}

# PASS: Deny statement does not use a wildcard principal
resource "aws_cloudwatch_log_destination_policy" "cw_destination_deny_no_wildcard" {
  destination_name = "cloudwatch_destination"
  access_policy    = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "cloudwatch:*",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::1234567890:user/foo"
                ]
            },
            "Effect": "Deny",
            "Resource": "arn:aws:logs:us-west-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*"
        }
    ]
}
EOF
}

# PASS: Deny statement uses a wildcard principal
resource "aws_cloudwatch_log_destination_policy" "cw_destination_deny_with_wildcard" {
  destination_name = "cloudwatch_destination"
  access_policy    = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "cloudwatch:*",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::1234567890:user/*"
                ]
            },
            "Effect": "Deny",
            "Resource": "arn:aws:logs:us-west-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*"
        }
    ]
}
EOF
}

# FAIL: Allow statement uses a wildcard principal
resource "aws_cloudwatch_log_destination_policy" "cw_destination_allow_with_wildcard" {
  destination_name = "cloudwatch_destination"
  access_policy    = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "cloudwatch:*",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::1234567890:user/*"
                ]
            },
       
Download .txt
gitextract_py6281yd/

├── .devcontainer/
│   ├── Dockerfile
│   ├── build/
│   │   ├── Dockerfile
│   │   └── dockerhub.sh
│   └── devcontainer.json
├── .dockerhub/
│   └── Dockerfile
├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── build_and_deploy.yml
│       ├── bump_version.yml
│       └── vscode_remote_development.yml
├── .gitignore
├── .goreleaser.yml
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── assertion/
│   ├── compare.go
│   ├── compare_test.go
│   ├── contains.go
│   ├── contains_test.go
│   ├── expression.go
│   ├── expression_test.go
│   ├── has_properties.go
│   ├── helper_test.go
│   ├── invoke.go
│   ├── invoke_test.go
│   ├── ip_operations.go
│   ├── ip_operations_test.go
│   ├── log.go
│   ├── match.go
│   ├── match_test.go
│   ├── rules.go
│   ├── rules_test.go
│   ├── search.go
│   ├── testdata/
│   │   ├── collection-assertions.yaml
│   │   ├── conditions.yaml
│   │   ├── default-severity.yaml
│   │   └── has-properties.yaml
│   ├── types.go
│   ├── util.go
│   ├── util_test.go
│   ├── value.go
│   └── value_test.go
├── cli/
│   ├── app.go
│   ├── app_test.go
│   ├── assets/
│   │   ├── lint-rules.yml
│   │   └── terraform/
│   │       └── aws/
│   │           ├── api_gateway/
│   │           │   └── api_gateway_domain_name/
│   │           │       └── security_policy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── security_policy.tf
│   │           │               └── test.yml
│   │           ├── batch/
│   │           │   └── batch_job_definition/
│   │           │       ├── aws_secrets/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── aws_secrets.tf
│   │           │       │       └── test.yml
│   │           │       └── container_properties_privileged/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── container_properties_privileged.tf
│   │           │               ├── terraform12/
│   │           │               │   └── container_properties_privileged.tf
│   │           │               └── test.yml
│   │           ├── cloudfront/
│   │           │   └── cloudfront_distribution/
│   │           │       ├── custom_origin_config/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── custom_origin_config.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── custom_origin_config.tf
│   │           │       │       └── test.yml
│   │           │       ├── logging_config/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── logging_config.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── logging_config.tf
│   │           │       │       └── test.yml
│   │           │       ├── minimum_ssl_protocol/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── minimum_ssl_protocol.tf
│   │           │       │       └── test.yml
│   │           │       └── viewer_protocol_policy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── viewer_protocol_policy.tf
│   │           │               ├── terraform12/
│   │           │               │   └── viewer_protocol_policy.tf
│   │           │               └── test.yml
│   │           ├── cloudtrail/
│   │           │   └── cloudtrail/
│   │           │       └── kms_key_id/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── kms_key_id.tf
│   │           │               ├── terraform12/
│   │           │               │   └── kms_key_id.tf
│   │           │               └── test.yml
│   │           ├── cloudwatch/
│   │           │   └── cloudwatch_log_destination_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── codebuild/
│   │           │   └── codebuild_project/
│   │           │       ├── artifact_encryption/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── artifact_encryption.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── artifact_encryption.tf
│   │           │       │       └── test.yml
│   │           │       └── project_encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── project_encryption.tf
│   │           │               ├── terraform12/
│   │           │               │   └── project_encryption.tf
│   │           │               └── test.yml
│   │           ├── codepipeline/
│   │           │   └── codepipeline/
│   │           │       └── encryption_key/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── encryption_key.tf
│   │           │               ├── terraform12/
│   │           │               │   └── encryption_key.tf
│   │           │               └── test.yml
│   │           ├── dms/
│   │           │   └── dms_endpoint/
│   │           │       └── endpoint_kms_key/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── kms_key.tf
│   │           │               ├── terraform12/
│   │           │               │   └── kms_key.tf
│   │           │               └── test.yml
│   │           ├── documentdb/
│   │           │   └── docdb_cluster/
│   │           │       ├── audit_logs/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── audit_logs.tf
│   │           │       │       └── test.yml
│   │           │       └── storage_encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── storage_encryption.tf
│   │           │               └── test.yml
│   │           ├── ec2/
│   │           │   ├── ami/
│   │           │   │   └── ebs_block_device_encrypted/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── ebs_block_device_encrypted.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── ebs_block_device_encrypted.tf
│   │           │   │           └── test.yml
│   │           │   ├── ami_copy/
│   │           │   │   └── encrypted/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── encrypted.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── encrypted.tf
│   │           │   │           └── test.yml
│   │           │   ├── ebs_volume/
│   │           │   │   └── encryption/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── encrypted.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── encrypted.tf
│   │           │   │           └── test.yml
│   │           │   └── instance/
│   │           │       └── ebs_block_device_encrypted/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── ebs_block_device_encrypted.tf
│   │           │               └── test.yml
│   │           ├── ecr/
│   │           │   └── ecr_repository_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── ecs/
│   │           │   └── ecs_task_definition/
│   │           │       └── task_definition_secrets/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── secrets.tf
│   │           │               ├── terraform12/
│   │           │               │   └── secrets.tf
│   │           │               └── test.yml
│   │           ├── efs/
│   │           │   └── efs_file_system/
│   │           │       └── encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── encrypted.tf
│   │           │               ├── terraform12/
│   │           │               │   └── encrypted.tf
│   │           │               └── test.yml
│   │           ├── elastic_load_balancing/
│   │           │   ├── alb/
│   │           │   │   └── alb_access_logs_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           └── test.yml
│   │           │   ├── alb_listener/
│   │           │   │   └── alb_listener_https/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── https.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── https.tf
│   │           │   │           └── test.yml
│   │           │   ├── elb/
│   │           │   │   └── access_logs_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           └── test.yml
│   │           │   ├── lb/
│   │           │   │   └── access_logs_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── access_logs_enabled.tf
│   │           │   │           └── test.yml
│   │           │   └── lb_listener/
│   │           │       ├── listener_https/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── https.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── https.tf
│   │           │       │       └── test.yml
│   │           │       └── listener_ssl_policy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── ssl_policy.tf
│   │           │               ├── terraform12/
│   │           │               │   └── ssl_policy.tf
│   │           │               └── test.yml
│   │           ├── elasticache/
│   │           │   └── elasticache_replication_group/
│   │           │       ├── encryption_at_rest/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── encryption_at_rest.tf
│   │           │       │       └── test.yml
│   │           │       └── encryption_in_transit/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── encryption_in_transit.tf
│   │           │               ├── terraform12/
│   │           │               │   └── encryption_in_transit.tf
│   │           │               └── test.yml
│   │           ├── elasticsearch/
│   │           │   ├── elasticsearch_domain/
│   │           │   │   ├── encryption_at_rest/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── encryption_at_rest.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── encryption_node_to_node/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── encryption_node_to_node.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── vpc_subnets/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── elasticsearch_vpc.tf
│   │           │   │           └── test.yml
│   │           │   └── shared/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   ├── elasticsearch_domain_policy_wildcard_principal.tf
│   │           │               │   └── elasticsearch_domain_wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── elastictranscoder/
│   │           │   └── elastictranscoder_pipeline/
│   │           │       └── require_encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── require_encryption.tf
│   │           │               └── test.yml
│   │           ├── emr/
│   │           │   └── emr_cluster/
│   │           │       └── logging/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── logging.tf
│   │           │               ├── terraform12/
│   │           │               │   └── logging.tf
│   │           │               └── test.yml
│   │           ├── glue/
│   │           │   └── glue_connection/
│   │           │       └── connection_properties/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── connection_properties.tf
│   │           │               └── test.yml
│   │           ├── iam/
│   │           │   ├── iam_group_membership/
│   │           │   │   └── group_and_users/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── group_and_users.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── group_and_users.tf
│   │           │   │           └── test.yml
│   │           │   ├── iam_policy/
│   │           │   │   ├── policy_action_wildcard/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_action_wildcard.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_action_wildcard.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── policy_notaction/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_notaction.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_notaction.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── policy_notresource/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_notresource.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_notresource.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── policy_resource_wildcard/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── policy_resource_wildcard.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── policy_resource_wildcard.tf
│   │           │   │           └── test.yml
│   │           │   ├── iam_role/
│   │           │   │   ├── assume_role_policy_action_wildcard/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── assume_role_policy_action_wildcard.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── assume_role_policy_action_wildcard.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── assume_role_policy_notaction/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── assume_role_policy_notaction.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── assume_role_policy_notaction.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── assume_role_policy_notprincipal/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── assume_role_policy_notprincipal.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── assume_role_policy_notprincipal.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── assume_role_policy_version/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── assume_role_policy_version.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── assume_role_policy_version.tf
│   │           │   │           └── test.yml
│   │           │   ├── iam_role_policy/
│   │           │   │   ├── role_policy_action_wildcard/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_action_wildcard.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_action_wildcard.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── role_policy_notaction/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_notaction.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_notaction.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── role_policy_notresource/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── policy_notresource.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── policy_notresource.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── role_policy_resource_wildcard/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── policy_resource_wildcard.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── policy_resource_wildcard.tf
│   │           │   │           └── test.yml
│   │           │   ├── iam_user_policy/
│   │           │   │   └── exists/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── resource_exists.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── resource_exists.tf
│   │           │   │           └── test.yml
│   │           │   └── iam_user_policy_attachment/
│   │           │       └── exists/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── resource_exists.tf
│   │           │               ├── terraform12/
│   │           │               │   └── resource_exists.tf
│   │           │               └── test.yml
│   │           ├── iot/
│   │           │   └── iot_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── kinesis/
│   │           │   └── kinesis_stream/
│   │           │       ├── kinesis_stream_encryption/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── encryption.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── encryption.tf
│   │           │       │       └── test.yml
│   │           │       └── kinesis_stream_kms_key/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── kms_key.tf
│   │           │               ├── terraform12/
│   │           │               │   └── kms_key.tf
│   │           │               └── test.yml
│   │           ├── kinesis_firehouse/
│   │           │   └── kinesis_firehose_delivery_stream/
│   │           │       └── encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── encryption.tf
│   │           │               ├── terraform12/
│   │           │               │   └── encryption.tf
│   │           │               └── test.yml
│   │           ├── kms/
│   │           │   └── kms_key/
│   │           │       ├── rotation/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── rotation.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── rotation.tf
│   │           │       │       └── test.yml
│   │           │       └── wildcard_policy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_policy.tf
│   │           │               └── test.yml
│   │           ├── lambda/
│   │           │   ├── lambda_function/
│   │           │   │   ├── encryption/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── encryption.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── encryption.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── environment_variables_aws_secrets/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── environment_variables_aws_secrets.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── environment_variables_aws_secrets.tf
│   │           │   │           └── test.yml
│   │           │   └── lambda_permission/
│   │           │       ├── action/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── action.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── action.tf
│   │           │       │       └── test.yml
│   │           │       └── principal_wildcard/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── principal_wildcard.tf
│   │           │               ├── terraform12/
│   │           │               │   └── principal_wildcard.tf
│   │           │               └── test.yml
│   │           ├── mediastore/
│   │           │   └── media_store_container_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── neptune/
│   │           │   └── neptune_cluster/
│   │           │       └── encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── encryption.tf
│   │           │               └── test.yml
│   │           ├── opsworks/
│   │           │   └── opsworks_application/
│   │           │       └── require_ssl/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── require_ssl.tf
│   │           │               └── test.yml
│   │           ├── rds/
│   │           │   ├── db_instance/
│   │           │   │   ├── encryption/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── storage_encryption.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── storage_encryption.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── publicly_accessible/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── publicly_accessible.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── publicly_accessible.tf
│   │           │   │           └── test.yml
│   │           │   └── rds_cluster/
│   │           │       └── encryption/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── storage_encryption.tf
│   │           │               └── test.yml
│   │           ├── redshift/
│   │           │   ├── redshift_cluster/
│   │           │   │   ├── encrypted/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── encrypted.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── enhanced_vpc_routing/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── enhanced_vpc_routing.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── enhanced_vpc_routing.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── kms_key_id/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── kms_key_id.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── kms_key_id.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── logging/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── logging.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── logging.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── publicly_accessible/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── publicly_accessible.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── publicly_accessible.tf
│   │           │   │           └── test.yml
│   │           │   └── redshift_parameter_group/
│   │           │       ├── require_ssl/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── require_ssl.tf
│   │           │       │       └── test.yml
│   │           │       └── user_logging/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── user_logging.tf
│   │           │               └── test.yml
│   │           ├── s3/
│   │           │   ├── s3_bucket/
│   │           │   │   ├── acl_not_public/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── acl_not_public.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── acl_not_public.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── server_side_encryption_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── server_side_encryption_enabled.tf
│   │           │   │           └── test.yml
│   │           │   ├── s3_bucket_object/
│   │           │   │   └── encryption_enabled/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── encryption_enabled.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── encryption_enabled.tf
│   │           │   │           └── test.yml
│   │           │   └── s3_bucket_policy/
│   │           │       ├── policy_statement_action_wildcard/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_action_wildcard.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_action_wildcard.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_notaction/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_notprincipal/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_principal_wildcard/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_principal_wildcard.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_principal_wildcard.tf
│   │           │       │       └── test.yml
│   │           │       └── policy_statement_secure_transport/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── policy_statement_secure_transport.tf
│   │           │               ├── terraform12/
│   │           │               │   └── policy_statement_secure_transport.tf
│   │           │               └── test.yml
│   │           ├── sagemaker/
│   │           │   ├── sagemaker_endpoint_configuration/
│   │           │   │   └── kms_key/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── kms_key.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── kms_key.tf
│   │           │   │           └── test.yml
│   │           │   └── sagemaker_notebook_instance/
│   │           │       └── kms_key/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── kms_key.tf
│   │           │               ├── terraform12/
│   │           │               │   └── kms_key.tf
│   │           │               └── test.yml
│   │           ├── ses/
│   │           │   └── ses_identity_policy/
│   │           │       └── wildcard_principal/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform12/
│   │           │               │   └── wildcard_principal.tf
│   │           │               └── test.yml
│   │           ├── shared/
│   │           │   ├── exists.todo.txt
│   │           │   ├── https.todo.txt
│   │           │   ├── kms_key.todo.txt
│   │           │   ├── policy.todo.txt
│   │           │   ├── policy_version.todo.txt
│   │           │   └── require_ssl.todo.txt
│   │           ├── sns/
│   │           │   ├── shared/
│   │           │   │   └── wildcard_principal/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform12/
│   │           │   │           │   ├── sns_topic_policy_wildcard_principal.tf
│   │           │   │           │   └── sns_topic_wildcard_principal.tf
│   │           │   │           └── test.yml
│   │           │   └── sns_topic_policy/
│   │           │       ├── topic_policy_statement_notaction/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       └── test.yml
│   │           │       ├── topic_policy_statement_notprincipal/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       └── test.yml
│   │           │       └── topic_policy_statement_principal_wildcard-copy/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── policy_statement_principal_wildcard-copy.tf
│   │           │               ├── terraform12/
│   │           │               │   └── policy_statement_principal_wildcard-copy.tf
│   │           │               └── test.yml
│   │           ├── sqs/
│   │           │   ├── shared/
│   │           │   │   └── wildcard_principal/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform12/
│   │           │   │           │   ├── sqs_queue_policy_wildcard_principal.tf
│   │           │   │           │   └── sqs_queue_wildcard_principal.tf
│   │           │   │           └── test.yml
│   │           │   ├── sqs_queue/
│   │           │   │   └── encryption/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── encryption.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── encryption.tf
│   │           │   │           └── test.yml
│   │           │   └── sqs_queue_policy/
│   │           │       ├── policy_statement_action_wildcard/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_action_wildcard.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_action_wildcard.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_notaction/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notaction.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_notprincipal/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_notprincipal.tf
│   │           │       │       └── test.yml
│   │           │       ├── policy_statement_principal_wildcard/
│   │           │       │   ├── rule.yml
│   │           │       │   └── tests/
│   │           │       │       ├── terraform11/
│   │           │       │       │   └── policy_statement_principal_wildcard.tf
│   │           │       │       ├── terraform12/
│   │           │       │       │   └── policy_statement_principal_wildcard.tf
│   │           │       │       └── test.yml
│   │           │       └── policy_version/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── policy_version.tf
│   │           │               ├── terraform12/
│   │           │               │   └── policy_version.tf
│   │           │               └── test.yml
│   │           ├── vpc/
│   │           │   ├── security_group/
│   │           │   │   ├── egress_all_protocols/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── egress_all_protocols.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── egress_all_protocols.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── egress_port_range/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── egress_port_range.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── egress_port_range.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── ingress_all_protocols/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── ingress_all_protocols.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── ingress_all_protocols.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── ingress_port_range/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── ingress_port_range.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── ingress_port_range.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── missing_egress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── missing_egress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── missing_egress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── non_32_ingress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── non_32_ingress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── non_32_ingress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── rdp_world_ingress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── rdp_world_ingress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── rdp_world_ingress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── ssh_world_ingress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── ssh_world_ingress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── ssh_world_ingress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   ├── world_egress/
│   │           │   │   │   ├── rule.yml
│   │           │   │   │   └── tests/
│   │           │   │   │       ├── terraform11/
│   │           │   │   │       │   └── world_egress.tf
│   │           │   │   │       ├── terraform12/
│   │           │   │   │       │   └── world_egress.tf
│   │           │   │   │       └── test.yml
│   │           │   │   └── world_ingress/
│   │           │   │       ├── rule.yml
│   │           │   │       └── tests/
│   │           │   │           ├── terraform11/
│   │           │   │           │   └── world_ingress.tf
│   │           │   │           ├── terraform12/
│   │           │   │           │   └── world_ingress.tf
│   │           │   │           └── test.yml
│   │           │   └── subnet/
│   │           │       └── map_public_ip_on_launch/
│   │           │           ├── rule.yml
│   │           │           └── tests/
│   │           │               ├── terraform11/
│   │           │               │   └── map_public_ip_on_launch.tf
│   │           │               ├── terraform12/
│   │           │               │   └── map_public_ip_on_launch.tf
│   │           │               └── test.yml
│   │           └── waf/
│   │               └── waf_web_acl/
│   │                   └── default_action_type/
│   │                       ├── rule.yml
│   │                       └── tests/
│   │                           ├── terraform11/
│   │                           │   └── default_action_type.tf
│   │                           ├── terraform12/
│   │                           │   └── default_action_type.tf
│   │                           └── test.yml
│   ├── builtin_test.go
│   ├── options.go
│   ├── options_test.go
│   ├── report_writer.go
│   ├── report_writer_test.go
│   ├── terraform_test.go
│   └── testdata/
│       ├── builtin/
│       │   └── terraform12/
│       │       └── test.tf
│       ├── dirtest/
│       │   ├── a.yml
│       │   └── b.yml
│       ├── exclude-list
│       ├── profile-exceptions.yml
│       ├── profile.yml
│       ├── smoketest_exceptions.tf
│       ├── smoketest_tf11.tf
│       ├── smoketest_tf12.tf
│       ├── syntax-errors.yml
│       └── terraform.yml
├── docs/
│   ├── README.md
│   ├── conditions.md
│   ├── coverpage.md
│   ├── css/
│   │   └── style.css
│   ├── design.md
│   ├── development.md
│   ├── example-rules.md
│   ├── faq.md
│   ├── github_workflow.md
│   ├── index.html
│   ├── install.md
│   ├── operations.md
│   ├── output.md
│   ├── profiles.md
│   ├── rule_development.md
│   ├── rules.md
│   ├── running.md
│   ├── sidebar.md
│   ├── terraform.md
│   ├── tests.md
│   ├── value_from.md
│   └── yaml.md
├── example-files/
│   ├── config/
│   │   ├── cloudfront.tf
│   │   ├── elb.tf
│   │   ├── generic.config
│   │   ├── iam.tf
│   │   ├── my-pod.yml
│   │   ├── network-policy-1.yml
│   │   ├── network-policy-2.yml
│   │   ├── no-containers.yml
│   │   ├── not-a-pod.yml
│   │   ├── pod-nginx.yml
│   │   ├── pod-redis.yml
│   │   ├── policy.yml
│   │   ├── provider.tf
│   │   ├── s3-bucket-policy.tf
│   │   ├── s3-encryption.tf
│   │   ├── s3.tf
│   │   ├── search-debug.tf
│   │   ├── security_group.tf
│   │   ├── service-account.yml
│   │   ├── sns.tf
│   │   ├── sqs.tf
│   │   ├── terraform.tf
│   │   ├── variables.tf
│   │   ├── volumes.tf
│   │   └── web-and-helper.yml
│   ├── demo-resources/
│   │   └── s3-bucket.tf
│   └── rules/
│       ├── alias.yml
│       ├── generic-json.yml
│       ├── generic-yaml.yml
│       ├── iam-policies.yml
│       ├── iam-restricted.yml
│       ├── kubernetes.yml
│       ├── lint-rules-with-error.yml
│       ├── no-iam-actions.yml
│       ├── s3-encryption.yml
│       ├── terraform-more.yml
│       ├── terraform.yml
│       └── variables.tf
├── go.mod
├── go.sum
└── linter/
    ├── common.go
    ├── common_test.go
    ├── csv_resource_loader.go
    ├── csv_resource_loader_test.go
    ├── file_linter.go
    ├── file_linter_test.go
    ├── helpers_test.go
    ├── json_resource_loader.go
    ├── json_resource_loader_test.go
    ├── kubernetes.go
    ├── kubernetes_test.go
    ├── linter.go
    ├── linter_test.go
    ├── resource_linter.go
    ├── resource_linter_test.go
    ├── rules_resource_loader.go
    ├── rules_resource_loader_test.go
    ├── schema.go
    ├── terraform.go
    ├── terraform_interpolate.go
    ├── terraform_interpolate_test.go
    ├── terraform_test.go
    ├── terraform_v12.go
    ├── terraform_v12_test.go
    ├── testdata/
    │   ├── data/
    │   │   ├── bucket_name
    │   │   ├── multi_line_content
    │   │   ├── reference_relative.tf
    │   │   ├── template_file_example_basic
    │   │   ├── template_file_example_conditional
    │   │   └── template_file_example_for_loop
    │   ├── resources/
    │   │   ├── batch_privileged.tf
    │   │   ├── cloudfront_access_logs.tf
    │   │   ├── defines_variables.tf
    │   │   ├── dms_endpoint_encryption.tf
    │   │   ├── dynamic_block.tf
    │   │   ├── ec2_public.tf
    │   │   ├── elasticache_encryption_rest.tf
    │   │   ├── elasticache_encryption_transit.tf
    │   │   ├── embedded_yaml.yml
    │   │   ├── empty_document.yml
    │   │   ├── emr_cluster_logs.tf
    │   │   ├── explicit_chars.tf
    │   │   ├── generic.config
    │   │   ├── invalid.yml
    │   │   ├── kinesis_kms_stream.tf
    │   │   ├── kms_key_rotation.tf
    │   │   ├── missing_kind.yml
    │   │   ├── multi_level.tf
    │   │   ├── multiple_blocks_same.tf
    │   │   ├── multiple_pods.yml
    │   │   ├── neptune_db_encryption.tf
    │   │   ├── nullable_value.tf
    │   │   ├── pod.yml
    │   │   ├── policy_with_expression.tf
    │   │   ├── policy_with_variables.tf
    │   │   ├── rds_publicly_available.tf
    │   │   ├── reference_file.tf
    │   │   ├── reference_file_multi_line.tf
    │   │   ├── reference_variables.tf
    │   │   ├── sagemaker_endpoint_encryption.tf
    │   │   ├── sagemaker_notebook_encryption.tf
    │   │   ├── tagging.tf
    │   │   ├── template_file_function_basic.tf
    │   │   ├── template_file_function_conditional.tf
    │   │   ├── template_file_function_for_loop.tf
    │   │   ├── terraform_data.tf
    │   │   ├── terraform_inner_objects.tf
    │   │   ├── terraform_instance.tf
    │   │   ├── terraform_module.tf
    │   │   ├── terraform_policy.tf
    │   │   ├── terraform_policy_empty.tf
    │   │   ├── terraform_policy_invalid_json.tf
    │   │   ├── terraform_provider.tf
    │   │   ├── terraform_syntax_error.tf
    │   │   ├── tf12_for_loop.tf
    │   │   ├── tf12_resource_dependency.tf
    │   │   ├── users.csv
    │   │   ├── users.json
    │   │   ├── uses_local_variables.tf
    │   │   ├── uses_tf12_variables.tf
    │   │   └── uses_variables.tf
    │   └── rules/
    │       ├── aggregate.yml
    │       ├── bad-format.yml
    │       ├── batch_definition.yml
    │       ├── cloudfront_access_logs.yml
    │       ├── dms_endpoint_encryption.yml
    │       ├── dynamic_block.yml
    │       ├── ec2_public.yml
    │       ├── elasticache_encryption_rest.yml
    │       ├── elasticache_encryption_transit.yml
    │       ├── emr_cluster_logs.yml
    │       ├── exclude_resource.yml
    │       ├── explicit_chars.yml
    │       ├── generic-csv.yml
    │       ├── generic-json.yml
    │       ├── generic-yaml.yml
    │       ├── kinesis_kms_stream.yml
    │       ├── kms_key_rotation.yml
    │       ├── kubernetes.yml
    │       ├── neptune_db_encryption.yml
    │       ├── nullable_value.yml
    │       ├── policy_variable.yml
    │       ├── rds_publicly_available.yml
    │       ├── rules.yml
    │       ├── sagemaker_endpoint_encryption.yml
    │       ├── sagemaker_notebook_encryption.yml
    │       ├── tagging.yml
    │       ├── terraform_bucket.yml
    │       ├── terraform_data.yml
    │       ├── terraform_instance.yml
    │       ├── terraform_module.yml
    │       ├── terraform_policy.yml
    │       ├── terraform_provider.yml
    │       ├── terraform_v12_variables.yml
    │       ├── tf12_for_loop.yml
    │       └── unknown.yml
    ├── tf12parser/
    │   ├── README.md
    │   ├── attribute.go
    │   ├── block.go
    │   ├── parser.go
    │   ├── parser_test.go
    │   ├── range.go
    │   └── schema.go
    ├── yaml_resource_loader.go
    └── yaml_resource_loader_test.go
Download .txt
SYMBOL INDEX (466 symbols across 61 files)

FILE: assertion/compare.go
  function intCompare (line 8) | func intCompare(n1 int, n2 int) int {
  function daysOld (line 18) | func daysOld(data interface{}) int {
  function compare (line 32) | func compare(data interface{}, value string, valueType string) int {

FILE: assertion/compare_test.go
  function TestDaysOldForToday (line 9) | func TestDaysOldForToday(t *testing.T) {
  function TestDaysOldFor90DaysAgo (line 14) | func TestDaysOldFor90DaysAgo(t *testing.T) {

FILE: assertion/contains.go
  function interfaceListContains (line 7) | func interfaceListContains(v []interface{}, key, value string) (MatchRes...
  function stringListContains (line 21) | func stringListContains(v []string, key, value string) (MatchResult, err...
  function stringContains (line 33) | func stringContains(v string, key, value string) (MatchResult, error) {
  function defaultContains (line 40) | func defaultContains(data interface{}, key, value string) (MatchResult, ...
  function contains (line 51) | func contains(data interface{}, key, value string) (MatchResult, error) {
  function doesNotContain (line 64) | func doesNotContain(data interface{}, key, value string) (MatchResult, e...
  function startsWith (line 75) | func startsWith(data interface{}, key, prefix string) (MatchResult, erro...
  function endsWith (line 87) | func endsWith(data interface{}, key, suffix string) (MatchResult, error) {

FILE: assertion/contains_test.go
  function TestContainsWithNonJSONType (line 10) | func TestContainsWithNonJSONType(t *testing.T) {
  function TestDoesNotContainWithNonJSONType (line 18) | func TestDoesNotContainWithNonJSONType(t *testing.T) {
  function TestContainsWithString (line 26) | func TestContainsWithString(t *testing.T) {
  function TestContainsWithSliceOfStrings (line 33) | func TestContainsWithSliceOfStrings(t *testing.T) {

FILE: assertion/expression.go
  function searchAndMatch (line 3) | func searchAndMatch(expression Expression, resource Resource) (MatchResu...
  function orExpression (line 16) | func orExpression(expressions []Expression, resource Resource) (MatchRes...
  function xorExpression (line 29) | func xorExpression(expressions []Expression, resource Resource) (MatchRe...
  function andExpression (line 46) | func andExpression(expressions []Expression, resource Resource) (MatchRe...
  function notExpression (line 59) | func notExpression(expressions []Expression, resource Resource) (MatchRe...
  function collectResources (line 73) | func collectResources(key string, resource Resource) ([]Resource, error) {
  function everyExpression (line 93) | func everyExpression(collectionExpression CollectionExpression, resource...
  function someExpression (line 112) | func someExpression(collectionExpression CollectionExpression, resource ...
  function noneExpression (line 131) | func noneExpression(collectionExpression CollectionExpression, resource ...
  function exactlyOneExpression (line 150) | func exactlyOneExpression(collectionExpression CollectionExpression, res...
  function booleanExpression (line 171) | func booleanExpression(expression Expression, resource Resource) (MatchR...
  function ExcludeResource (line 200) | func ExcludeResource(rule Rule, resource Resource) bool {
  function FilterResourceExceptions (line 210) | func FilterResourceExceptions(rule Rule, resources []Resource) []Resource {
  function CheckExpression (line 224) | func CheckExpression(rule Rule, expression Expression, resource Resource...

FILE: assertion/expression_test.go
  type ExpressionTestCase (line 8) | type ExpressionTestCase struct
  function TestCheckExpression (line 14) | func TestCheckExpression(t *testing.T) {
  function TestNestedBooleans (line 406) | func TestNestedBooleans(t *testing.T) {
  function TestExceptions (line 469) | func TestExceptions(t *testing.T) {
  function TestNoExceptions (line 486) | func TestNoExceptions(t *testing.T) {
  function TestUsingFixtures (line 503) | func TestUsingFixtures(t *testing.T) {

FILE: assertion/has_properties.go
  function hasProperties (line 7) | func hasProperties(data interface{}, list string) (MatchResult, error) {

FILE: assertion/helper_test.go
  type FixtureTestCases (line 11) | type FixtureTestCases struct
  type FixtureTestCase (line 17) | type FixtureTestCase struct
  function FailTestIfError (line 26) | func FailTestIfError(err error, message string, t *testing.T) {
  function LoadTestCasesFromFixture (line 33) | func LoadTestCasesFromFixture(filename string, t *testing.T) FixtureTest...
  function RunTestCasesFromFixture (line 49) | func RunTestCasesFromFixture(filename string, t *testing.T) {

FILE: assertion/invoke.go
  type InvokeViolation (line 12) | type InvokeViolation struct
  type InvokeResponse (line 17) | type InvokeResponse struct
  type StandardExternalRuleInvoker (line 22) | type StandardExternalRuleInvoker struct
    method Invoke (line 45) | func (e StandardExternalRuleInvoker) Invoke(rule Rule, resource Resour...
  function makeViolation (line 25) | func makeViolation(rule Rule, resource Resource, message string) Violati...
  function makeViolations (line 39) | func makeViolations(rule Rule, resource Resource, message string) []Viol...

FILE: assertion/invoke_test.go
  function TestInvokeOK (line 13) | func TestInvokeOK(t *testing.T) {
  function TestInvokeWithViolations (line 33) | func TestInvokeWithViolations(t *testing.T) {
  function TestInvokeSendsMetadata (line 60) | func TestInvokeSendsMetadata(t *testing.T) {

FILE: assertion/ip_operations.go
  function getIPObject (line 13) | func getIPObject(addressString string) (net.IP, error) {
  function isSubnet (line 24) | func isSubnet(ipAddressStr string, supernet string) bool {
  function isPrivateIP (line 37) | func isPrivateIP(ipAddressStr string) bool {
  function maxHostCount (line 46) | func maxHostCount(ruleCidr string, hostLimitStr string) bool {
  function hostCountByNetmaskOnes (line 64) | func hostCountByNetmaskOnes(netmaskOnes int) int {

FILE: assertion/ip_operations_test.go
  function TestIsSubnet (line 19) | func TestIsSubnet(t *testing.T) {
  function TestIsPrivateIp (line 48) | func TestIsPrivateIp(t *testing.T) {
  function TestMaxHostCount (line 73) | func TestMaxHostCount(t *testing.T) {

FILE: assertion/log.go
  function SetDebug (line 10) | func SetDebug(b bool) {
  function Debugf (line 15) | func Debugf(format string, args ...interface{}) {
  function DebugJSON (line 22) | func DebugJSON(title string, object interface{}) {

FILE: assertion/match.go
  function matches (line 9) | func matches() (MatchResult, error) {
  function doesNotMatch (line 13) | func doesNotMatch(format string, args ...interface{}) (MatchResult, erro...
  function matchError (line 20) | func matchError(err error) (MatchResult, error) {
  function isMatch (line 27) | func isMatch(data interface{}, expression Expression) (MatchResult, erro...

FILE: assertion/match_test.go
  type MatchTestCase (line 9) | type MatchTestCase struct
  function getQuotesRight (line 17) | func getQuotesRight(jsonString string) string {
  function unmarshal (line 27) | func unmarshal(s string) (interface{}, error) {
  function TestIsMatch (line 39) | func TestIsMatch(t *testing.T) {

FILE: assertion/rules.go
  function ParseRules (line 9) | func ParseRules(rules string) (RuleSet, error) {
  function FilterRulesByTag (line 16) | func FilterRulesByTag(rules []Rule, tags []string) []Rule {
  function FilterRulesByID (line 27) | func FilterRulesByID(rules []Rule, ruleIDs []string, ignoreRuleIDs []str...
  function uniqueRules (line 54) | func uniqueRules(list []Rule) []Rule {
  function FilterRulesByTagAndID (line 67) | func FilterRulesByTagAndID(rules []Rule, tags []string, ruleIds []string...
  function ResolveRules (line 81) | func ResolveRules(rules []Rule, valueSource ValueSource) ([]Rule, []Viol...
  function ResolveRule (line 93) | func ResolveRule(rule Rule, valueSource ValueSource) (Rule, []Violation) {
  function CheckRule (line 121) | func CheckRule(rule Rule, resource Resource, e ExternalRuleInvoker) (str...
  function JoinRuleSets (line 170) | func JoinRuleSets(firstSet RuleSet, secondSet RuleSet) (RuleSet, error) {

FILE: assertion/rules_test.go
  type TestValueSource (line 10) | type TestValueSource struct
    method GetValue (line 12) | func (t TestValueSource) GetValue(expression Expression) (string, erro...
  function testValueSource (line 19) | func testValueSource() ValueSource {
  type TestValueSourceWithError (line 25) | type TestValueSourceWithError struct
    method GetValue (line 27) | func (t TestValueSourceWithError) GetValue(expression Expression) (str...
  function testValueSourceWithError (line 31) | func testValueSourceWithError() ValueSource {
  type MockExternalRuleInvoker (line 37) | type MockExternalRuleInvoker
    method Invoke (line 44) | func (e *MockExternalRuleInvoker) Invoke(Rule, Resource) (string, []Vi...
  function mockExternalRuleInvoker (line 39) | func mockExternalRuleInvoker() *MockExternalRuleInvoker {
  function MustParseRules (line 84) | func MustParseRules(content string, t *testing.T) RuleSet {
  function TestParseRules (line 92) | func TestParseRules(t *testing.T) {
  type FilterTestCase (line 99) | type FilterTestCase struct
  function TestFilterRules (line 106) | func TestFilterRules(t *testing.T) {
  function TestFilterRulesByTagAndID (line 127) | func TestFilterRulesByTagAndID(t *testing.T) {
  function TestRuleWithMultipleFilter (line 155) | func TestRuleWithMultipleFilter(t *testing.T) {
  function TestMultipleFiltersWithSingleFailure (line 175) | func TestMultipleFiltersWithSingleFailure(t *testing.T) {
  function TestMultipleFiltersWithMultipleFailures (line 195) | func TestMultipleFiltersWithMultipleFailures(t *testing.T) {
  function TestValueFrom (line 228) | func TestValueFrom(t *testing.T) {
  function TestResolveRuleError (line 252) | func TestResolveRuleError(t *testing.T) {
  function TestInvokeRule (line 274) | func TestInvokeRule(t *testing.T) {

FILE: assertion/search.go
  function SearchData (line 8) | func SearchData(expression string, data interface{}) (interface{}, error) {

FILE: assertion/types.go
  type Resource (line 6) | type Resource struct
  type RuleSet (line 16) | type RuleSet struct
  type Rule (line 28) | type Rule struct
  type Expression (line 44) | type Expression struct
  type CollectionExpression (line 61) | type CollectionExpression struct
  type ValueFrom (line 67) | type ValueFrom struct
  type InvokeRuleAPI (line 73) | type InvokeRuleAPI struct
  type ResourceConfig (line 79) | type ResourceConfig struct
  type ColumnConfig (line 86) | type ColumnConfig struct
  type ValidationReport (line 91) | type ValidationReport struct
  type Violation (line 98) | type Violation struct
  type ScannedResource (line 112) | type ScannedResource struct
  type ValueSource (line 122) | type ValueSource interface
  type ExternalRuleInvoker (line 127) | type ExternalRuleInvoker interface
  type MatchResult (line 132) | type MatchResult struct
  type Result (line 138) | type Result struct

FILE: assertion/util.go
  function unquoted (line 10) | func unquoted(s string) string {
  function quoted (line 17) | func quoted(s string) string {
  function isAbsent (line 21) | func isAbsent(s string) bool {
  function isPresent (line 28) | func isPresent(s string) bool {
  function isEmpty (line 32) | func isEmpty(data interface{}) bool {
  function isArray (line 48) | func isArray(data interface{}) bool {
  function listsIntersect (line 63) | func listsIntersect(list1 []string, list2 []string) bool {
  function jsonListsIntersect (line 74) | func jsonListsIntersect(s1 string, s2 string) bool {
  function ShouldIncludeFile (line 89) | func ShouldIncludeFile(patterns []string, filename string) (bool, error) {
  function FilterResourcesByType (line 107) | func FilterResourcesByType(resources []Resource, resourceType string, re...
  function FilterResourcesByTypes (line 121) | func FilterResourcesByTypes(resources []Resource, resourceTypes []string...
  function categoryMatches (line 131) | func categoryMatches(c1, c2 string) bool {
  function JSONStringify (line 139) | func JSONStringify(data interface{}) (string, error) {
  function currentTime (line 147) | func currentTime() string {
  function SliceContains (line 151) | func SliceContains(list []string, value string) bool {
  function ExcludeResourceTypes (line 161) | func ExcludeResourceTypes(resources []Resource, resourceTypes []string, ...
  function FilterResourcesForRule (line 172) | func FilterResourcesForRule(resources []Resource, rule Rule) []Resource {

FILE: assertion/util_test.go
  function TestUnquotedWithoutQuotes (line 8) | func TestUnquotedWithoutQuotes(t *testing.T) {
  function TestUnquotedWithQuotes (line 14) | func TestUnquotedWithQuotes(t *testing.T) {
  function TestIsAbsentEmptyString (line 20) | func TestIsAbsentEmptyString(t *testing.T) {
  function TestIsAbsentEmptyArray (line 26) | func TestIsAbsentEmptyArray(t *testing.T) {
  function TestIsAbsentNull (line 32) | func TestIsAbsentNull(t *testing.T) {
  function TestIsAbsentFalse (line 38) | func TestIsAbsentFalse(t *testing.T) {
  function TestIntersectTrue (line 44) | func TestIntersectTrue(t *testing.T) {
  function TestIntersectFalse (line 52) | func TestIntersectFalse(t *testing.T) {
  function TestJSONListsIntersectTrue (line 60) | func TestJSONListsIntersectTrue(t *testing.T) {
  function TestShouldIncludeFile (line 68) | func TestShouldIncludeFile(t *testing.T) {
  function TestShouldNotIncludeFile (line 79) | func TestShouldNotIncludeFile(t *testing.T) {
  function TestFilterShouldIncludeResources (line 90) | func TestFilterShouldIncludeResources(t *testing.T) {
  function TestFilterShouldExcludeResources (line 101) | func TestFilterShouldExcludeResources(t *testing.T) {
  function TestFilterShouldIncludeAllResources (line 112) | func TestFilterShouldIncludeAllResources(t *testing.T) {
  function TestFilterShouldMatchCategoryForResources (line 123) | func TestFilterShouldMatchCategoryForResources(t *testing.T) {
  function TestSliceContainsTrue (line 134) | func TestSliceContainsTrue(t *testing.T) {
  function TestSliceContainsFalse (line 142) | func TestSliceContainsFalse(t *testing.T) {
  function TestFilterPluralShouldMatchMultipleResources (line 150) | func TestFilterPluralShouldMatchMultipleResources(t *testing.T) {
  function TestFilterPluralShouldNotHaveUnlistedResources (line 161) | func TestFilterPluralShouldNotHaveUnlistedResources(t *testing.T) {
  function TestFilterResourcesForRuleSlice (line 173) | func TestFilterResourcesForRuleSlice(t *testing.T) {
  function TestFilterResourcesForRuleString (line 190) | func TestFilterResourcesForRuleString(t *testing.T) {
  function TestFilterResourcesForWildcard (line 204) | func TestFilterResourcesForWildcard(t *testing.T) {
  function TestFilterResourcesForDefault (line 218) | func TestFilterResourcesForDefault(t *testing.T) {
  function TestFilterExcludeResourcesForRuleString (line 230) | func TestFilterExcludeResourcesForRuleString(t *testing.T) {

FILE: assertion/value.go
  type StandardValueSource (line 17) | type StandardValueSource struct
    method GetValue (line 22) | func (v StandardValueSource) GetValue(expression Expression) (string, ...
    method GetValueFromS3 (line 52) | func (v StandardValueSource) GetValueFromS3(bucket string, key string)...
    method GetValueFromHTTP (line 94) | func (v StandardValueSource) GetValueFromHTTP(url string) (string, err...
  function getBucketRegion (line 77) | func getBucketRegion(bucket string) (string, error) {

FILE: assertion/value_test.go
  function TestCommandLineVariable (line 11) | func TestCommandLineVariable(t *testing.T) {
  function TestValueFromHttp (line 27) | func TestValueFromHttp(t *testing.T) {

FILE: cli/app.go
  type LinterOptions (line 25) | type LinterOptions struct
  type ProfileOptions (line 37) | type ProfileOptions struct
  type RuleException (line 52) | type RuleException struct
  type CommandLineOptions (line 61) | type CommandLineOptions struct
  type ReportWriter (line 82) | type ReportWriter interface
  type DefaultReportWriter (line 87) | type DefaultReportWriter struct
  function main (line 92) | func main() {
  function addExceptions (line 160) | func addExceptions(ruleSets []assertion.RuleSet, exceptions []RuleExcept...
  function addExceptionsToRuleSet (line 168) | func addExceptionsToRuleSet(ruleSet assertion.RuleSet, exceptions []Rule...
  function resourceMatch (line 182) | func resourceMatch(rule assertion.Rule, exception RuleException) bool {
  function categoryMatch (line 189) | func categoryMatch(rule assertion.Rule, exception RuleException) bool {
  function validateRules (line 193) | func validateRules(filenames []string, w ReportWriter) (int, error) {
  function loadRuleSets (line 205) | func loadRuleSets(args arrayFlags) ([]assertion.RuleSet, error) {
  function isYamlFile (line 223) | func isYamlFile(filename string) bool {
  function yamlFilesOnly (line 230) | func yamlFilesOnly(filenames []string) []string {
  function loadBuiltInRuleSet (line 243) | func loadBuiltInRuleSet(filename string) (assertion.RuleSet, error) {
  function addRuleSet (line 279) | func addRuleSet(ruleSet assertion.RuleSet, box packr.Box, filename strin...
  function getRuleSet (line 299) | func getRuleSet(box packr.Box, name string) (assertion.RuleSet, error) {
  function applyRules (line 313) | func applyRules(ruleSets []assertion.RuleSet, args arrayFlags, options L...
  function printReport (line 352) | func printReport(w io.Writer, report assertion.ValidationReport, queryEx...
  type arrayFlags (line 377) | type arrayFlags
    method String (line 379) | func (i *arrayFlags) String() string {
    method Set (line 386) | func (i *arrayFlags) Set(value string) error {
  function generateExitCode (line 391) | func generateExitCode(report assertion.ValidationReport) int {
  function loadFilenames (line 400) | func loadFilenames(commandLineFilenames []string, profileFilenames []str...
  function defaultToCurrentDirectory (line 410) | func defaultToCurrentDirectory(filenames []string) []string {
  function excludeFilenames (line 417) | func excludeFilenames(filenames []string, excludePatterns []string) []st...
  function excludeFilename (line 428) | func excludeFilename(filename string, excludePatterns []string) bool {
  function getFilenames (line 439) | func getFilenames(args []string) []string {
  function getFilesInDirectory (line 462) | func getFilesInDirectory(root string) []string {

FILE: cli/app_test.go
  function TestLoadTerraformRules (line 13) | func TestLoadTerraformRules(t *testing.T) {
  function TestLoadValidateRules (line 20) | func TestLoadValidateRules(t *testing.T) {
  function TestExcludeAll (line 27) | func TestExcludeAll(t *testing.T) {
  function TestExcludeSubdirectory (line 36) | func TestExcludeSubdirectory(t *testing.T) {
  function TestExcludeOnePattern (line 45) | func TestExcludeOnePattern(t *testing.T) {
  function TestExcludeMultiplePattern (line 54) | func TestExcludeMultiplePattern(t *testing.T) {
  function TestExcludeFrom (line 63) | func TestExcludeFrom(t *testing.T) {
  function TestProfileExceptions (line 80) | func TestProfileExceptions(t *testing.T) {
  function TestBuiltRules (line 107) | func TestBuiltRules(t *testing.T) {
  function TestPrintReport (line 137) | func TestPrintReport(t *testing.T) {
  function TestPrintReportWithQueryString (line 147) | func TestPrintReportWithQueryString(t *testing.T) {
  type MockReportWriter (line 162) | type MockReportWriter struct
    method WriteReport (line 166) | func (w MockReportWriter) WriteReport(r assertion.ValidationReport, o ...
  function TestApplyRules (line 170) | func TestApplyRules(t *testing.T) {
  function TestValidateRules (line 184) | func TestValidateRules(t *testing.T) {
  function TestResourceMatch (line 191) | func TestResourceMatch(t *testing.T) {
  function TestLoadRuleSetsBadFilename (line 233) | func TestLoadRuleSetsBadFilename(t *testing.T) {
  function TestLoadRuleSetsParseErrors (line 239) | func TestLoadRuleSetsParseErrors(t *testing.T) {
  function TestStdinFilename (line 248) | func TestStdinFilename(t *testing.T) {
  function TestGetFilenamesUsingDirectory (line 254) | func TestGetFilenamesUsingDirectory(t *testing.T) {
  function TestLoadFilenamesFromCommandLine (line 261) | func TestLoadFilenamesFromCommandLine(t *testing.T) {
  function TestLoadFilenamesFromProfile (line 268) | func TestLoadFilenamesFromProfile(t *testing.T) {
  function TestArrayFlags (line 275) | func TestArrayFlags(t *testing.T) {
  function TestLoadBuiltInRuleSetMissing (line 283) | func TestLoadBuiltInRuleSetMissing(t *testing.T) {

FILE: cli/builtin_test.go
  function loadRules (line 11) | func loadRules(t *testing.T, filename string) assertion.RuleSet {
  type BuiltInTestCase (line 17) | type BuiltInTestCase struct
  function numberOfWarnings (line 24) | func numberOfWarnings(violations []assertion.Violation) int {
  function numberOfFailures (line 33) | func numberOfFailures(violations []assertion.Violation) int {
  function getViolationsString (line 44) | func getViolationsString(severity string, violations []assertion.Violati...

FILE: cli/options.go
  function getCommandLineOptions (line 14) | func getCommandLineOptions() CommandLineOptions {
  function getLinterOptions (line 41) | func getLinterOptions(o CommandLineOptions, p ProfileOptions) (LinterOpt...
  function loadProfile (line 65) | func loadProfile(filename string) (ProfileOptions, error) {
  function makeTagList (line 96) | func makeTagList(tags string, profileOptions []string) []string {
  function makeRulesList (line 106) | func makeRulesList(ruleIDs string, profileOptions []string) []string {
  function makeQueryExpression (line 116) | func makeQueryExpression(queryExpression string, verboseReport bool, pro...
  function parseVariables (line 131) | func parseVariables(vars []string) map[string]string {
  function mergeVariables (line 144) | func mergeVariables(a, b map[string]string) map[string]string {
  function loadExcludePatterns (line 157) | func loadExcludePatterns(patterns []string, excludeFromFilenames []strin...
  function validateParser (line 176) | func validateParser(parser string) (string, error) {

FILE: cli/options_test.go
  function emptyCommandLineOptions (line 7) | func emptyCommandLineOptions() CommandLineOptions {
  function TestCommandLineOnlyOptions (line 21) | func TestCommandLineOnlyOptions(t *testing.T) {
  function TestProfileOnlyOptions (line 36) | func TestProfileOnlyOptions(t *testing.T) {
  function TestCommandLineOverridesProfile (line 51) | func TestCommandLineOverridesProfile(t *testing.T) {
  function TestCommandLineVariables (line 68) | func TestCommandLineVariables(t *testing.T) {
  function TestMergeVariables (line 87) | func TestMergeVariables(t *testing.T) {
  function TestLoadProfile (line 116) | func TestLoadProfile(t *testing.T) {
  function TestProfileExclude (line 126) | func TestProfileExclude(t *testing.T) {
  function TestValidateParser (line 150) | func TestValidateParser(t *testing.T) {

FILE: cli/report_writer.go
  method WriteReport (line 8) | func (w DefaultReportWriter) WriteReport(report assertion.ValidationRepo...

FILE: cli/report_writer_test.go
  function TestReportWriter (line 10) | func TestReportWriter(t *testing.T) {

FILE: cli/terraform_test.go
  type TestSuite (line 17) | type TestSuite struct
  type TestCase (line 26) | type TestCase struct
  function isTestCase (line 36) | func isTestCase(filename string) bool {
  function loadTestSuite (line 47) | func loadTestSuite(filename string) (TestSuite, error) {
  function getTestResources (line 65) | func getTestResources(directory string) ([]string, error) {
  function contains (line 83) | func contains(arr []string, str string) bool {
  function runTestSuite (line 93) | func runTestSuite(t *testing.T, ts TestSuite) {
  function RunBuiltinTests (line 160) | func RunBuiltinTests(t *testing.T, resourceType string) {
  function TestTerraformBuiltInRules (line 177) | func TestTerraformBuiltInRules(t *testing.T) {

FILE: linter/common.go
  function readContent (line 15) | func readContent(filename string) ([]byte, error) {
  function loadYAML (line 22) | func loadYAML(filename string) ([]interface{}, error) {
  function splitYAMLDocs (line 48) | func splitYAMLDocs(content string) []string {
  function docIsNotEmpty (line 68) | func docIsNotEmpty(s string) bool {
  function loadJSON (line 72) | func loadJSON(filename string) ([]interface{}, error) {
  function loadCSV (line 88) | func loadCSV(filename string) ([][]string, error) {
  function getResourceIDFromFilename (line 96) | func getResourceIDFromFilename(filename string) string {
  function CombineValidationReports (line 102) | func CombineValidationReports(r1, r2 assertion.ValidationReport) asserti...

FILE: linter/common_test.go
  function TestLoadYamlFileError (line 10) | func TestLoadYamlFileError(t *testing.T) {
  function TestLoadYamlParseError (line 17) | func TestLoadYamlParseError(t *testing.T) {
  function TestLoadYamlUnexpectedFormat (line 27) | func TestLoadYamlUnexpectedFormat(t *testing.T) {
  function TestLoadYamlMultipleDocuments (line 33) | func TestLoadYamlMultipleDocuments(t *testing.T) {
  function TestLoadYamlWithEmbeddedYaml (line 39) | func TestLoadYamlWithEmbeddedYaml(t *testing.T) {
  function TestLoadYamlWithEmptyDocument (line 45) | func TestLoadYamlWithEmptyDocument(t *testing.T) {
  function TestGetResourceIDFromFilename (line 51) | func TestGetResourceIDFromFilename(t *testing.T) {
  function TestCombineValidationReports (line 59) | func TestCombineValidationReports(t *testing.T) {

FILE: linter/csv_resource_loader.go
  type CSVResourceLoader (line 9) | type CSVResourceLoader struct
    method Load (line 23) | func (l CSVResourceLoader) Load(filename string) (FileResources, error) {
    method PostLoad (line 50) | func (l CSVResourceLoader) PostLoad(r FileResources) ([]assertion.Reso...
  function extractCSVResourceID (line 13) | func extractCSVResourceID(expression string, properties interface{}) str...

FILE: linter/csv_resource_loader_test.go
  function TestCSVLinterValidate (line 9) | func TestCSVLinterValidate(t *testing.T) {
  function TestCSVLinterSearch (line 24) | func TestCSVLinterSearch(t *testing.T) {

FILE: linter/file_linter.go
  type Variable (line 13) | type Variable struct
  type FileResources (line 18) | type FileResources struct
  type FileResourceLoader (line 23) | type FileResourceLoader interface
  type FileLinter (line 28) | type FileLinter struct
    method Validate (line 36) | func (fl FileLinter) Validate(ruleSet assertion.RuleSet, options Optio...
    method Search (line 119) | func (fl FileLinter) Search(ruleSet assertion.RuleSet, searchExpressio...
  function filterFiles (line 75) | func filterFiles(fileNames []string, patterns []string) (ret []string) {
  function iterateFiles (line 85) | func iterateFiles(fl FileLinter, ruleSet assertion.RuleSet, filesScanned...
  function makeLoadViolation (line 104) | func makeLoadViolation(filename string, err error) assertion.Violation {

FILE: linter/file_linter_test.go
  function TestFilterFiles (line 8) | func TestFilterFiles(t *testing.T) {

FILE: linter/helpers_test.go
  function loadRulesForTest (line 9) | func loadRulesForTest(filename string, t *testing.T) assertion.RuleSet {
  function assertViolationsCount (line 23) | func assertViolationsCount(testName string, count int, violations []asse...
  function assertViolationByRuleID (line 30) | func assertViolationByRuleID(testName string, ruleID string, violations ...

FILE: linter/json_resource_loader.go
  type JSONResourceLoader (line 9) | type JSONResourceLoader struct
    method Load (line 23) | func (l JSONResourceLoader) Load(filename string) (FileResources, erro...
    method PostLoad (line 58) | func (l JSONResourceLoader) PostLoad(r FileResources) ([]assertion.Res...
  function extractJSONResourceID (line 13) | func extractJSONResourceID(expression string, properties interface{}) st...

FILE: linter/json_resource_loader_test.go
  function TestJSONLinterValidate (line 9) | func TestJSONLinterValidate(t *testing.T) {
  function TestJSONLinterSearch (line 25) | func TestJSONLinterSearch(t *testing.T) {

FILE: linter/kubernetes.go
  type KubernetesLinter (line 10) | type KubernetesLinter struct
  type KubernetesResourceLoader (line 16) | type KubernetesResourceLoader struct
    method Load (line 28) | func (l KubernetesResourceLoader) Load(filename string) (FileResources...
    method PostLoad (line 63) | func (l KubernetesResourceLoader) PostLoad(r FileResources) ([]asserti...
  function getResourceIDFromMetadata (line 18) | func getResourceIDFromMetadata(m map[string]interface{}) (string, bool) {

FILE: linter/kubernetes_test.go
  function TestKubernetesSpecialVariables (line 9) | func TestKubernetesSpecialVariables(t *testing.T) {
  function TestKubernetesMissingKind (line 21) | func TestKubernetesMissingKind(t *testing.T) {

FILE: linter/linter.go
  type Linter (line 12) | type Linter interface
  type Options (line 18) | type Options struct
  function NewLinter (line 26) | func NewLinter(ruleSet assertion.RuleSet, vs assertion.ValueSource, file...

FILE: linter/linter_test.go
  type NewLinterTestCase (line 9) | type NewLinterTestCase struct
  function TestNewLinter (line 14) | func TestNewLinter(t *testing.T) {
  function TestUnknownLinterType (line 42) | func TestUnknownLinterType(t *testing.T) {
  type MockValueSource (line 54) | type MockValueSource struct
    method GetValue (line 56) | func (m MockValueSource) GetValue(e assertion.Expression) (string, err...

FILE: linter/resource_linter.go
  type ResourceLinter (line 8) | type ResourceLinter struct
    method ValidateResources (line 13) | func (r ResourceLinter) ValidateResources(resources []assertion.Resour...

FILE: linter/resource_linter_test.go
  function TestIgnoreResource (line 7) | func TestIgnoreResource(t *testing.T) {

FILE: linter/rules_resource_loader.go
  type RulesResourceLoader (line 9) | type RulesResourceLoader struct
    method Load (line 21) | func (l RulesResourceLoader) Load(filename string) (FileResources, err...
    method PostLoad (line 59) | func (l RulesResourceLoader) PostLoad(r FileResources) ([]assertion.Re...
  function getAttr (line 11) | func getAttr(m map[string]interface{}, keys ...string) []interface{} {

FILE: linter/rules_resource_loader_test.go
  function TestRulesLinterValidate (line 9) | func TestRulesLinterValidate(t *testing.T) {
  function TestRulesLinterSearch (line 25) | func TestRulesLinterSearch(t *testing.T) {

FILE: linter/terraform.go
  type TerraformResourceLoader (line 19) | type TerraformResourceLoader struct
    method Load (line 159) | func (l TerraformResourceLoader) Load(filename string) (FileResources,...
    method PostLoad (line 274) | func (l TerraformResourceLoader) PostLoad(fr FileResources) ([]asserti...
  type TerraformLoadResult (line 22) | type TerraformLoadResult struct
  function loadHCL (line 32) | func loadHCL(filename string) (TerraformLoadResult, error) {
  function loadVariables (line 81) | func loadVariables(data interface{}) []Variable {
  function loadLocalVariables (line 96) | func loadLocalVariables(data interface{}) []Variable {
  function getVariableValue (line 112) | func getVariableValue(key string, resource interface{}) interface{} {
  function getVariableFromEnvironment (line 120) | func getVariableFromEnvironment(key string) interface{} {
  function getVariableDefault (line 124) | func getVariableDefault(resource interface{}) interface{} {
  function flattenMaps (line 136) | func flattenMaps(v interface{}) interface{} {
  function getResourceLineNumber (line 148) | func getResourceLineNumber(resourceType, resourceID, filename string, ro...
  function addIDToProviders (line 180) | func addIDToProviders(providers []interface{}) []interface{} {
  function addIDToProvider (line 188) | func addIDToProvider(provider interface{}) interface{} {
  function addIDToProviderValue (line 199) | func addIDToProviderValue(value interface{}) interface{} {
  function addKeyToModules (line 208) | func addKeyToModules(modules []interface{}) []interface{} {
  function addKeyToModule (line 216) | func addKeyToModule(resources map[string]interface{}, module interface{}...
  function getResources (line 241) | func getResources(filename string, ast *ast.File, objects []interface{},...
  function replaceVariables (line 288) | func replaceVariables(templateResource interface{}, variables []Variable...
  function replaceVariablesInMap (line 300) | func replaceVariablesInMap(templateResource map[string]interface{}, vari...
  function replaceVariablesInList (line 316) | func replaceVariablesInList(list []interface{}, variables []Variable) []...
  function parseJSONDocuments (line 324) | func parseJSONDocuments(resource interface{}) (interface{}, error) {
  function getProperties (line 344) | func getProperties(templateResource interface{}) map[string]interface{} {

FILE: linter/terraform_interpolate.go
  function makeList (line 14) | func makeList(variables []interface{}) []ast.Variable {
  function makeMap (line 22) | func makeMap(m map[string]interface{}) map[string]ast.Variable {
  function makeVar (line 32) | func makeVar(v interface{}) ast.Variable {
  function makeVarMap (line 58) | func makeVarMap(variables []Variable) map[string]ast.Variable {
  function interpolationFuncFile (line 66) | func interpolationFuncFile() ast.Function {
  function interpolationFuncLookup (line 86) | func interpolationFuncLookup() ast.Function {
  function interpolationFuncJoin (line 128) | func interpolationFuncJoin() ast.Function {
  function interpolationFuncConcat (line 159) | func interpolationFuncConcat() ast.Function {
  function interpolationFuncFormat (line 200) | func interpolationFuncFormat() ast.Function {
  function interpolationFuncList (line 215) | func interpolationFuncList() ast.Function {
  function interpolationFuncReplace (line 254) | func interpolationFuncReplace() ast.Function {
  function interpolationFuncElement (line 282) | func interpolationFuncElement() ast.Function {
  function interpolationFuncMap (line 313) | func interpolationFuncMap() ast.Function {
  function interpolationFuncMerge (line 355) | func interpolationFuncMerge() ast.Function {
  function Funcs (line 375) | func Funcs() map[string]ast.Function {
  function interpolate (line 390) | func interpolate(s string, variables []Variable) interface{} {

FILE: linter/terraform_interpolate_test.go
  type interpolationTestCase (line 8) | type interpolationTestCase struct
  function TestInterpolation (line 13) | func TestInterpolation(t *testing.T) {

FILE: linter/terraform_test.go
  function TestTerraformLinter (line 11) | func TestTerraformLinter(t *testing.T) {
  function loadResourcesToTest (line 26) | func loadResourcesToTest(t *testing.T, filename string) []assertion.Reso...
  function getResourceTags (line 35) | func getResourceTags(r assertion.Resource) map[string]interface{} {
  function TestTerraformVariable (line 41) | func TestTerraformVariable(t *testing.T) {
  function TestTerraformVariableWithNoDefault (line 48) | func TestTerraformVariableWithNoDefault(t *testing.T) {
  function TestTerraformFunctionCall (line 55) | func TestTerraformFunctionCall(t *testing.T) {
  function TestTerraformListVariable (line 62) | func TestTerraformListVariable(t *testing.T) {
  function TestTerraformLocalVariable (line 69) | func TestTerraformLocalVariable(t *testing.T) {
  function TestTerraformVariablesFromEnvironment (line 76) | func TestTerraformVariablesFromEnvironment(t *testing.T) {
  function TestTerraformFileFunction (line 85) | func TestTerraformFileFunction(t *testing.T) {
  function TestTerraformVariablesInDifferentFile (line 92) | func TestTerraformVariablesInDifferentFile(t *testing.T) {
  type TestingValueSource (line 110) | type TestingValueSource struct
    method GetValue (line 112) | func (s TestingValueSource) GetValue(a assertion.Expression) (string, ...
  function TestTerraformDataLoader (line 119) | func TestTerraformDataLoader(t *testing.T) {
  type terraformLinterTestCase (line 126) | type terraformLinterTestCase struct
  function TestTerraformLinterCases (line 133) | func TestTerraformLinterCases(t *testing.T) {

FILE: linter/terraform_v12.go
  type Terraform12ResourceLoader (line 15) | type Terraform12ResourceLoader struct
    method Load (line 50) | func (l Terraform12ResourceLoader) Load(filename string) (FileResource...
    method LoadMany (line 64) | func (l Terraform12ResourceLoader) LoadMany(filenames []string) (FileR...
    method PostLoad (line 266) | func (l Terraform12ResourceLoader) PostLoad(inputResources FileResourc...
  type Terraform12LoadResult (line 18) | type Terraform12LoadResult struct
  function loadHCLv2 (line 78) | func loadHCLv2(paths []string) (Terraform12LoadResult, error) {
  function getBlocksOfType (line 112) | func getBlocksOfType(blocks tf12parser.Blocks, blockCategory string) []a...
  function attributesToMap (line 170) | func attributesToMap(block tf12parser.Block) map[string]interface{} {
  function iterateElements (line 209) | func iterateElements(propertyMap map[string]interface{}, name string, va...
  function setValue (line 226) | func setValue(m map[string]interface{}, name string, value string) {
  function ctyValueToString (line 235) | func ctyValueToString(value cty.Value) string {

FILE: linter/terraform_v12_test.go
  function TestTerraformV12Linter (line 16) | func TestTerraformV12Linter(t *testing.T) {
  function loadResources12ToTest (line 31) | func loadResources12ToTest(t *testing.T, filename string) []assertion.Re...
  function filterByCategory (line 40) | func filterByCategory(t *testing.T, resources []assertion.Resource, cate...
  function TestSingleResourceType (line 50) | func TestSingleResourceType(t *testing.T) {
  function TestResourceDependency (line 58) | func TestResourceDependency(t *testing.T) {
  function TestTupleType (line 65) | func TestTupleType(t *testing.T) {
  function TestMultipleBlocksOfSameType12 (line 77) | func TestMultipleBlocksOfSameType12(t *testing.T) {
  function TestInnerObjects12 (line 85) | func TestInnerObjects12(t *testing.T) {
  function TestTerraform12Variable (line 94) | func TestTerraform12Variable(t *testing.T) {
  function TestTerraform12VariableWithNoDefault (line 102) | func TestTerraform12VariableWithNoDefault(t *testing.T) {
  function TestTerraform12FunctionCall (line 110) | func TestTerraform12FunctionCall(t *testing.T) {
  function TestTerraform12ListVariable (line 118) | func TestTerraform12ListVariable(t *testing.T) {
  function TestTerraform12LocalVariable (line 126) | func TestTerraform12LocalVariable(t *testing.T) {
  function TestTerraform12VariablesFromEnvironment (line 134) | func TestTerraform12VariablesFromEnvironment(t *testing.T) {
  function TestTerraform12FileFunction (line 144) | func TestTerraform12FileFunction(t *testing.T) {
  function TestTerraform12VariablesInDifferentFile (line 151) | func TestTerraform12VariablesInDifferentFile(t *testing.T) {
  function TestTerraform12DataLoader (line 169) | func TestTerraform12DataLoader(t *testing.T) {
  function TestTerraform12ResourceLineNumber (line 176) | func TestTerraform12ResourceLineNumber(t *testing.T) {
  function TestTerraform12ResourceFileName (line 181) | func TestTerraform12ResourceFileName(t *testing.T) {
  function TestTerraform12DataLineNumber (line 186) | func TestTerraform12DataLineNumber(t *testing.T) {
  function TestTerraform12DataFileName (line 191) | func TestTerraform12DataFileName(t *testing.T) {
  function TestTerraform12ProviderLineNumber (line 196) | func TestTerraform12ProviderLineNumber(t *testing.T) {
  function TestTerraform12ProviderFileName (line 201) | func TestTerraform12ProviderFileName(t *testing.T) {
  function TestTerraform12ModuleLineNumber (line 206) | func TestTerraform12ModuleLineNumber(t *testing.T) {
  function TestTerraform12ModuleFileName (line 211) | func TestTerraform12ModuleFileName(t *testing.T) {
  function getViolationsString (line 218) | func getViolationsString(violations []assertion.Violation) string {
  function TestTerraform12LinterCases (line 236) | func TestTerraform12LinterCases(t *testing.T) {
  function TestTerraform12FileFunctionMultiLineContent (line 434) | func TestTerraform12FileFunctionMultiLineContent(t *testing.T) {
  function TestTerraform12FileFunctionResourceFileAbsolutePath (line 444) | func TestTerraform12FileFunctionResourceFileAbsolutePath(t *testing.T) {
  function TestTerraform12FileFunctionTemplateFileFunction (line 452) | func TestTerraform12FileFunctionTemplateFileFunction(t *testing.T) {
  function TestTerraform12FileFunctionTemplateFileForLoop (line 460) | func TestTerraform12FileFunctionTemplateFileForLoop(t *testing.T) {
  function TestTerraform12FileFunctionTemplateFileConditional (line 468) | func TestTerraform12FileFunctionTemplateFileConditional(t *testing.T) {
  function TestTerraform12FileFunctionReferenceFileAbsoultePath (line 478) | func TestTerraform12FileFunctionReferenceFileAbsoultePath(t *testing.T) {

FILE: linter/tf12parser/attribute.go
  type Attribute (line 9) | type Attribute struct
    method IsLiteral (line 21) | func (attr *Attribute) IsLiteral() bool {
    method Type (line 25) | func (attr *Attribute) Type() cty.Type {
    method Value (line 29) | func (attr *Attribute) Value() cty.Value {
    method Range (line 40) | func (attr *Attribute) Range() Range {
    method Name (line 48) | func (attr *Attribute) Name() string {
  function NewAttribute (line 14) | func NewAttribute(attr *hclsyntax.Attribute, ctx *hcl.EvalContext) *Attr...

FILE: linter/tf12parser/block.go
  type Block (line 10) | type Block struct
    method body (line 52) | func (block *Block) body() *hclsyntax.Body {
    method Type (line 56) | func (block *Block) Type() string {
    method Labels (line 60) | func (block *Block) Labels() []string {
    method Range (line 64) | func (block *Block) Range() Range {
    method AllBlocks (line 76) | func (block *Block) AllBlocks() []*Block {
    method GetBlock (line 87) | func (block *Block) GetBlock(name string) *Block {
    method GetBlocks (line 99) | func (block *Block) GetBlocks(name string) Blocks {
    method GetAttributes (line 112) | func (block *Block) GetAttributes() []*Attribute {
    method GetAttribute (line 123) | func (block *Block) GetAttribute(name string) *Attribute {
    method Name (line 135) | func (block *Block) Name() string {
  type Blocks (line 16) | type Blocks
    method OfType (line 18) | func (blocks Blocks) OfType(t string) Blocks {
    method RemoveDuplicates (line 28) | func (blocks Blocks) RemoveDuplicates() Blocks {
  function NewBlock (line 45) | func NewBlock(hclBlock *hcl.Block, ctx *hcl.EvalContext) *Block {

FILE: linter/tf12parser/parser.go
  constant maxContextIterations (line 15) | maxContextIterations = 32
  type Parser (line 18) | type Parser struct
    method ParseMany (line 31) | func (parser *Parser) ParseMany(paths []string) (Blocks, error) {
    method ParseDirectory (line 53) | func (parser *Parser) ParseDirectory(path string) (Blocks, error) {
    method ParseFile (line 76) | func (parser *Parser) ParseFile(path string) (Blocks, error) {
    method parseFile (line 95) | func (parser *Parser) parseFile(file *hcl.File) (hcl.Blocks, error) {
    method recursivelyParseDirectory (line 109) | func (parser *Parser) recursivelyParseDirectory(path string) error {
    method buildEvaluationContext (line 140) | func (parser *Parser) buildEvaluationContext(blocks hcl.Blocks, path s...
    method parseModuleBlock (line 205) | func (parser *Parser) parseModuleBlock(block *hcl.Block, parentContext...
    method readValues (line 260) | func (parser *Parser) readValues(ctx *hcl.EvalContext, block *hcl.Bloc...
    method getValuesByBlockType (line 278) | func (parser *Parser) getValuesByBlockType(ctx *hcl.EvalContext, block...
  function New (line 24) | func New() *Parser {

FILE: linter/tf12parser/parser_test.go
  function Test_BasicParsing (line 16) | func Test_BasicParsing(t *testing.T) {
  function Test_Modules (line 127) | func Test_Modules(t *testing.T) {
  function createTestFile (line 177) | func createTestFile(filename, contents string) string {
  function createTestFileWithModule (line 189) | func createTestFileWithModule(contents string, moduleContents string) st...

FILE: linter/tf12parser/range.go
  type Range (line 6) | type Range struct
    method String (line 13) | func (r *Range) String() string {

FILE: linter/yaml_resource_loader.go
  type YAMLResourceLoader (line 9) | type YAMLResourceLoader struct
    method Load (line 23) | func (l YAMLResourceLoader) Load(filename string) (FileResources, erro...
    method PostLoad (line 58) | func (l YAMLResourceLoader) PostLoad(r FileResources) ([]assertion.Res...
  function extractYAMLResourceID (line 13) | func extractYAMLResourceID(expression string, properties interface{}) st...

FILE: linter/yaml_resource_loader_test.go
  function TestYAMLLinterValidate (line 9) | func TestYAMLLinterValidate(t *testing.T) {
  function TestYAMLLinterSearch (line 25) | func TestYAMLLinterSearch(t *testing.T) {
Condensed preview — 666 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (998K chars).
[
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 49,
    "preview": "FROM stelligent/vscode-remote-config-lint:latest\n"
  },
  {
    "path": ".devcontainer/build/Dockerfile",
    "chars": 2807,
    "preview": "FROM ubuntu:latest\n\n# Avoid warnings by switching to noninteractive\nENV DEBIAN_FRONTEND=noninteractive\n\n# Install packag"
  },
  {
    "path": ".devcontainer/build/dockerhub.sh",
    "chars": 701,
    "preview": "#!/bin/bash -ex\n\nset +x\nif [[ -z ${DOCKER_ORG} ]];\nthen\n  echo DOCKER_ORG must be set in the environment\n  exit 1\nfi\nif "
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 2351,
    "preview": "{\n\t\"name\": \"config-lint Development\",\n\t\"dockerFile\": \"Dockerfile\",\n\t\"appPort\": 9000,\n\t\"remote.containers.workspaceMountC"
  },
  {
    "path": ".dockerhub/Dockerfile",
    "chars": 61,
    "preview": "FROM scratch\nCOPY config-lint /\nENTRYPOINT [\"/config-lint\"] \n"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 818,
    "preview": "name: Build\n\non:\n  push:\n    branches-ignore:\n      - 'master'\n    tags-ignore:\n      - '**'\n    paths-ignore:\n      - '"
  },
  {
    "path": ".github/workflows/build_and_deploy.yml",
    "chars": 1137,
    "preview": "name: Build & Deploy\n\non:\n  push:\n    tags:\n      - 'v*.*.*'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n     "
  },
  {
    "path": ".github/workflows/bump_version.yml",
    "chars": 635,
    "preview": "# Push with a commit message containing `#major` to bump major version\n# and update the major version number here.\n\n# MA"
  },
  {
    "path": ".github/workflows/vscode_remote_development.yml",
    "chars": 640,
    "preview": "name: VS Code DockerHub Build & Push\n\non:\n  push:\n    branches:\n      - 'master'\n    paths:\n      - '.devcontainer/**'\n\n"
  },
  {
    "path": ".gitignore",
    "chars": 520,
    "preview": "# Local dev\ndist/\n.bundle\n.DS_Store\n.vscode/**/*\n.release/\n.idea/\n.DS_Store\n.test/\n*/coverage.out\n*/*packr.go\n**/*.log\n*"
  },
  {
    "path": ".goreleaser.yml",
    "chars": 1100,
    "preview": "builds:\n-\n  main: ./cli\n  env:\n    - CGO_ENABLED=0\n  goos:\n   - linux\n   - darwin\n   - windows\n  goarch:\n    - 386\n    -"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3580,
    "preview": "# Contributing to config-lint\n\nHelp wanted! We'd love your contributions to config-lint. Please review the following gui"
  },
  {
    "path": "LICENSE.md",
    "chars": 1142,
    "preview": "MIT License\n\nCopyright (c) 2018-2020 Stelligent\nPortions copyright 2019 Liam Galvin (https://github.com/liamg/tfsec)\n\n\nP"
  },
  {
    "path": "Makefile",
    "chars": 2063,
    "preview": "# Versioning based on latest git tag.\nVERSION := $(shell git tag -l --sort=creatordate | grep \"^v[0-9]*.[0-9]*.[0-9]*$$\""
  },
  {
    "path": "README.md",
    "chars": 2548,
    "preview": "[![Latest Release](https://img.shields.io/github/v/release/stelligent/config-lint?color=%233D9970)](https://img.shields."
  },
  {
    "path": "assertion/compare.go",
    "chars": 1310,
    "preview": "package assertion\n\nimport (\n\t\"strconv\"\n\t\"time\"\n)\n\nfunc intCompare(n1 int, n2 int) int {\n\tif n1 < n2 {\n\t\treturn -1\n\t}\n\tif"
  },
  {
    "path": "assertion/compare_test.go",
    "chars": 458,
    "preview": "package assertion\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestDaysOldForToday(t *test"
  },
  {
    "path": "assertion/contains.go",
    "chars": 2434,
    "preview": "package assertion\n\nimport (\n\t\"strings\"\n)\n\nfunc interfaceListContains(v []interface{}, key, value string) (MatchResult, e"
  },
  {
    "path": "assertion/contains_test.go",
    "chars": 1063,
    "preview": "package assertion\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\n// The non error cases are covered in ma"
  },
  {
    "path": "assertion/expression.go",
    "chars": 7080,
    "preview": "package assertion\n\nfunc searchAndMatch(expression Expression, resource Resource) (MatchResult, error) {\n\tv, err := Searc"
  },
  {
    "path": "assertion/expression_test.go",
    "chars": 10778,
    "preview": "package assertion\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n)\n\ntype ExpressionTestCase struct {\n\tRule           Rule\n\tResour"
  },
  {
    "path": "assertion/has_properties.go",
    "chars": 330,
    "preview": "package assertion\n\nimport (\n\t\"strings\"\n)\n\nfunc hasProperties(data interface{}, list string) (MatchResult, error) {\n\tfor "
  },
  {
    "path": "assertion/helper_test.go",
    "chars": 1608,
    "preview": "package assertion\n\nimport (\n\t\"github.com/ghodss/yaml\"\n\t\"io/ioutil\"\n\t\"testing\"\n)\n\ntype (\n\t// FixtureTestCases is used to "
  },
  {
    "path": "assertion/invoke.go",
    "chars": 2859,
    "preview": "package assertion\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n)\n\n// InvokeViolation has message "
  },
  {
    "path": "assertion/invoke_test.go",
    "chars": 2135,
    "preview": "package assertion\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net"
  },
  {
    "path": "assertion/ip_operations.go",
    "chars": 1565,
    "preview": "package assertion\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar rfc1918PrivateCIDRs = []string{\"10.0.0.0/"
  },
  {
    "path": "assertion/ip_operations_test.go",
    "chars": 1846,
    "preview": "package assertion\n\nimport (\n\t\"testing\"\n)\n\nvar ipTests = []struct {\n\tvalue          string\n\tsupernet       string\n\texpect"
  },
  {
    "path": "assertion/log.go",
    "chars": 485,
    "preview": "package assertion\n\nimport \"fmt\"\n\nvar (\n\tisDebug = false\n)\n\n// SetDebug turns verbose logging on or off\nfunc SetDebug(b b"
  },
  {
    "path": "assertion/match.go",
    "chars": 4849,
    "preview": "package assertion\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nfunc matches() (MatchResult, error) {\n\treturn MatchResult{Mat"
  },
  {
    "path": "assertion/match_test.go",
    "chars": 7312,
    "preview": "package assertion\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype MatchTestCase struct {\n\tSearchResult   interface{"
  },
  {
    "path": "assertion/rules.go",
    "chars": 6064,
    "preview": "package assertion\n\nimport (\n\t\"errors\"\n\t\"github.com/ghodss/yaml\"\n)\n\n// ParseRules converts YAML string content to a Resul"
  },
  {
    "path": "assertion/rules_test.go",
    "chars": 7768,
    "preview": "package assertion\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\n// TestValueSource provides test values\n\ntype TestValueSource struct"
  },
  {
    "path": "assertion/search.go",
    "chars": 288,
    "preview": "package assertion\n\nimport (\n\t\"github.com/jmespath/go-jmespath\"\n)\n\n// SearchData applies a JMESPath to a JSON object\nfunc"
  },
  {
    "path": "assertion/testdata/collection-assertions.yaml",
    "chars": 5614,
    "preview": "---\ndescription: Test collection assertions\ntest_cases:\n\n  - name: every_OK\n    rule:\n      id:       COLLECTION\n      m"
  },
  {
    "path": "assertion/testdata/conditions.yaml",
    "chars": 1433,
    "preview": "---\ndescription: Test conditions\ntest_cases:\n\n  - name: conditions_false\n    rule:\n      id: CONDITIONS_1\n      message:"
  },
  {
    "path": "assertion/testdata/default-severity.yaml",
    "chars": 400,
    "preview": "---\ndescription: Test uses default severity\ntest_cases:\n\n  - name: default-severity-FAILURE\n    rule:\n      id: PROPERTI"
  },
  {
    "path": "assertion/testdata/has-properties.yaml",
    "chars": 824,
    "preview": "---\ndescription: Test has-properties operator\ntest_cases:\n\n  - name: has-properties_OK\n    rule:\n      id: PROPERTIES_1\n"
  },
  {
    "path": "assertion/types.go",
    "chars": 3444,
    "preview": "package assertion\n\ntype (\n\n\t// Resource describes a resource to be linted\n\tResource struct {\n\t\tID         string `cty:\"a"
  },
  {
    "path": "assertion/util.go",
    "chars": 4434,
    "preview": "package assertion\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"time\"\n)\n\nfunc unquoted(s string) string {\n\tif s[0"
  },
  {
    "path": "assertion/util_test.go",
    "chars": 6689,
    "preview": "package assertion\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestUnquotedWithoutQuotes(t *testing.T) {\n\tif unquoted(\"Foo\") "
  },
  {
    "path": "assertion/value.go",
    "chars": 3322,
    "preview": "package assertion\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/aws/aws-sdk-go/aws\"\n\t\"github.com/aws/aws-sdk-go/aws/s"
  },
  {
    "path": "assertion/value_test.go",
    "chars": 988,
    "preview": "package assertion\n\nimport (\n\t\"fmt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\n"
  },
  {
    "path": "cli/app.go",
    "chars": 13113,
    "preview": "package main\n\n//go:generate packr -v\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepat"
  },
  {
    "path": "cli/app_test.go",
    "chars": 9019,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/gobuffalo/packr\"\n\t\"github.com/stelligent/config-lint/assertion\""
  },
  {
    "path": "cli/assets/lint-rules.yml",
    "chars": 3034,
    "preview": "---\nversion: 1\ndescription: Rules for config-lint\ntype: LintRules\nfiles:\n  - \"*.yml\"\nrules:\n\n  - id: VALID_TYPE\n    mess"
  },
  {
    "path": "cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/rule.yml",
    "chars": 378,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: API_GW_DOMA"
  },
  {
    "path": "cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/tests/terraform12/security_policy.tf",
    "chars": 874,
    "preview": "# Test that an api_gateway_domain_name is using TLS 1.2\n# https://www.terraform.io/docs/providers/aws/r/api_gateway_doma"
  },
  {
    "path": "cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/tests/test.yml",
    "chars": 226,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/rule.yml",
    "chars": 1351,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: BATCH_JOB_A"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/tests/terraform12/aws_secrets.tf",
    "chars": 2941,
    "preview": "# Test that AWS secrets are not being used in batch environment variables\n# https://www.terraform.io/docs/providers/aws/"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/tests/test.yml",
    "chars": 215,
    "preview": "---\nversion: 1\ndescription: Terraform 12 test\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: BA"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/rule.yml",
    "chars": 418,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: BATCH_DEFIN"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/terraform11/container_properties_privileged.tf",
    "chars": 2206,
    "preview": "# Pass\nresource \"aws_batch_job_definition\" \"container_properties_privileged_not_set\" {\n  name = \"foo\"\n  type = \"containe"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/terraform12/container_properties_privileged.tf",
    "chars": 2206,
    "preview": "# Pass\nresource \"aws_batch_job_definition\" \"container_properties_privileged_not_set\" {\n  name = \"foo\"\n  type = \"containe"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/test.yml",
    "chars": 240,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/rule.yml",
    "chars": 672,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDFRONT_"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/terraform11/custom_origin_config.tf",
    "chars": 4753,
    "preview": "# Pass\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_not_set\" {\n  enabled = true\n\n  origin {\n    domain_n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/terraform12/custom_origin_config.tf",
    "chars": 5431,
    "preview": "# Test that a cloudfront_distribution resource is using OAI or origin_protocol_policy is using https-only\n# https://www."
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/test.yml",
    "chars": 250,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/rule.yml",
    "chars": 358,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDFRONT_"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/terraform11/logging_config.tf",
    "chars": 2065,
    "preview": "# Pass\nresource \"aws_cloudfront_distribution\" \"logging_enabled\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http:/"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/terraform12/logging_config.tf",
    "chars": 2354,
    "preview": "## Setup Helper\nvariable \"test_domain_s3_location\" {\n  default = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n}\n\nvari"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/test.yml",
    "chars": 244,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/rule.yml",
    "chars": 501,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDFRONT_"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/tests/terraform12/minimum_ssl_protocol.tf",
    "chars": 3899,
    "preview": "# Test that a CloudFront distribution viewer_certificate is using TLS 1.2\n# https://www.terraform.io/docs/providers/aws/"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/tests/test.yml",
    "chars": 212,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/rule.yml",
    "chars": 553,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDFRONT_"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/terraform11/viewer_protocol_policy.tf",
    "chars": 8561,
    "preview": "# Pass\nresource \"aws_cloudfront_distribution\" \"default_cache_behavior_viewer_protocol_policy_set_to_https-only\" {\n  enab"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/terraform12/viewer_protocol_policy.tf",
    "chars": 8753,
    "preview": "## Setup Helper\nvariable \"test_domain_s3_location\" {\n  default = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n}\n\nvari"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/test.yml",
    "chars": 245,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/rule.yml",
    "chars": 332,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDTRAIL_"
  },
  {
    "path": "cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/terraform11/kms_key_id.tf",
    "chars": 674,
    "preview": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucke"
  },
  {
    "path": "cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/terraform12/kms_key_id.tf",
    "chars": 751,
    "preview": "## Setup Helper\nvariable \"test_cloudtrail_name\" {\n  default = \"foo\"\n}\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_r"
  },
  {
    "path": "cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/test.yml",
    "chars": 234,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/cloudwatch/cloudwatch_log_destination_policy/wildcard_principal/rule.yml",
    "chars": 594,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDWATCH_"
  },
  {
    "path": "cli/assets/terraform/aws/cloudwatch/cloudwatch_log_destination_policy/wildcard_principal/tests/terraform12/wildcard_principal.tf",
    "chars": 3246,
    "preview": "# Test that CloudWatch log destination policy is not using a wildcard principal\n# https://www.terraform.io/docs/provider"
  },
  {
    "path": "cli/assets/terraform/aws/cloudwatch/cloudwatch_log_destination_policy/wildcard_principal/tests/test.yml",
    "chars": 212,
    "preview": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: C"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/artifact_encryption/rule.yml",
    "chars": 1067,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CODEBUILD_P"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/artifact_encryption/tests/terraform11/artifact_encryption.tf",
    "chars": 5153,
    "preview": "# Resource required for creating project\nresource \"aws_iam_role\" \"build\" {\n  name = \"build\"\n\n  assume_role_policy = <<EO"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/artifact_encryption/tests/terraform12/artifact_encryption.tf",
    "chars": 5117,
    "preview": "# Resource required for creating project\nresource \"aws_iam_role\" \"build\" {\n  name = \"build\"\n\n  assume_role_policy = <<EO"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/artifact_encryption/tests/test.yml",
    "chars": 251,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/project_encryption/rule.yml",
    "chars": 339,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CODEBUILD_P"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/project_encryption/tests/terraform11/project_encryption.tf",
    "chars": 1858,
    "preview": "# Resource required for creating project\nresource \"aws_iam_role\" \"build\" {\n  name = \"build\"\n\n  assume_role_policy = <<EO"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/project_encryption/tests/terraform12/project_encryption.tf",
    "chars": 1842,
    "preview": "# Resource required for creating project\nresource \"aws_iam_role\" \"build\" {\n  name = \"build\"\n\n  assume_role_policy = <<EO"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/project_encryption/tests/test.yml",
    "chars": 241,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/codepipeline/codepipeline/encryption_key/rule.yml",
    "chars": 445,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CODEPIPELIN"
  },
  {
    "path": "cli/assets/terraform/aws/codepipeline/codepipeline/encryption_key/tests/terraform11/encryption_key.tf",
    "chars": 3072,
    "preview": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  acl = \"private\"\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  as"
  },
  {
    "path": "cli/assets/terraform/aws/codepipeline/codepipeline/encryption_key/tests/terraform12/encryption_key.tf",
    "chars": 3042,
    "preview": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  acl = \"private\"\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  as"
  },
  {
    "path": "cli/assets/terraform/aws/codepipeline/codepipeline/encryption_key/tests/test.yml",
    "chars": 236,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/dms/dms_endpoint/endpoint_kms_key/rule.yml",
    "chars": 333,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AWS_DMS_END"
  },
  {
    "path": "cli/assets/terraform/aws/dms/dms_endpoint/endpoint_kms_key/tests/terraform11/kms_key.tf",
    "chars": 413,
    "preview": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\n# Pass\nresource \"aws_dms_endpoint\" \""
  },
  {
    "path": "cli/assets/terraform/aws/dms/dms_endpoint/endpoint_kms_key/tests/terraform12/kms_key.tf",
    "chars": 618,
    "preview": "## Setup Helper\nvariable \"test_id\" {\n  default = \"foo\"\n}\n\nvariable \"test_engine_type\" {\n  default = \"source\"\n}\n\nvariable"
  },
  {
    "path": "cli/assets/terraform/aws/dms/dms_endpoint/endpoint_kms_key/tests/test.yml",
    "chars": 240,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/audit_logs/rule.yml",
    "chars": 376,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: DOCUMENTDB_"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/audit_logs/tests/terraform12/audit_logs.tf",
    "chars": 1505,
    "preview": "# Test that DocumentDB audit logging is enabled\n# https://www.terraform.io/docs/providers/aws/r/docdb_cluster.html#enabl"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/audit_logs/tests/test.yml",
    "chars": 211,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/storage_encryption/rule.yml",
    "chars": 582,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: DOCUMENTDB_"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/storage_encryption/tests/terraform12/storage_encryption.tf",
    "chars": 2065,
    "preview": "# Test that a DocumentDB cluster has encryption enabled and a KMS key\n# https://www.terraform.io/docs/providers/aws/r/do"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/storage_encryption/tests/test.yml",
    "chars": 322,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami/ebs_block_device_encrypted/rule.yml",
    "chars": 387,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AMI_VOLUMES"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami/ebs_block_device_encrypted/tests/terraform11/ebs_block_device_encrypted.tf",
    "chars": 532,
    "preview": "# Pass\nresource \"aws_ami\" \"ebs_block_device_encrypted_set_to_true\" {\n  name = \"foo\"\n\n  ebs_block_device {\n    device_nam"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami/ebs_block_device_encrypted/tests/terraform12/ebs_block_device_encrypted.tf",
    "chars": 696,
    "preview": "## Setup Helper\nvariable \"test_device\" {\n  default = \"/dev/xvda\"\n}\n\nvariable \"test_volume\" {\n  default = 8\n}\n\n# Pass\nres"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami/ebs_block_device_encrypted/tests/test.yml",
    "chars": 234,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami_copy/encrypted/rule.yml",
    "chars": 340,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AMI_COPY_SN"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami_copy/encrypted/tests/terraform11/encrypted.tf",
    "chars": 528,
    "preview": "# Pass\nresource \"aws_ami_copy\" \"encrypted_set_to_true\" {\n  name              = \"foo\"\n  source_ami_id     = \"ami-xxxxxxxx"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami_copy/encrypted/tests/terraform12/encrypted.tf",
    "chars": 654,
    "preview": "## Setup Helper\nvariable \"test_ami\" {\n  default = \"ami-xxxxxxxx\"\n}\n\nvariable \"test_region\" {\n  default = \"us-east-1\"\n}\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami_copy/encrypted/tests/test.yml",
    "chars": 241,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ebs_volume/encryption/rule.yml",
    "chars": 319,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: EBS_VOLUME_"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ebs_volume/encryption/tests/terraform11/encrypted.tf",
    "chars": 417,
    "preview": "# Pass\nresource \"aws_ebs_volume\" \"encrypted_set_to_true\" {\n  availability_zone = \"us-west-2a\"\n  size              = 20\n "
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ebs_volume/encryption/tests/terraform12/encrypted.tf",
    "chars": 417,
    "preview": "# Pass\nresource \"aws_ebs_volume\" \"encrypted_set_to_true\" {\n  availability_zone = \"us-west-2a\"\n  size              = 20\n "
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ebs_volume/encryption/tests/test.yml",
    "chars": 234,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/instance/ebs_block_device_encrypted/rule.yml",
    "chars": 748,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: EBS_BLOCK_D"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/instance/ebs_block_device_encrypted/tests/terraform12/ebs_block_device_encrypted.tf",
    "chars": 1698,
    "preview": "# Test that EBS block device is using encrpytion and specifies a KMS key\n# https://www.terraform.io/docs/providers/aws/r"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/instance/ebs_block_device_encrypted/tests/test.yml",
    "chars": 327,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/ecr/ecr_repository_policy/wildcard_principal/rule.yml",
    "chars": 535,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ECR_WILDCAR"
  },
  {
    "path": "cli/assets/terraform/aws/ecr/ecr_repository_policy/wildcard_principal/tests/terraform12/wildcard_principal.tf",
    "chars": 1852,
    "preview": "# Test that ECR allow policy is not using a wildcard principal\n# https://www.terraform.io/docs/providers/aws/r/ecr_repos"
  },
  {
    "path": "cli/assets/terraform/aws/ecr/ecr_repository_policy/wildcard_principal/tests/test.yml",
    "chars": 205,
    "preview": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: E"
  },
  {
    "path": "cli/assets/terraform/aws/ecs/ecs_task_definition/task_definition_secrets/rule.yml",
    "chars": 1343,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ECS_ENVIRON"
  },
  {
    "path": "cli/assets/terraform/aws/ecs/ecs_task_definition/task_definition_secrets/tests/terraform11/secrets.tf",
    "chars": 4620,
    "preview": "# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_not_set\" {\n  family                = \"foo\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/ecs/ecs_task_definition/task_definition_secrets/tests/terraform12/secrets.tf",
    "chars": 4620,
    "preview": "# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_not_set\" {\n  family                = \"foo\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/ecs/ecs_task_definition/task_definition_secrets/tests/test.yml",
    "chars": 236,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/efs/efs_file_system/encryption/rule.yml",
    "chars": 314,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: EFS_ENCRYPT"
  },
  {
    "path": "cli/assets/terraform/aws/efs/efs_file_system/encryption/tests/terraform11/encrypted.tf",
    "chars": 321,
    "preview": "# Pass\nresource \"aws_efs_file_system\" \"encrypted_set_to_true\" {\n  creation_token = \"foo\"\n  encrypted      = true\n}\n\n# Fa"
  },
  {
    "path": "cli/assets/terraform/aws/efs/efs_file_system/encryption/tests/terraform12/encrypted.tf",
    "chars": 321,
    "preview": "# Pass\nresource \"aws_efs_file_system\" \"encrypted_set_to_true\" {\n  creation_token = \"foo\"\n  encrypted      = true\n}\n\n# Fa"
  },
  {
    "path": "cli/assets/terraform/aws/efs/efs_file_system/encryption/tests/test.yml",
    "chars": 226,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb/alb_access_logs_enabled/rule.yml",
    "chars": 422,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n  - id: ALB_ACCESS_L"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb/alb_access_logs_enabled/tests/terraform11/access_logs_enabled.tf",
    "chars": 899,
    "preview": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucke"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb/alb_access_logs_enabled/tests/terraform12/access_logs_enabled.tf",
    "chars": 879,
    "preview": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucke"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb/alb_access_logs_enabled/tests/test.yml",
    "chars": 228,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb_listener/alb_listener_https/rule.yml",
    "chars": 518,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ALB_LISTENE"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb_listener/alb_listener_https/tests/terraform11/https.tf",
    "chars": 2801,
    "preview": "# Pass\nresource \"aws_alb_listener\" \"listener_secure_https_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb_listener/alb_listener_https/tests/terraform12/https.tf",
    "chars": 2630,
    "preview": "## Setup Helper\nresource \"aws_vpc\" \"test_vpc\" {\n  cidr_block = \"10.0.0.0/16\"\n}\n\nresource \"aws_acm_certificate\" \"test_cer"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb_listener/alb_listener_https/tests/test.yml",
    "chars": 231,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/elb/access_logs_enabled/rule.yml",
    "chars": 850,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELB_ACCESS_"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/elb/access_logs_enabled/tests/terraform11/access_logs_enabled.tf",
    "chars": 1419,
    "preview": "# Pass\nresource \"aws_elb\" \"access_logs_set\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/elb/access_logs_enabled/tests/terraform12/access_logs_enabled.tf",
    "chars": 1419,
    "preview": "# Pass\nresource \"aws_elb\" \"access_logs_set\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/elb/access_logs_enabled/tests/test.yml",
    "chars": 231,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb/access_logs_enabled/rule.yml",
    "chars": 418,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: LB_ACCESS_L"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb/access_logs_enabled/tests/terraform11/access_logs_enabled.tf",
    "chars": 895,
    "preview": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucke"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb/access_logs_enabled/tests/terraform12/access_logs_enabled.tf",
    "chars": 875,
    "preview": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucke"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb/access_logs_enabled/tests/test.yml",
    "chars": 227,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_https/rule.yml",
    "chars": 518,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ALB_LISTENE"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_https/tests/terraform11/https.tf",
    "chars": 2795,
    "preview": "# Pass\nresource \"aws_lb_listener\" \"listener_secure_https_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-e"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_https/tests/terraform12/https.tf",
    "chars": 2795,
    "preview": "# Pass\nresource \"aws_lb_listener\" \"listener_secure_https_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-e"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_https/tests/test.yml",
    "chars": 231,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_ssl_policy/rule.yml",
    "chars": 676,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ALB_LISTENE"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_ssl_policy/tests/terraform11/ssl_policy.tf",
    "chars": 4594,
    "preview": "# Pass\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2016-08\" {\n  load_balancer_arn = \"arn:aws:elastic"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_ssl_policy/tests/terraform12/ssl_policy.tf",
    "chars": 4594,
    "preview": "# Pass\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2016-08\" {\n  load_balancer_arn = \"arn:aws:elastic"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_ssl_policy/tests/test.yml",
    "chars": 236,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_at_rest/rule.yml",
    "chars": 660,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICACHE"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_at_rest/tests/terraform12/encryption_at_rest.tf",
    "chars": 1514,
    "preview": "# Test that at-rest encryption is enabled with a KMS key\n# https://www.terraform.io/docs/providers/aws/r/elasticache_rep"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_at_rest/tests/test.yml",
    "chars": 329,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_in_transit/rule.yml",
    "chars": 396,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICACHE"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_in_transit/tests/terraform11/encryption_in_transit.tf",
    "chars": 918,
    "preview": "# Pass\nresource \"aws_elasticache_replication_group\" \"transit_encryption_enabled_is_set_to_true\" {\n  replication_group_id"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_in_transit/tests/terraform12/encryption_in_transit.tf",
    "chars": 918,
    "preview": "# Pass\nresource \"aws_elasticache_replication_group\" \"transit_encryption_enabled_is_set_to_true\" {\n  replication_group_id"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_in_transit/tests/test.yml",
    "chars": 243,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_at_rest/rule.yml",
    "chars": 478,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICSEAR"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_at_rest/tests/terraform12/encryption_at_rest.tf",
    "chars": 1075,
    "preview": "# Test for encryption at rest options on an elasticsearch domain\n# https://www.terraform.io/docs/providers/aws/r/elastic"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_at_rest/tests/test.yml",
    "chars": 219,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_node_to_node/rule.yml",
    "chars": 505,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICSEAR"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_node_to_node/tests/terraform12/encryption_node_to_node.tf",
    "chars": 1140,
    "preview": "# Test for node to node encryption for an elasticsearch domain\n# https://www.terraform.io/docs/providers/aws/r/elasticse"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_node_to_node/tests/test.yml",
    "chars": 227,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/vpc_subnets/rule.yml",
    "chars": 331,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICSEAR"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/vpc_subnets/tests/terraform12/elasticsearch_vpc.tf",
    "chars": 765,
    "preview": "# Test that elasticsearch domain is in a VPC using vpc_options\n# https://www.terraform.io/docs/providers/aws/r/elasticse"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/vpc_subnets/tests/test.yml",
    "chars": 207,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/shared/wildcard_principal/rule.yml",
    "chars": 629,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICSEAR"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/shared/wildcard_principal/tests/terraform12/elasticsearch_domain_policy_wildcard_principal.tf",
    "chars": 3167,
    "preview": "# Test that an elasticsearch domain policy is not using a wildcard principal\n# https://www.terraform.io/docs/providers/a"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/shared/wildcard_principal/tests/terraform12/elasticsearch_domain_wildcard_principal.tf",
    "chars": 2846,
    "preview": "# Test that an elasticsearch domain policy is not using a wildcard principal\n# https://www.terraform.io/docs/providers/a"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/shared/wildcard_principal/tests/test.yml",
    "chars": 229,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/elastictranscoder/elastictranscoder_pipeline/require_encryption/rule.yml",
    "chars": 366,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: TRANSCODER_"
  },
  {
    "path": "cli/assets/terraform/aws/elastictranscoder/elastictranscoder_pipeline/require_encryption/tests/terraform12/require_encryption.tf",
    "chars": 1134,
    "preview": "# Test that encryption is enabled\n# https://www.terraform.io/docs/providers/aws/r/elastictranscoder_pipeline.html#aws_km"
  },
  {
    "path": "cli/assets/terraform/aws/elastictranscoder/elastictranscoder_pipeline/require_encryption/tests/test.yml",
    "chars": 212,
    "preview": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: T"
  },
  {
    "path": "cli/assets/terraform/aws/emr/emr_cluster/logging/rule.yml",
    "chars": 313,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AWS_EMR_CLU"
  },
  {
    "path": "cli/assets/terraform/aws/emr/emr_cluster/logging/tests/terraform11/logging.tf",
    "chars": 471,
    "preview": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_emr_cluster\" \"log_uri_is_set\" {\n  name "
  },
  {
    "path": "cli/assets/terraform/aws/emr/emr_cluster/logging/tests/terraform12/logging.tf",
    "chars": 471,
    "preview": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_emr_cluster\" \"log_uri_is_set\" {\n  name "
  },
  {
    "path": "cli/assets/terraform/aws/emr/emr_cluster/logging/tests/test.yml",
    "chars": 236,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/glue/glue_connection/connection_properties/rule.yml",
    "chars": 376,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: GLUE_CONNEC"
  },
  {
    "path": "cli/assets/terraform/aws/glue/glue_connection/connection_properties/tests/terraform12/connection_properties.tf",
    "chars": 824,
    "preview": "# Test that connection_properties is not providing a plaintext password\n# https://www.terraform.io/docs/providers/aws/r/"
  },
  {
    "path": "cli/assets/terraform/aws/glue/glue_connection/connection_properties/tests/test.yml",
    "chars": 216,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_group_membership/group_and_users/rule.yml",
    "chars": 359,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_USER_GR"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_group_membership/group_and_users/tests/terraform11/group_and_users.tf",
    "chars": 858,
    "preview": "## Setup Helper\nresource \"aws_iam_group\" \"test_group\" {\n  name = \"test-group\"\n}\n\nresource \"aws_iam_user\" \"test_user\" {\n "
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_group_membership/group_and_users/tests/terraform12/group_and_users.tf",
    "chars": 838,
    "preview": "## Setup Helper\nresource \"aws_iam_group\" \"test_group\" {\n  name = \"test-group\"\n}\n\nresource \"aws_iam_user\" \"test_user\" {\n "
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_group_membership/group_and_users/tests/test.yml",
    "chars": 227,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_action_wildcard/rule.yml",
    "chars": 530,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_POLICY_"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_action_wildcard/tests/terraform11/policy_action_wildcard.tf",
    "chars": 901,
    "preview": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"201"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_action_wildcard/tests/terraform12/policy_action_wildcard.tf",
    "chars": 901,
    "preview": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"201"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_action_wildcard/tests/test.yml",
    "chars": 239,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notaction/rule.yml",
    "chars": 413,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_POLICY_"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notaction/tests/terraform11/policy_notaction.tf",
    "chars": 606,
    "preview": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_without_notaction\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  "
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notaction/tests/terraform12/policy_notaction.tf",
    "chars": 606,
    "preview": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_without_notaction\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  "
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notaction/tests/test.yml",
    "chars": 234,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notresource/rule.yml",
    "chars": 419,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_POLICY_"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notresource/tests/terraform11/policy_notresource.tf",
    "chars": 588,
    "preview": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_without_notresource\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notresource/tests/terraform12/policy_notresource.tf",
    "chars": 588,
    "preview": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_without_notresource\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notresource/tests/test.yml",
    "chars": 236,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_resource_wildcard/rule.yml",
    "chars": 536,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_POLICY_"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_resource_wildcard/tests/terraform11/policy_resource_wildcard.tf",
    "chars": 881,
    "preview": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_allow_resource_without_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_resource_wildcard/tests/terraform12/policy_resource_wildcard.tf",
    "chars": 881,
    "preview": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_allow_resource_without_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_resource_wildcard/tests/test.yml",
    "chars": 241,
    "preview": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ru"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_action_wildcard/rule.yml",
    "chars": 544,
    "preview": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_ROLE_WI"
  }
]

// ... and 466 more files (download for full content)

About this extraction

This page contains the full source code of the stelligent/config-lint GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 666 files (840.3 KB), approximately 283.5k tokens, and a symbol index with 466 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!