Git Product home page Git Product logo

aws-cloudformation / cloudformation-guard Goto Github PK

View Code? Open in Web Editor NEW
1.3K 41.0 178.0 11.15 MB

Guard offers a policy-as-code domain-specific language (DSL) to write rules and validate JSON- and YAML-formatted data such as CloudFormation Templates, K8s configurations, and Terraform JSON plans/configurations against those rules. Take this survey to provide feedback about cfn-guard: https://amazonmr.au1.qualtrics.com/jfe/form/SV_bpyzpfoYGGuuUl0

License: Apache License 2.0

Rust 93.47% Shell 0.77% Makefile 0.06% C 0.07% Dockerfile 0.09% PowerShell 0.29% JavaScript 1.18% TypeScript 3.53% Python 0.56%
policy-as-code cloudformation terraform k8s policy-rule-evaluation governance security compliance cfn-guard

cloudformation-guard's Issues

[BUG] Cannot properly check a nested field in strict mode

Describe the bug

When running in strict mode, the presence of a nested field is checked only if there is more than one resource in the template.

To Reproduce

I'm running cfn-guard 1.0.0:

$cfn-guard --version
CloudFormation Guard 1.0.0

I have a rule file with this unique rule:

AWS::S3::Bucket BucketEncryption.ServerSideEncryptionConfiguration.*.ServerSideEncryptionByDefault.SSEAlgorithm == AES256

And I have this template:

---
AWSTemplateFormatVersion: 2010-09-09
Resources:
  S3Bucket1:
    Type: 'AWS::S3::Bucket'
    Properties:
      AccessControl: Private

  S3Bucket2:
    Type: 'AWS::S3::Bucket'
    Properties:
      AccessControl: Private
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256

If I run cfn-guard in strict mode, it works as intended, with S3Bucket1 failing the check:

$ cfn-guard check -s --rule_set ../templates_cloudformation/rulesets/general.rules --template ../templates_cloudformation/template-s3a.yml -v
2020-10-15 10:37:21,720 INFO  [cfn_guard] CloudFormation Guard is checking the template '../templates_cloudformation/template-s3a.yml' against the rules in '../templates_cloudformation/rulesets/general.rules'
2020-10-15 10:37:21,722 INFO  [cfn_guard] Loading CloudFormation Template and Rule Set
2020-10-15 10:37:21,723 INFO  [cfn_guard] Parsing rule set
2020-10-15 10:37:21,729 INFO  [cfn_guard] Checking resources
2020-10-15 10:37:21,729 INFO  [cfn_guard] Applying rule 'CompoundRule(
    CompoundRule {
        compound_type: OR,
        raw_rule: "AWS::S3::Bucket BucketEncryption.ServerSideEncryptionConfiguration.*.ServerSideEncryptionByDefault.SSEAlgorithm == AES256",
        rule_list: [
            SimpleRule(
                Rule {
                    resource_type: "AWS::S3::Bucket",
                    field: "BucketEncryption.ServerSideEncryptionConfiguration.0.ServerSideEncryptionByDefault.SSEAlgorithm",
                    operation: Require,
                    value: "AES256",
                    rule_vtype: Value,
                    custom_msg: None,
                },
            ),
        ],
    },
)'
2020-10-15 10:37:21,742 INFO  [cfn_guard] Checking [S3Bucket1] which is of type "AWS::S3::Bucket"
2020-10-15 10:37:21,743 INFO  [cfn_guard] Checking [S3Bucket2] which is of type "AWS::S3::Bucket"
2020-10-15 10:37:21,743 INFO  [cfn_guard] Result: PASS
[S3Bucket1] failed because it does not contain the required property of [BucketEncryption.ServerSideEncryptionConfiguration.0.ServerSideEncryptionByDefault.SSEAlgorithm]
Number of failures: 1

Now, let's I remove S3Bucket2 from the template:

---
AWSTemplateFormatVersion: 2010-09-09
Resources:
  S3Bucket1:
    Type: 'AWS::S3::Bucket'
    Properties:
      AccessControl: Private

  # S3Bucket2:
  #   Type: 'AWS::S3::Bucket'
  #   Properties:
  #     AccessControl: Private
  #     BucketEncryption:
  #       ServerSideEncryptionConfiguration:
  #         - ServerSideEncryptionByDefault:
  #             SSEAlgorithm: AES256

It can be deleted or commented out, it doesn't matter.

If I re-run the same cfn-guard command, it does not check S3Bucket1 at all:

$ cfn-guard check -s --rule_set ../templates_cloudformation/rulesets/general.rules --template ../templates_cloudformation/template-s3a.yml -v
2020-10-15 10:38:45,957 INFO  [cfn_guard] CloudFormation Guard is checking the template '../templates_cloudformation/template-s3a.yml' against the rules in '../templates_cloudformation/rulesets/general.rules'
2020-10-15 10:38:45,959 INFO  [cfn_guard] Loading CloudFormation Template and Rule Set
2020-10-15 10:38:45,960 INFO  [cfn_guard] Parsing rule set
2020-10-15 10:38:45,965 INFO  [cfn_guard] Checking resources
2020-10-15 10:38:45,966 INFO  [cfn_guard] Applying rule 'CompoundRule(
    CompoundRule {
        compound_type: OR,
        raw_rule: "AWS::S3::Bucket BucketEncryption.ServerSideEncryptionConfiguration.*.ServerSideEncryptionByDefault.SSEAlgorithm == AES256",
        rule_list: [],
    },
)'
2020-10-15 10:38:45,971 INFO  [cfn_guard] All CloudFormation resources passed

I don't think this behavior is normal.

license conflicts with license header

Describe the bug
The repo has an apache 2.0 license, but the file headers has content that is not standard apache pre-amble and its unclear if its even related to opensource vs some contractual agreement executed on a party to party basis.

"This AWS Content is provided subject to the terms of the AWS Customer Agreement available at http://aws.amazon.com/agreement or other written agreement between Customer and either Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both."

Disallow Certain Type/Types

Customers may need to be able to disallow entire resource types, including those types of resources that do not have any required properties.

Possible Form:
!AWS::IAM::User

[Enhancement] Maintain default sort order of result based on input ruleset

Is your feature request related to a problem? Please describe.
Currently we are alphabetically sorting result which makes it harder to parse the response if you want to pick first error message. It would be ideal if the response follows same order as the input ruleset.

Describe the solution you'd like
Make sorting optional by passing a sort variable
cfn-guard --rule_set --template -s
(-s is for sorted response).

Describe alternatives you've considered
An alternate to this could be to allow fail fast and return the first error only. While in most cases customers may want to see all the errors at once, there are cases where only first error is needed.

Additional context
None

Create new forms for the inverse of the current wildcards

In other words, where

AWS::IAM::Role AssumeRolePolicyDocument.Statement.*.Principal.Service.* == lambda.amazonaws.com

means "At least one item that matches those wildcards should match that value“

and

AWS::IAM::Role AssumeRolePolicyDocument.Statement.*.Principal.Service.* != lambda.amazonaws.com

means "None of the items that those wildcards match should match that value“

Customers have some use cases where a wildcard syntax that’s equivalent to “All wildcards must match this value” would be very useful.

For example: if customers want the ability to ensure only whitelisted services are included in the trust policy. The only allowed values are specified in a list.

AWS::IAM::Role AssumeRolePolicyDocument.Statement.*.Principal.Service.* ONLY_IN ["lambda.amazonaws.com","ec2.amazonaws.com"]

means “All items must match a value in the list

And if they really want to make sure every wildcarded item only matches an exact value, we could add something like:

AWS::IAM::Role AssumeRolePolicyDocument.Statement.*.Principal.Service.* ONLY lambda.amazon

[BUG] cfn-guard-rulegen output is not consistent

Describe the bug
When I tried to use the tool to generate the ruleset, it is not consistent between the different runs.

To Reproduce
Please supply:

  1. install cfn-guard-rulegen
  2. run against ebs-volume-template.json in the repo

first run

cfn-guard-rulegen ebs-volume-template.json
AWS::EC2::Volume AvailabilityZone == us-west-2b |OR| AWS::EC2::Volume AvailabilityZone == us-west-2c
AWS::EC2::Volume Encrypted == false
AWS::EC2::Volume Size == 99 |OR| AWS::EC2::Volume Size == 101

second run

cfn-guard-rulegen ebs-volume-template.json
AWS::EC2::Volume Encrypted == false
AWS::EC2::Volume AvailabilityZone == us-west-2c |OR| AWS::EC2::Volume AvailabilityZone == us-west-2b
AWS::EC2::Volume Size == 99 |OR| AWS::EC2::Volume Size == 101

NOTE: Please be sure that the templates, rule sets and logs you provide as part of your bug report do not contain any sensitive information.

Expected behavior
Should always produce the same ruleset in order.

Add the ability to do conditional checks based on properties

Customers may need to check things conditionally, based on the values of other properties.

Here's a basic form that could suffice:

<AWS RESOURCE TYPE> IF <address> ==|=!|IN|NOT_IN THEN <address> ==|=!|IN|NOT_IN <VALUE> ... AND|OR ...

Where address is a dotted notation that starts from the root of the template (or whatever form the structured data is in) and can be absolute:

Template.Resources.<RESOURCE NAME>|*.Properties.<PROP NAME>.<PROP NAME>|*... == <VALUE>

or relative to the root of the type under inspection by prefacing the address with a ".":

.Properties.<PROP NAME>.<PROP NAME>|*... == <VALUE>

Release Best Practices Rules

I think it is a lot of effort to write custom rules set and even you will miss some best practices or security settings.

I really looking forward that AWS releases a set of best practices and security-relevant rules sets, for example

  • Encryption of all data at rest
  • well-architected rules
  • RDS not started in a single AZ

For many of these best practices, AWS has already services in place which check these rules after or during deployment like Trusted Advisor and AWS Config. My hope AWS use their knowledge to transfer these into rules, so we as the customer can validate our template before we deploy Cloudformation stacks.

JSON/YAML discrepancy when parsing functions

Hi,

I have a policy to check for specific tags like:

AWS::ElasticLoadBalancingV2::LoadBalancer Tags.0 == {"Key":"EnvironmentType","Value":"EnvironmentType"}
AWS::ElasticLoadBalancingV2::LoadBalancer Tags.1 == {"Key":"OwnerContact","Value":"OwnerContact"}

They work fine on YAMLs in format:

        - Key: EnvironmentType
          Value: !Ref EnvironmentType
        - Key: OwnerContact
          Value: !Ref OwnerContact

However they fail on JSON:

          { "Key" : "EnvironmentType", "Value" : { "Ref" : "EnvironmentType" } },
          { "Key" : "OwnerContact", "Value" : { "Ref" : "OwnerContact" } },

with the error:

"[Sg] failed because [Tags.0] is [{\"Key\":\"EnvironmentType\",\"Value\":{\"Ref\":\"EnvironmentType\"}}] and the permitted value is [{\"Key\":\"EnvironmentType\",\"Value\":\"EnvironmentType\"}]"
"[Sg] failed because [Tags.1] is [{\"Key\":\"OwnerContact\",\"Value\":{\"Ref\":\"OwnerContact\"}}] and the permitted value is [{\"Key\":\"OwnerContact\",\"Value\":\"OwnerContact\"}]"

What would be the proper way of matching both formats?

//Edit

Using IN [{"Key":"OwnerContact","Value":"OwnerContact"},{"Key":"OwnerContact","Value":{"Ref":"OwnerContact"}}] doesnt work either with the error:

"[Alb] failed because [{\"Key\":\"OwnerContact\",\"Value\":\"OwnerContact\"}] is not in [{\"Key\":\"OwnerContact\",\"Value\":\"OwnerContact\"},{\"Key\":\"OwnerContact\",\"Value\":{\"Ref\":\"OwnerContact\"}}] for [Tags.1]"

However I believe this should be working as the matching object is in the list. (I tried with [[{...}],[{...}]] same result)

//Edit2:

== /^\{"Key":"OwnerContact","Value":"OwnerContact"\}$|^\{"Key":"OwnerContact","Value":\{"Ref":"OwnerContact"\}\}$/
This seems to work, but I highly doubt this is how it's intended to work

Add inline "AND" form

Customers may need to have explicit in-line "AND's" (as opposed to the implicit AND's that are already part of a ruleset) for clarity:

AWS::EC2::SecurityGroup SecurityGroupIngress.*.ToPort != 3306 |AND| SecurityGroupIngress.*.CidrIp != 0.0.0.0/0

[Enhancement] Run cfn-guard check without parameters

Is your feature request related to a problem? Please describe.
Currently you always have to specify both the ruleset and the templates. This makes scanning mulitple templates in one source code repository, or templates across different repositories difficult (see also #22 )

Describe the solution you'd like
Having a default filename for the rules and allowing a list of templates as input, would make it a lot easier to run. eg.

cfn-guard check template1.yaml template2.yaml
cfn-guard check template*.yaml
cfn-guard check --rule_set non-default-rules *.yaml

Simple regex seeking public CIDR not working

Using the public website to evaluate rust regexp (https://rustexp.lpil.uk/), I found i could use "CidrIp":\s*"0.0.0.0/0" to find public IP

But upon placing and then running tests against a security group, it rendered false positives

Test Case snippet of CFN
"Resources": {
"WebLoadBalancerSG": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"VpcId": {
"Ref": "VPCid"
},
"GroupDescription": "WS Load Balancer Security Group",
"Tags": [
{
"Key": "Environment",
"Value": {
"Ref": "Environment"
}
},
{
"Key": "Application",
"Value": {
"Ref": "Application"
}
},
{
"Key": "Vertical",
"Value": {
"Ref": "Vertical"
}
},
{
"Key": "Project",
"Value": {
"Ref": "Project"
}
}
],
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "80",
"CidrIp": "10.0.0.1/8"
},
{
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "80",
"CidrIp": "192.168.0.0/16"
},
{
"IpProtocol": "tcp",
"FromPort": "443",
"ToPort": "443",
"CidrIp": "0.0.0.1/0"
},
{
"IpProtocol": "tcp",
"FromPort": "443",
"ToPort": "443",
"CidrIp": "192.168.0.0/16"
},
{
"IpProtocol": "tcp",
"FromPort": "3389",
"ToPort": "3389",
"CidrIp": "10.0.0.1/8"
},
{
"IpProtocol": "tcp",
"FromPort": "3389",
"ToPort": "3389",
"CidrIp": "192.168.0.0/16"
}
]
}
}
},

Rule
AWS::EC2::SecurityGroup SecurityGroupIngress == /"CidrIp":"0.0.0.0/0"/ << Publicly accessible Security Group

Produces,

cfn-guard --template fake.json --rule_set tt.txt
[WebLoadBalancerSG] failed because [SecurityGroupIngress] is [[{"CidrIp":"10.0.0.1/8","FromPort":"80","IpProtocol":"tcp","ToPort":"80"},{"CidrIp":"192.168.0.0/16","FromPort":"80","IpProtocol":"tcp","ToPort":"80"},{"CidrIp":"0.0.0.1/0","FromPort":"443","IpProtocol":"tcp","ToPort":"443"},{"CidrIp":"192.168.0.0/16","FromPort":"443","IpProtocol":"tcp","ToPort":"443"},{"CidrIp":"10.0.0.1/8","FromPort":"3389","IpProtocol":"tcp","ToPort":"3389"},{"CidrIp":"192.168.0.0/16","FromPort":"3389","IpProtocol":"tcp","ToPort":"3389"}]] and Publicly accessible Security Group
Number of failures: 1

Use serde::rename to fix `strict_check` casing in cfn-guard-lambda

From

#[derive(Deserialize, Debug)]
struct CustomEvent {
    #[serde(rename = "template")]
    template: String,
    #[serde(rename = "ruleSet")]
    rule_set: String,
    strict_checks: bool,
}
```
To

```
#[derive(Deserialize, Debug)]
struct CustomEvent {
    #[serde(rename = "template")]
    template: String,
    #[serde(rename = "ruleSet")]
    rule_set: String,
    #[serde(rename = "strictChecks")]
    strict_checks: bool,
}
```

NOTE:  This requires updates to the Makefile and marks a breaking change in the lambda's API.

[Enhancement] Publish to crates.io via travisci

Uploading to crates.io from desktop is error prone. We need a step in our deployment script on travis to do this for each package automatically. Since there are multiple crates in this repo, we need something a bit more sophisticated than just running from the root. Additionally, it would nice to keep a common versioning for all the tools as cfn-guard depends on rulegen and the lambdas depend on their respective tools as well.

[BUG] Installation Failing on CodeBuild

Describe the bug

While trying to use cfn-guard with CodeBuild, I was facing issues running 'make' command. I had tried to follow this blog post by AWS.

https://aws.amazon.com/blogs/security/integrating-aws-cloudformation-security-tests-with-aws-security-hub-and-aws-codebuild-reports/

To Reproduce

To reproduce the same, a CodeBuild Project is required with build image aws/codebuild/amazonlinux2-x86_64-standard:3.0 with compute 3 GB memory, 2 vCPUs.

Buildspec Install Phase :

install:
commands:
- export date=date +%Y-%m-%dT%H:%M:%S.%NZ
- echo Installing cloudformation-guard
- yum install glibc-devel gcc git make jq -y
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
- git clone https://github.com/aws-cloudformation/cloudformation-guard.git --depth 1 /usr/local/cloudformation-guard
- source $HOME/.cargo/env
- (cd /usr/local/cloudformation-guard && make )
- export PATH=$PATH:/usr/local/cloudformation-guard/bin/

The command in bold is failing.

ERROR CodeBuild LOG:

[Container] 2020/10/05 09:30:36 Running command (cd /usr/local/cloudformation-guard && make )
69 | cd cfn-guard; cargo build --release; cp target/release/cfn-guard ../bin
70 | Updating crates.io index
71 | Downloading crates ...
72 | Downloaded jni v0.14.0
73 | Downloaded combine v3.8.1
74 | Downloaded vec_map v0.8.2
75 | Downloaded byteorder v1.3.4
76 | Downloaded dtoa v0.4.6
77 | Downloaded simple_logger v1.9.0
78 | Downloaded unreachable v1.0.0
79 | Downloaded cesu8 v1.1.0
80 | Downloaded same-file v1.0.6
81 | Downloaded textwrap v0.11.0
82 | Downloaded yaml-rust v0.4.4
83 | Downloaded itoa v0.4.6
84 | Downloaded quote v1.0.7
85 | Downloaded object v0.20.0
86 | Downloaded serde_json v1.0.57
87 | Downloaded aho-corasick v0.7.13
88 | Downloaded syn v1.0.41
89 | Downloaded regex-syntax v0.6.18
90 | Downloaded clap v2.33.3
91 | Downloaded libc v0.2.77
92 | Downloaded gimli v0.22.0
93 | Downloaded regex v1.3.9
94 | Downloaded serde v1.0.116
95 | Downloaded chrono v0.4.15
96 | Downloaded proc-macro2 v1.0.21
97 | Downloaded memchr v2.3.3
98 | Downloaded linked-hash-map v0.5.3
99 | Downloaded lazy_static v1.4.0
100 | Downloaded error-chain v0.12.4
101 | Downloaded colored v1.9.3
102 | Downloaded cfg-if v0.1.10
103 | Downloaded bitflags v1.2.1
104 | Downloaded ansi_term v0.11.0
105 | Downloaded adler v0.2.3
106 | Downloaded addr2line v0.13.0
107 | Downloaded walkdir v2.3.1
108 | Downloaded void v1.0.2
109 | Downloaded version_check v0.9.2
110 | Downloaded unicode-width v0.1.8
111 | Downloaded time v0.1.44
112 | Downloaded strsim v0.8.0
113 | Downloaded serde_derive v1.0.116
114 | Downloaded ryu v1.0.5
115 | Downloaded rustc-demangle v0.1.16
116 | Downloaded num-traits v0.2.12
117 | Downloaded backtrace v0.3.50
118 | Downloaded ascii v0.9.3
119 | Downloaded jni-sys v0.3.0
120 | Downloaded num-integer v0.1.43
121 | Downloaded miniz_oxide v0.4.2
122 | Downloaded log v0.4.11
123 | Downloaded either v1.6.1
124 | Downloaded atty v0.2.14
125 | Downloaded unicode-xid v0.2.1
126 | Downloaded thread_local v1.0.1
127 | Downloaded serde_yaml v0.8.13
128 | Downloaded autocfg v1.0.1
129 | Compiling autocfg v1.0.1
130 | Compiling libc v0.2.77
131 | Compiling proc-macro2 v1.0.21
132 | Compiling unicode-xid v0.2.1
133 | Compiling syn v1.0.41
134 | Compiling cfg-if v0.1.10
135 | Compiling memchr v2.3.3
136 | Compiling serde_derive v1.0.116
137 | Compiling serde v1.0.116
138 | Compiling log v0.4.11
139 | Compiling lazy_static v1.4.0
140 | Compiling adler v0.2.3
141 | Compiling ryu v1.0.5
142 | Compiling byteorder v1.3.4
143 | Compiling gimli v0.22.0
144 | Compiling version_check v0.9.2
145 | Compiling bitflags v1.2.1
146 | Compiling same-file v1.0.6
147 | Compiling serde_json v1.0.57
148 | Compiling linked-hash-map v0.5.3
149 | Compiling object v0.20.0
150 | Compiling void v1.0.2
151 | Compiling unicode-width v0.1.8
152 | Compiling rustc-demangle v0.1.16
153 | Compiling strsim v0.8.0
154 | Compiling itoa v0.4.6
155 | Compiling ascii v0.9.3
156 | Compiling either v1.6.1
157 | Compiling vec_map v0.8.2
158 | Compiling ansi_term v0.11.0
159 | Compiling dtoa v0.4.6
160 | Compiling regex-syntax v0.6.18
161 | Compiling jni-sys v0.3.0
162 | Compiling cesu8 v1.1.0
163 | Compiling num-traits v0.2.12
164 | Compiling num-integer v0.1.43
165 | Compiling miniz_oxide v0.4.2
166 | Compiling thread_local v1.0.1
167 | Compiling error-chain v0.12.4
168 | Compiling addr2line v0.13.0
169 | Compiling walkdir v2.3.1
170 | Compiling yaml-rust v0.4.4
171 | Compiling unreachable v1.0.0
172 | Compiling textwrap v0.11.0
173 | Compiling jni v0.14.0
174 | Compiling atty v0.2.14
175 | Compiling time v0.1.44
176 | Compiling aho-corasick v0.7.13
177 | Compiling quote v1.0.7
178 | Compiling combine v3.8.1
179 | Compiling colored v1.9.3
180 | Compiling clap v2.33.3
181 | Compiling backtrace v0.3.50
182 | Compiling regex v1.3.9
183 | Compiling chrono v0.4.15
184 | Compiling simple_logger v1.9.0
185 | Compiling serde_yaml v0.8.13
186 | Compiling cfn-guard-rulegen v1.0.0 (/usr/local/cloudformation-guard/cfn-guard-rulegen)
187 | Compiling cfn-guard v1.0.0 (/usr/local/cloudformation-guard/cfn-guard)
188 | warning: `extern` fn uses type `str`, which is not FFI-safe
189 | --> cfn-guard/src/lib.rs:54:29
190 | \|
191 | 54 \|     template_file_contents: &str,
192 | \|                             ^^^^ not FFI-safe
193 | \|
194 | = note: `#[warn(improper_ctypes_definitions)]` on by default
195 | = help: consider using `*const u8` and a length instead
196 | = note: string slices have no C equivalent
197 |  
198 | warning: `extern` fn uses type `str`, which is not FFI-safe
199 | --> cfn-guard/src/lib.rs:55:26
200 | \|
201 | 55 \|     rules_file_contents: &str,
202 | \|                          ^^^^ not FFI-safe
203 | \|
204 | = help: consider using `*const u8` and a length instead
205 | = note: string slices have no C equivalent
206 |  
207 | warning: `extern` fn uses type `std::result::Result<(std::vec::Vec<std::string::String>, usize), std::string::String>`, which is not FFI-safe
208 | --> cfn-guard/src/lib.rs:57:6
209 | \|
210 | 57 \| ) -> Result<(Vec<String>, usize), String> {
211 | \|      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
212 | \|
213 | = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
214 | = note: enum has no representation hint
215 |  
216 | warning: 3 warnings emitted
217 |  
218 | warning: use of deprecated item 'simple_logger::init_with_level': Please use the Builder pattern instead.
219 | --> cfn-guard/src/main.rs:78:9
220 | \|
221 | 78 \|         simple_logger::init_with_level(log_level).unwrap();
222 | \|         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
223 | \|
224 | = note: `#[warn(deprecated)]` on by default
225 |  
226 | warning: use of deprecated item 'simple_logger::init_with_level': Please use the Builder pattern instead.
227 | --> cfn-guard/src/main.rs:103:13
228 | \|
229 | 103 \|             simple_logger::init_with_level(log_level).unwrap();
230 | \|             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
231 |  
232 | warning: 2 warnings emitted
233 |  
234 | Finished release [optimized] target(s) in 2m 59s
235 | cp: cannot stat 'target/release/cfn-guard': No such file or directory
236 | make: *** [cfn-guard] Error 1
237 |  
238 | [Container] 2020/10/05 09:33:36 Command did not exit successfully (cd /usr/local/cloudformation-guard && make ) exit status 2

Expected behavior
It should have been installed.

Operating System:
Amazon Linux

[Enhancement] Add a way to enforce encryption of EC2 instances in Block mappings

Maybe it is already supported and I just haven't configured it properly but is there a way to enforce encryption of volumes declared as in the block device mappings of an ec2 instance?

I tried this but assume it is because BlockDeviceMappings is a list object so it would need to iterate over every entry in list.

I didn't see an example of the syntax if it was supported. If it is not supported, can this be added to the roadmap?

AWS::EC2::Instance == BlockDeviceMappings.Ebs.Encrypted == true

I could always use the aws::ec2::volumeattachment resource after creating the volume but it would not fix the problem for the root volume

[BUG] Cfn-guard incorrectly passes the template that fails the rules file

Describe the bug
If I write a # comment in the same line as an existing rule, cfn-guard doesn't process that rule.

To Reproduce
An example rule set and template that results in the error
ebs-volume-template.zip
The commands you used to invoke the tool: cfn-guard -t ebs-volume-template.json -r ebs-volume-template.ruleset

Expected behavior
Expect cfn-guard to either process that rule or show an error that a comment and a rule cannot be on the same line.

Operating System:
Windows 10

Add a Comment Form

Customers may want to add documentation to their rule sets in the form of comments within the rule set itself.

Proposed form:

# This is a comment

Support JSON-string formatted objects

Given the rule:

AWS::IAM::Role AssumeRolePolicyDocument.Statement.*.Principal.Service.* == lambda.amazonaws.com

The following template correctly flags an issue:

Resources:
  LambdaRoleHelper:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - notlambda.amazonaws.com

However if I format the template to perform the same function, but using the JSON syntax like so:

Resources:
  LambdaRoleHelper:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument: |
        {
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "notlambda.amazonaws.com"
                ]
              }
            }
          ]
        }

The ruleset does not pick up on the violation.

Check existence and values of specific tag keys

Hi,

Is there away to enforce specific tags to be set, ideally with specific values?

AWS::EC2::EIP Tags == [{"Key":"Foo","Value":"abc"},{"Key":"Bar","Value":"def"}]

would fail if the resource has additional Tags

[Enhancement] Add rules to block entire Resource/Service.

Is your feature request related to a problem? Please describe.
In our team we manage our AWS implementation and we don't want our developers to deploy certain services/resources. We want to use cfn-guard for this, to give quick feedback to our devs. However, we don't have the possibility to block an entire resource in Cloudformation (I'll use EC2 as an example) through cfn-guard.

Describe the solution you'd like
I want to be able to have a rule like AWS::EC2 == false.

Describe alternatives you've considered
As an alternative, we've worked with rules like AWS:: EC2::Instance InstanceType == false. This works, but does not give the proper feedback, and is more of a workaround.

Looking for help: S3 Bucket Policy

Hi,

Looking for some help.
Trying to setup cfn-guard to parse S3 BucketPolicy to disallow public write. Is cfn-guard able to parse the S3 BucketPolicy, looking for s3:pubobject or s3:* on principal: "*" ?

I tried playing with regex and json in the ruleset file, but I can't get it to work. Any help is appreciated.

Thank you.

Support numeric comparison operators

I'd like to test "less than or equals to" for numeric values. This allows us to test that an ECS task is compliant to resource budgets.

e.g.

let allowed_max_memory = 512

AWS::ECS::TaskDefinition Memory <= %allowed_max_memory

[Enhancement] cfn-guard-rulegen for AWS::IAM::Role without a name is missing a rule

Is your feature request related to a problem? Please describe.
I have existing templates were the AWS::IAM::Role Name attribute is NOT present, this is because we do not want roles to be named inside the template. We prefer that CFN generate the name of the role automatically based on the stack AND making it unique, etc.

Describe the solution you'd like
I would like to see cfn-guard-rulegen detect that there is no name attribute present and make a rule to NOT allow the name to be defined.

AWS::IAM::Role Name == ''

or some such. Sorry, I'm a bit of a newbie using the tool, and this looked like the only way to express the 'NOT present'

Describe alternatives you've considered
Obviously hand coding the rule AFTER cfn-guard-rulegen is executed. However, that is a bit of a pain on 100+ CFN templates.

[BUG]: Compilation is broken for Linux/Ubuntu with make

Describe the bug
After make compilation there is no cfn-guard binary that can be used and this breaks the CI. Before worked just fine.

   Compiling cfn-guard v1.0.0 (/codebuild/output/src853229193/src/cloudformation-guard/cfn-guard)
warning: use of deprecated item 'simple_logger::init_with_level': Please use the Builder pattern instead.
  --> cfn-guard-rulegen/src/main.rs:31:5
   |
31 |     simple_logger::init_with_level(log_level).unwrap();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(deprecated)]` on by default

warning: use of deprecated item 'simple_logger::init_with_level': Please use the Builder pattern instead.
  --> cfn-guard/src/main.rs:78:9
   |
78 |         simple_logger::init_with_level(log_level).unwrap();
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(deprecated)]` on by default

warning: use of deprecated item 'simple_logger::init_with_level': Please use the Builder pattern instead.
   --> cfn-guard/src/main.rs:103:13
    |
103 |             simple_logger::init_with_level(log_level).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   Compiling lambda_runtime v0.2.1
   Compiling cfn-guard-rulegen-lambda v1.0.0 (/codebuild/output/src853229193/src/cloudformation-guard/cfn-guard-rulegen-lambda)
warning: use of deprecated item 'simple_logger::init_with_level': Please use the Builder pattern instead.
  --> cfn-guard-rulegen-lambda/src/main.rs:18:5
   |
18 |     simple_logger::init_with_level(log::Level::Info)?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(deprecated)]` on by default

   Compiling cfn-guard-lambda v1.0.0 (/codebuild/output/src853229193/src/cloudformation-guard/cfn-guard-lambda)
warning: use of deprecated item 'simple_logger::init_with_level': Please use the Builder pattern instead.
  --> cfn-guard-lambda/src/main.rs:30:5
   |
30 |     simple_logger::init_with_level(log::Level::Info)?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(deprecated)]` on by default

    Finished release [optimized] target(s) in 5m 45s

To Reproduce

version: 0.2
env:
  variables:
    # Definining CloudFormation Teamplate and Ruleset as variables - part of the code repo
    CF_TEMPLATE: "cfn_template_file_example.yaml"
    CF_ORG_RULESET:  "cfn_guard_ruleset_example"
phases:
  install:
    commands:
      - apt-get update
      - apt-get install build-essential -y
      - apt-get install cargo -y
      - apt-get install git -y
  pre_build:
    commands:
      - echo "Setting up the environment for AWS CloudFormation Guard"
      - echo "More info https://github.com/aws-cloudformation/cloudformation-guard"
      - echo "Install Rust"
      - curl https://sh.rustup.rs -sSf | sh -s -- -y
      - echo "Clone code from AWS CloudFormation Guard github repo"
      - git clone https://github.com/aws-cloudformation/cloudformation-guard
  build:
    commands:
       - echo "Compiling Latest AWS CloudFormation Guard release"
       - echo "More info https://github.com/aws-cloudformation/cloudformation-guard#using-the-makefile"
       - cd cloudformation-guard
       - make
  post_build:
    commands:
        - cd .. 
        - echo "Validate CloudFormation template with cfn-guard tool"
        - echo "More information https://github.com/aws-cloudformation/cloudformation-guard/blob/master/cfn-guard/README.md"
        - cloudformation-guard/bin/cfn-guard check --rule_set $CF_ORG_RULESET --template $CF_TEMPLATE --strict-checks

Pre-built binaries

It would be great if the project host pre-built binaries for major OS platforms through the GitHub release.

(and if it provides additional support for Homebrew installation for macOS, I'm going to dance with it ❤️)

[Enhancement] Create example explaining that strict checking means missing resources properties and expected error.

When I started using cfn-guard (by the way, love the tool!), it took awhile to figure out if it would fail if a required resource property was completely missing. It's not completely obvious (well, to me at least) that "strict" equates to missing CloudFormation resource properties.

The Readme.md file is not easy to parse, and it took a few times reading through it to understand how the tool works. I actually figured out how strict works from a closed issue request.

I think it would help tremendously to include an example where (and why) strict is used, and what the output looks like for a failure.

For instance, I wanted to validate an EC2 Launch Template where EBS encryption is mandated.

AWSTemplateFormatVersion: "2010-09-09"
Resources:
    SampleLaunchTemplate:
        Type: "AWS::EC2::LaunchTemplate"
        Properties:
            LaunchTemplateData:
                BlockDeviceMappings:
                    Ebs:
                        Encrypted: true

with a ruleset file such as...

let encryption_flag = true

AWS::EC2::LaunchTemplate LaunchTemplateData.BlockDeviceMappings.Ebs.Encrypted == %encryption_flag

For the example, something like...

"In the example above, use strict (-s) to determine if a resource property is completely missing, such as removing the BlockDeviceMappings section". See below.

AWSTemplateFormatVersion: "2010-09-09"
Resources:
    SampleLaunchTemplate:
        Type: "AWS::EC2::LaunchTemplate"
        Properties:
            LaunchTemplateData:

Sample command:

>cargo run -- -t ec2launchtemplate-template.yaml -r ec2launchtemplate.ruleset -s

Sample result

[SampleLaunchTemplate] failed because it does not contain the required property of [BlockDeviceMappings]
Number of failures: 1

Many thanks!

[Enhancement] Support different output formats (e.g. JSON)

Is your feature request related to a problem? Please describe.
Currently, it is hard to parse the output of cloudformation-guard using other scripts since the output is in text only, having different output formats would help ingest that output to different tools.

Describe the solution you'd like
It will be nice to have an option like cfn-lint/cfn_nag/cfripper/tflint/tfsec/terrascan/checkov that allows the user to specify different output options

Describe alternatives you've considered
Current solution is to parse the text output which is not ideal

[BUG] Running on CentOS 7

Describe the bug
Running cfn-guard in a CentOS 7 container produces

[root@6ef65b9fa037 /]# cfn-guard 
cfn-guard: /lib64/libc.so.6: version `GLIBC_2.18' not found (required by cfn-guard)

To Reproduce
Run using

FROM docker.io/centos:7

ENV TOOL_VERSION_CFN_GUARD="1.0.0"

RUN curl -L https://github.com/aws-cloudformation/cloudformation-guard/releases/download/${TOOL_VERSION_CFN_GUARD}/cfn-guard-linux-${TOOL_VERSION_CFN_GUARD}.tar.gz \
      -o cfn-guard-linux-${TOOL_VERSION_CFN_GUARD}.tar.gz \
    && tar -zxf cfn-guard-linux-${TOOL_VERSION_CFN_GUARD}.tar.gz \
    && mv cfn-guard-linux/cfn-guard /usr/local/bin/cfn-guard

Expected behavior
For cfn-guard to run without error

Screenshots
n/a

Operating System:
CentOS

OS Version
7

Additional context
Running with the following works fine

FROM docker.io/centos:8

ENV TOOL_VERSION_CFN_GUARD="1.0.0"

RUN curl -L https://github.com/aws-cloudformation/cloudformation-guard/releases/download/${TOOL_VERSION_CFN_GUARD}/cfn-guard-linux-${TOOL_VERSION_CFN_GUARD}.tar.gz \
      -o cfn-guard-linux-${TOOL_VERSION_CFN_GUARD}.tar.gz \
    && tar -zxf cfn-guard-linux-${TOOL_VERSION_CFN_GUARD}.tar.gz \
    && mv cfn-guard-linux/cfn-guard /usr/local/bin/cfn-guard

[BUG] Beginning and end of line Regex characters aren't working

Describe the bug
The ^ and & characters, which are used in Regex to denote the beginning and end of the line, don't seem to be recognised as valid, and a regex check using these characters doesn't match anything.
When testing my regex, I'm using the pattern ^([0-9]|[A-Z]){4}$, which should match any 4 digits or capitalised characters, such as 1234, however when running the test I get the following error:

[EC2Instance2] failed because [Tags.0.Value] is [1234] and the permitted pattern is [^([0-9]|[A-Z]){4}$]

If I remove the ^ and $ the regex matches, however it doesn't do an exact match for exactly 4 characters, so 12345 would then show as valid, as it contains 4 in a row, but with no defined end.

CloudFormation Guard 0.5.2

To Reproduce
Please supply:

  1. An example rule set and template that results in the error

Rule Set:

AWS::EC2::Instance Tags.0.Key == /(TestKey)/ AWS::EC2::Instance Tags.0.Value == /^([0-9]|[A-Z]){4}$/

Template:

{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "AWS CloudFormation Sample Template EC2InstanceWithSecurityGroupSample: Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based on the region in which the stack is run. This example creates an EC2 security group for the instance to give you SSH access. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.",

  "Resources" : {
    "EC2Instance2" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "InstanceType" : { "Ref" : "InstanceType" },
        "SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
        "KeyName" : { "Ref" : "KeyName" },
        "Tags": [
          {
            "Key": "TestKey",
            "Value": "1234"
          }
        ],
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                          { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] }
      }
    }
  }
}
  1. The commands you used to invoke the tool
    cfn-guard -vvv -t tests/test_failure/ec2.json -r cfn-test.cfn
  2. The output of a -vvv log level if it's not related to cfn-guard-lambda, or the relevant CloudWatch log messages if it is related to the lambda
2020-07-30 12:13:33,102 DEBUG [cfn_guard] Parameters are ArgMatches {
    args: {
        "rule_set": MatchedArg {
            occurs: 1,
            indices: [
                7,
            ],
            vals: [
                "cfn-test.cfn",
            ],
        },
        "template": MatchedArg {
            occurs: 1,
            indices: [
                5,
            ],
            vals: [
                "tests/test_failure/ec2.json",
            ],
        },
        "v": MatchedArg {
            occurs: 3,
            indices: [
                1,
                2,
                3,
            ],
            vals: [],
        },
    },
    subcommand: None,
    usage: Some(
        "USAGE:\n    cfn-guard [FLAGS] --rule_set <RULE_SET_FILE> --template <TEMPLATE_FILE>",
    ),
}
2020-07-30 12:13:33,103 INFO  [cfn_guard] CloudFormation Guard is checking the template 'tests/test_failure/ec2.json' against the rules in 'cfn-test.cfn'
2020-07-30 12:13:33,103 DEBUG [cfn_guard] Entered run
2020-07-30 12:13:33,103 TRACE [cfn_guard] Template file is 'tests/test_failure/ec2.json' and its contents are:
'{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "AWS CloudFormation Sample Template EC2InstanceWithSecurityGroupSample: Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based on the region in which the stack is run. This example creates an EC2 security group for the instance to give you SSH access. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.",

  "Resources" : {
    "EC2Instance2" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "InstanceType" : { "Ref" : "InstanceType" },
        "SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
        "KeyName" : { "Ref" : "KeyName" },
        "Tags": [
          {
            "Key": "TestKey",
            "Value": "1234"
          }
        ],
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                          { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] }
      }
    }
  }
}'
2020-07-30 12:13:33,103 TRACE [cfn_guard] Rules file is 'cfn-test.cfn' and its contents are: 

AWS::EC2::Instance Tags.0.Key == /(TestKey)/
AWS::EC2::Instance Tags.0.Value == /^([0-9]|[A-Z]){4}$/



2020-07-30 12:13:33,104 INFO  [cfn_guard] Loading CloudFormation Template and Rule Set
2020-07-30 12:13:33,104 DEBUG [cfn_guard] Entered run_check
2020-07-30 12:13:33,104 TRACE [cfn_guard] Normalizing booleans in inputs
2020-07-30 12:13:33,104 TRACE [cfn_guard] Cleaned template contents are:
'{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "AWS CloudFormation Sample Template EC2InstanceWithSecurityGroupSample: Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based on the region in which the stack is run. This example creates an EC2 security group for the instance to give you SSH access. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.",

  "Resources" : {
    "EC2Instance2" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "InstanceType" : { "Ref" : "InstanceType" },
        "SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
        "KeyName" : { "Ref" : "KeyName" },
        "Tags": [
          {
            "Key": "TestKey",
            "Value": "1234"
          }
        ],
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                          { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] }
      }
    }
  }
}'


2020-07-30 12:13:33,104 TRACE [cfn_guard] Cleaned rules file contents are:
'

AWS::EC2::Instance Tags.0.Key == /(TestKey)/
AWS::EC2::Instance Tags.0.Value == /^([0-9]|[A-Z]){4}$/


'
2020-07-30 12:13:33,104 DEBUG [cfn_guard] Deserializing CloudFormation template
2020-07-30 12:13:33,104 TRACE [cfn_guard] CFN Template is '{
    "Description": String(
        "AWS CloudFormation Sample Template EC2InstanceWithSecurityGroupSample: Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based on the region in which the stack is run. This example creates an EC2 security group for the instance to give you SSH access. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.",
    ),
    "Resources": Object({
        "EC2Instance2": Object({
            "Properties": Object({
                "ImageId": Object({
                    "Fn::FindInMap": Array([
                        String(
                            "AWSRegionArch2AMI",
                        ),
                        Object({
                            "Ref": String(
                                "AWS::Region",
                            ),
                        }),
                        Object({
                            "Fn::FindInMap": Array([
                                String(
                                    "AWSInstanceType2Arch",
                                ),
                                Object({
                                    "Ref": String(
                                        "InstanceType",
                                    ),
                                }),
                                String(
                                    "Arch",
                                ),
                            ]),
                        }),
                    ]),
                }),
                "InstanceType": Object({
                    "Ref": String(
                        "InstanceType",
                    ),
                }),
                "KeyName": Object({
                    "Ref": String(
                        "KeyName",
                    ),
                }),
                "SecurityGroups": Array([
                    Object({
                        "Ref": String(
                            "InstanceSecurityGroup",
                        ),
                    }),
                ]),
                "Tags": Array([
                    Object({
                        "Key": String(
                            "TestKey",
                        ),
                        "Value": String(
                            "1234",
                        ),
                    }),
                ]),
            }),
            "Type": String(
                "AWS::EC2::Instance",
            ),
        }),
    }),
    "AWSTemplateFormatVersion": String(
        "2010-09-09",
    ),
}'
2020-07-30 12:13:33,105 TRACE [cfn_guard] CFN resources are: {"EC2Instance2": Object({"Properties": Object({"ImageId": Object({"Fn::FindInMap": Array([String("AWSRegionArch2AMI"), Object({"Ref": String("AWS::Region")}), Object({"Fn::FindInMap": Array([String("AWSInstanceType2Arch"), Object({"Ref": String("InstanceType")}), String("Arch")])})])}), "InstanceType": Object({"Ref": String("InstanceType")}), "KeyName": Object({"Ref": String("KeyName")}), "SecurityGroups": Array([Object({"Ref": String("InstanceSecurityGroup")})]), "Tags": Array([Object({"Key": String("TestKey"), "Value": String("1234")})])}), "Type": String("AWS::EC2::Instance")})}
2020-07-30 12:13:33,105 INFO  [cfn_guard] Parsing rule set
2020-07-30 12:13:33,105 DEBUG [cfn_guard::parser] Entered parse_rules
2020-07-30 12:13:33,105 TRACE [cfn_guard::parser] Parse rules entered with rules_file_contents: "\n\nAWS::EC2::Instance Tags.0.Key == /(TestKey)/\nAWS::EC2::Instance Tags.0.Value == /^([0-9]|[A-Z]){4}$/\n\n\n"
2020-07-30 12:13:33,105 TRACE [cfn_guard::parser] Parse rules entered with cfn_resources: {
    "EC2Instance2": Object({
        "Properties": Object({
            "ImageId": Object({
                "Fn::FindInMap": Array([
                    String(
                        "AWSRegionArch2AMI",
                    ),
                    Object({
                        "Ref": String(
                            "AWS::Region",
                        ),
                    }),
                    Object({
                        "Fn::FindInMap": Array([
                            String(
                                "AWSInstanceType2Arch",
                            ),
                            Object({
                                "Ref": String(
                                    "InstanceType",
                                ),
                            }),
                            String(
                                "Arch",
                            ),
                        ]),
                    }),
                ]),
            }),
            "InstanceType": Object({
                "Ref": String(
                    "InstanceType",
                ),
            }),
            "KeyName": Object({
                "Ref": String(
                    "KeyName",
                ),
            }),
            "SecurityGroups": Array([
                Object({
                    "Ref": String(
                        "InstanceSecurityGroup",
                    ),
                }),
            ]),
            "Tags": Array([
                Object({
                    "Key": String(
                        "TestKey",
                    ),
                    "Value": String(
                        "1234",
                    ),
                }),
            ]),
        }),
        "Type": String(
            "AWS::EC2::Instance",
        ),
    }),
}
2020-07-30 12:13:33,105 TRACE [cfn_guard::parser] Rules file lines: Lines(
    Map {
        iter: SplitTerminator(
            SplitInternal {
                start: 0,
                end: 105,
                matcher: CharSearcher {
                    haystack: "\n\nAWS::EC2::Instance Tags.0.Key == /(TestKey)/\nAWS::EC2::Instance Tags.0.Value == /^([0-9]|[A-Z]){4}$/\n\n\n",
                    finger: 0,
                    finger_back: 105,
                    needle: '\n',
                    utf8_size: 1,
                    utf8_encoded: [
                        10,
                        0,
                        0,
                        0,
                    ],
                },
                allow_trailing_empty: false,
                finished: false,
            },
        ),
    },
)
2020-07-30 12:13:33,106 DEBUG [cfn_guard::parser] Parsing ''
2020-07-30 12:13:33,106 DEBUG [cfn_guard::parser] Parsing ''
2020-07-30 12:13:33,106 DEBUG [cfn_guard::parser] Parsing 'AWS::EC2::Instance Tags.0.Key == /(TestKey)/'
2020-07-30 12:13:33,111 DEBUG [cfn_guard::parser] line_type is Rule
2020-07-30 12:13:33,111 DEBUG [cfn_guard::parser] Line is an 'AND' rule
2020-07-30 12:13:33,111 TRACE [cfn_guard::parser] Entered destructure_rule
2020-07-30 12:13:33,113 TRACE [cfn_guard::parser] Parsed rule's captures are: Captures(
    {
        0: Some(
            "AWS::EC2::Instance Tags.0.Key == /(TestKey)/",
        ),
        "resource_type": Some(
            "AWS::EC2::Instance",
        ),
        "resource_property": Some(
            "Tags.0.Key",
        ),
        "operator": Some(
            "==",
        ),
        "rule_value": Some(
            "/(TestKey)/",
        ),
    },
)
2020-07-30 12:13:33,114 TRACE [cfn_guard::parser] Destructured rules are: [
    Rule {
        resource_type: "AWS::EC2::Instance",
        field: "Tags.0.Key",
        operation: Require,
        value: "(TestKey)",
        rule_vtype: Regex,
        custom_msg: None,
    },
]
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] Parsed rule is: CompoundRule {
    compound_type: AND,
    rule_list: [
        Rule {
            resource_type: "AWS::EC2::Instance",
            field: "Tags.0.Key",
            operation: Require,
            value: "(TestKey)",
            rule_vtype: Regex,
            custom_msg: None,
        },
    ],
}
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] Parsing 'AWS::EC2::Instance Tags.0.Value == /^([0-9]|[A-Z]){4}$/'
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] line_type is Rule
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] Line is an 'AND' rule
2020-07-30 12:13:33,114 TRACE [cfn_guard::parser] Entered destructure_rule
2020-07-30 12:13:33,114 TRACE [cfn_guard::parser] Parsed rule's captures are: Captures(
    {
        0: Some(
            "AWS::EC2::Instance Tags.0.Value == /^([0-9]|[A-Z]){4}$/",
        ),
        "resource_type": Some(
            "AWS::EC2::Instance",
        ),
        "resource_property": Some(
            "Tags.0.Value",
        ),
        "operator": Some(
            "==",
        ),
        "rule_value": Some(
            "/^([0-9]|[A-Z]){4}$/",
        ),
    },
)
2020-07-30 12:13:33,114 TRACE [cfn_guard::parser] Destructured rules are: [
    Rule {
        resource_type: "AWS::EC2::Instance",
        field: "Tags.0.Value",
        operation: Require,
        value: "^([0-9]|[A-Z]){4}$",
        rule_vtype: Regex,
        custom_msg: None,
    },
]
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] Parsed rule is: CompoundRule {
    compound_type: AND,
    rule_list: [
        Rule {
            resource_type: "AWS::EC2::Instance",
            field: "Tags.0.Value",
            operation: Require,
            value: "^([0-9]|[A-Z]){4}$",
            rule_vtype: Regex,
            custom_msg: None,
        },
    ],
}
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] Parsing ''
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] Parsing ''
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] Variables dictionary is {"ENV_VSCODE_GIT_ASKPASS_MAIN": "********", "ENV_VSCODE_IPC_HOOK_CLI": "********", "ENV_VSCODE_GIT_ASKPASS_NODE": "********", "ENV_AWS_REGION": "********", "ENV_VSCODE_GIT_IPC_HANDLE": "********", "ENV_APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL": "********", "ENV__": "********", "ENV_HOME": "********", "ENV_NAME": "********", "ENV_PWD": "********", "ENV_SHLVL": "********", "ENV_AWS_SDK_LOAD_CONFIG": "********", "ENV_GIT_ASKPASS": "********", "ENV_TERM_PROGRAM": "********", "ENV_SHELL": "********", "ENV_LOGNAME": "********", "ENV_TERM": "********", "ENV_HOSTTYPE": "********", "ENV_AMD_ENTRYPOINT": "********", "ENV_LS_COLORS": "********", "ENV_PATH": "********", "ENV_VERBOSE_LOGGING": "********", "ENV_TERM_PROGRAM_VERSION": "********", "ENV_LESSCLOSE": "********", "ENV_USER": "********", "ENV_XDG_DATA_DIRS": "********", "ENV_LESSOPEN": "********", "ENV_WSLENV": "********", "ENV_COLORTERM": "********", "ENV_PIPE_LOGGING": "********", "ENV_LANG": "********"}
2020-07-30 12:13:33,114 DEBUG [cfn_guard::parser] Rule Set is [
    CompoundRule {
        compound_type: AND,
        rule_list: [
            Rule {
                resource_type: "AWS::EC2::Instance",
                field: "Tags.0.Key",
                operation: Require,
                value: "(TestKey)",
                rule_vtype: Regex,
                custom_msg: None,
            },
        ],
    },
    CompoundRule {
        compound_type: AND,
        rule_list: [
            Rule {
                resource_type: "AWS::EC2::Instance",
                field: "Tags.0.Value",
                operation: Require,
                value: "^([0-9]|[A-Z]){4}$",
                rule_vtype: Regex,
                custom_msg: None,
            },
        ],
    },
]
2020-07-30 12:13:33,115 INFO  [cfn_guard] Checking resources
2020-07-30 12:13:33,115 INFO  [cfn_guard] Applying rule 'CompoundRule {
    compound_type: AND,
    rule_list: [
        Rule {
            resource_type: "AWS::EC2::Instance",
            field: "Tags.0.Key",
            operation: Require,
            value: "(TestKey)",
            rule_vtype: Regex,
            custom_msg: None,
        },
    ],
}'
2020-07-30 12:13:33,115 DEBUG [cfn_guard] Applying rule 'Rule { resource_type: "AWS::EC2::Instance", field: "Tags.0.Key", operation: Require, value: "(TestKey)", rule_vtype: Regex, custom_msg: None }'
2020-07-30 12:13:33,115 INFO  [cfn_guard] Checking [EC2Instance2] which is of type "AWS::EC2::Instance"
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] Getting ["Tags", "0", "Key"] from {"ImageId":{"Fn::FindInMap":["AWSRegionArch2AMI",{"Ref":"AWS::Region"},{"Fn::FindInMap":["AWSInstanceType2Arch",{"Ref":"InstanceType"},"Arch"]}]},"InstanceType":{"Ref":"InstanceType"},"KeyName":{"Ref":"KeyName"},"SecurityGroups":[{"Ref":"InstanceSecurityGroup"}],"Tags":[{"Key":"TestKey","Value":"1234"}]}
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] field_list is ["Tags", "0", "Key"]
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] next_field is "Tags" and field_list is now ["0", "Key"]
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] props are Object({
    "ImageId": Object({
        "Fn::FindInMap": Array([
            String(
                "AWSRegionArch2AMI",
            ),
            Object({
                "Ref": String(
                    "AWS::Region",
                ),
            }),
            Object({
                "Fn::FindInMap": Array([
                    String(
                        "AWSInstanceType2Arch",
                    ),
                    Object({
                        "Ref": String(
                            "InstanceType",
                        ),
                    }),
                    String(
                        "Arch",
                    ),
                ]),
            }),
        ]),
    }),
    "InstanceType": Object({
        "Ref": String(
            "InstanceType",
        ),
    }),
    "KeyName": Object({
        "Ref": String(
            "KeyName",
        ),
    }),
    "SecurityGroups": Array([
        Object({
            "Ref": String(
                "InstanceSecurityGroup",
            ),
        }),
    ]),
    "Tags": Array([
        Object({
            "Key": String(
                "TestKey",
            ),
            "Value": String(
                "1234",
            ),
        }),
    ]),
})
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] Getting ["0", "Key"] from [{"Key":"TestKey","Value":"1234"}]
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] field_list is ["0", "Key"]
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] next_field is 0 and field_list is now ["Key"]
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] props are Array([
    Object({
        "Key": String(
            "TestKey",
        ),
        "Value": String(
            "1234",
        ),
    }),
])
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] Getting ["Key"] from {"Key":"TestKey","Value":"1234"}
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] field_list is ["Key"]
2020-07-30 12:13:33,115 TRACE [cfn_guard::util] next_field is "Key" and field_list is now []
2020-07-30 12:13:33,116 TRACE [cfn_guard::util] props are Object({
    "Key": String(
        "TestKey",
    ),
    "Value": String(
        "1234",
    ),
})
2020-07-30 12:13:33,116 DEBUG [cfn_guard] Template val is String("TestKey")
2020-07-30 12:13:33,116 TRACE [cfn_guard::util] Entered dereference_rule_value() with 'Rule {
    resource_type: "AWS::EC2::Instance",
    field: "Tags.0.Key",
    operation: Require,
    value: "(TestKey)",
    rule_vtype: Regex,
    custom_msg: None,
}' and Variables '{
    "ENV_VERBOSE_LOGGING": "********",
    "ENV_PATH": "********",
    "ENV_COLORTERM": "********",
    "ENV_TERM_PROGRAM_VERSION": "********",
    "ENV_LESSOPEN": "********",
    "ENV_PIPE_LOGGING": "********",
    "ENV_LOGNAME": "********",
    "ENV_SHLVL": "********",
    "ENV_NAME": "********",
    "ENV_VSCODE_GIT_ASKPASS_NODE": "********",
    "ENV_VSCODE_IPC_HOOK_CLI": "********",
    "ENV_PWD": "********",
    "ENV_USER": "********",
    "ENV_LESSCLOSE": "********",
    "ENV_LANG": "********",
    "ENV_WSLENV": "********",
    "ENV_XDG_DATA_DIRS": "********",
    "ENV_TERM_PROGRAM": "********",
    "ENV_AMD_ENTRYPOINT": "********",
    "ENV_AWS_REGION": "********",
    "ENV_GIT_ASKPASS": "********",
    "ENV_SHELL": "********",
    "ENV_TERM": "********",
    "ENV_APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL": "********",
    "ENV_HOSTTYPE": "********",
    "ENV__": "********",
    "ENV_VSCODE_GIT_ASKPASS_MAIN": "********",
    "ENV_AWS_SDK_LOAD_CONFIG": "********",
    "ENV_VSCODE_GIT_IPC_HANDLE": "********",
    "ENV_LS_COLORS": "********",
    "ENV_HOME": "********",
}'
2020-07-30 12:13:33,116 DEBUG [cfn_guard] rule_val is (TestKey) and val is "TestKey"
2020-07-30 12:13:33,116 DEBUG [cfn_guard] OpCode::Require with rule_val as (TestKey) and val as "TestKey" of RValueType::Regex
2020-07-30 12:13:33,117 INFO  [cfn_guard] Result: PASS
2020-07-30 12:13:33,117 INFO  [cfn_guard] Applying rule 'CompoundRule {
    compound_type: AND,
    rule_list: [
        Rule {
            resource_type: "AWS::EC2::Instance",
            field: "Tags.0.Value",
            operation: Require,
            value: "^([0-9]|[A-Z]){4}$",
            rule_vtype: Regex,
            custom_msg: None,
        },
    ],
}'
2020-07-30 12:13:33,117 DEBUG [cfn_guard] Applying rule 'Rule { resource_type: "AWS::EC2::Instance", field: "Tags.0.Value", operation: Require, value: "^([0-9]|[A-Z]){4}$", rule_vtype: Regex, custom_msg: None }'
2020-07-30 12:13:33,117 INFO  [cfn_guard] Checking [EC2Instance2] which is of type "AWS::EC2::Instance"
2020-07-30 12:13:33,117 TRACE [cfn_guard::util] Getting ["Tags", "0", "Value"] from {"ImageId":{"Fn::FindInMap":["AWSRegionArch2AMI",{"Ref":"AWS::Region"},{"Fn::FindInMap":["AWSInstanceType2Arch",{"Ref":"InstanceType"},"Arch"]}]},"InstanceType":{"Ref":"InstanceType"},"KeyName":{"Ref":"KeyName"},"SecurityGroups":[{"Ref":"InstanceSecurityGroup"}],"Tags":[{"Key":"TestKey","Value":"1234"}]}
2020-07-30 12:13:33,117 TRACE [cfn_guard::util] field_list is ["Tags", "0", "Value"]
2020-07-30 12:13:33,117 TRACE [cfn_guard::util] next_field is "Tags" and field_list is now ["0", "Value"]
2020-07-30 12:13:33,117 TRACE [cfn_guard::util] props are Object({
    "ImageId": Object({
        "Fn::FindInMap": Array([
            String(
                "AWSRegionArch2AMI",
            ),
            Object({
                "Ref": String(
                    "AWS::Region",
                ),
            }),
            Object({
                "Fn::FindInMap": Array([
                    String(
                        "AWSInstanceType2Arch",
                    ),
                    Object({
                        "Ref": String(
                            "InstanceType",
                        ),
                    }),
                    String(
                        "Arch",
                    ),
                ]),
            }),
        ]),
    }),
    "InstanceType": Object({
        "Ref": String(
            "InstanceType",
        ),
    }),
    "KeyName": Object({
        "Ref": String(
            "KeyName",
        ),
    }),
    "SecurityGroups": Array([
        Object({
            "Ref": String(
                "InstanceSecurityGroup",
            ),
        }),
    ]),
    "Tags": Array([
        Object({
            "Key": String(
                "TestKey",
            ),
            "Value": String(
                "1234",
            ),
        }),
    ]),
})
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] Getting ["0", "Value"] from [{"Key":"TestKey","Value":"1234"}]
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] field_list is ["0", "Value"]
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] next_field is 0 and field_list is now ["Value"]
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] props are Array([
    Object({
        "Key": String(
            "TestKey",
        ),
        "Value": String(
            "1234",
        ),
    }),
])
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] Getting ["Value"] from {"Key":"TestKey","Value":"1234"}
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] field_list is ["Value"]
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] next_field is "Value" and field_list is now []
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] props are Object({
    "Key": String(
        "TestKey",
    ),
    "Value": String(
        "1234",
    ),
})
2020-07-30 12:13:33,118 DEBUG [cfn_guard] Template val is String("1234")
2020-07-30 12:13:33,118 TRACE [cfn_guard::util] Entered dereference_rule_value() with 'Rule {
    resource_type: "AWS::EC2::Instance",
    field: "Tags.0.Value",
    operation: Require,
    value: "^([0-9]|[A-Z]){4}$",
    rule_vtype: Regex,
    custom_msg: None,
}' and Variables '{
    "ENV_LESSCLOSE": "********",
    "ENV_HOSTTYPE": "********",
    "ENV_LS_COLORS": "********",
    "ENV_VSCODE_IPC_HOOK_CLI": "********",
    "ENV_SHELL": "********",
    "ENV_XDG_DATA_DIRS": "********",
    "ENV_NAME": "********",
    "ENV_TERM_PROGRAM_VERSION": "********",
    "ENV_GIT_ASKPASS": "********",
    "ENV_AWS_REGION": "********",
    "ENV_VERBOSE_LOGGING": "********",
    "ENV_WSLENV": "********",
    "ENV_TERM_PROGRAM": "********",
    "ENV_HOME": "********",
    "ENV_SHLVL": "********",
    "ENV_USER": "********",
    "ENV_PATH": "********",
    "ENV_PWD": "********",
    "ENV_APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL": "********",
    "ENV_COLORTERM": "********",
    "ENV_TERM": "********",
    "ENV__": "********",
    "ENV_PIPE_LOGGING": "********",
    "ENV_AWS_SDK_LOAD_CONFIG": "********",
    "ENV_VSCODE_GIT_IPC_HANDLE": "********",
    "ENV_AMD_ENTRYPOINT": "********",
    "ENV_LANG": "********",
    "ENV_VSCODE_GIT_ASKPASS_MAIN": "********",
    "ENV_VSCODE_GIT_ASKPASS_NODE": "********",
    "ENV_LESSOPEN": "********",
    "ENV_LOGNAME": "********",
}'
2020-07-30 12:13:33,118 DEBUG [cfn_guard] rule_val is ^([0-9]|[A-Z]){4}$ and val is "1234"
2020-07-30 12:13:33,118 DEBUG [cfn_guard] OpCode::Require with rule_val as ^([0-9]|[A-Z]){4}$ and val as "1234" of RValueType::Regex
2020-07-30 12:13:33,119 INFO  [cfn_guard] Result: FAIL
2020-07-30 12:13:33,119 TRACE [cfn_guard::util] Removing spaces and newline characters from '1234'
2020-07-30 12:13:33,119 TRACE [cfn_guard::util] formatted_value is '1234'
2020-07-30 12:13:33,119 DEBUG [cfn_guard] Outcome was: '[
    "[EC2Instance2] failed because [Tags.0.Value] is [1234] and the permitted pattern is [^([0-9]|[A-Z]){4}$]",
]'
[EC2Instance2] failed because [Tags.0.Value] is [1234] and the permitted pattern is [^([0-9]|[A-Z]){4}$]
Number of failures: 1

NOTE: Please be sure that the templates, rule sets and logs you provide as part of your bug report do not contain any sensitive information.

Expected behavior
A clear and concise description of what you expected to happen.

The Regex I'm using is ^([0-9]|[A-Z]){4}$ - which should match any sequence of 4 digits and capitalised numbers, but not allow more or less than 4 characters.
However the regex fails to match at all, no matter what parameters are passed to it. I've also tried including (?m) in the regex string to turn on multiline, but that doesn't make a difference

Screenshots
If applicable, add screenshots to help explain your problem.

Operating System:
[eg, MacOS, Windows, Ubuntu, etc]
Ubuntu

OS Version
[eg Catalina, 10, 18.04, etc]
Ubuntu 18.04.4 LTS using WSL

Additional context
Add any other context about the problem here.

[Enhancement] Regular expression inside json

Is your feature request related to a problem? Please describe.
I want to say thank you for this great tool. I tried to use regular expression in inside json, but it did not work

Describe the solution you'd like
I have the following policy:

{
    "Description": "Create Test Role",
    "Parameters": {
        "KmsDataKeyArn": {
            "Description": "Test",
            "Type": "String"
        }
    },
    "Resources": {
        "TestRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "TestPolicyDocument",
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": [
                                    {
                                        "Fn::Sub": "${AWS::AccountId}"
                                    }
                                ],
                                "Service": [
                                    "iam.amazonaws.com",
                                    "ec2.amazonaws.com"
                                ]
                            },
                            "Action": "sts:AssumeRole"
                        }
                    ]
                },
                "Path": "/",
                "Policies": [
                    {
                        "PolicyName": "TestPolicy",
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Sid": "ACMPA",
                                    "Effect": "Allow",
                                    "Action": [
                                        "acm-pca:CreateCertificateAuthority",
                                        "acm-pca:IssueCertificate",
                                        "acm-pca:GetCertificate"
                                    ],
                                    "Resource": {
                                        "Fn::Sub": "arn:aws:acm-pca:${AWS::Region}:${AWS::AccountId}:certificate-authority/as"
                                    }
                                } 
                            ]
                        }
                    }
                ]
            }
        }
    }
}

I used the following rule and it worked as intended:
AWS::IAM::Role Policies.*.PolicyDocument.Statement.*.Resource == {"Fn::Sub": "arn:aws:acm-pca:${AWS::Region}:${AWS::AccountId}:certificate-authority/as"}

I am just wondering if I could do something with regular expression like
AWS::IAM::Role Policies.*.PolicyDocument.Statement.*.Resource == {/.+Sub/: "arn:aws:acm-pca:${AWS::Region}:${AWS::AccountId}:certificate-authority/as"}

or
AWS::IAM::Role Policies.*.PolicyDocument.Statement.*.Resource == {"/.+Sub/": "arn:aws:acm-pca:${AWS::Region}:${AWS::AccountId}:certificate-authority/as"}

to match the use cases below:

- "Resource": {
    "Fn::Sub": "arn:aws:acm-pca:${AWS::Region}:${AWS::AccountId}:certificate-authority/as"
}
- "Resource": {
    "!Sub": "arn:aws:acm-pca:${AWS::Region}:${AWS::AccountId}:certificate-authority/as"
}

Or a rule like the one below would not return any finding
AWS::IAM::Role Policies.*.PolicyDocument.Statement.*.Resource == {"Fn::Sub": "/.+\${AWS::Region}:\${AWS::AccountId}.+/"}

I am just wondering if I am using the right syntax or if I am missing something.

Thank you

[Enhancement]

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Add a Java JNI dylib to use cfn-guard from java code.

Describe the solution you'd like
A clear and concise description of what you want to happen.

Use the jni crate to add a library function for JNI. build cfn-guard as a dylib alongside the executable.
Cargo.toml
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

The main alternative is to impl a parser for the rules syntax in a language that is more commonly used for libraries (java/python/typescript). Obviously this is not good because there will always be a huge lag between multiple impls.

Additional context
Add any other context or screenshots about the feature request here.

Enforce mandatory tags for all tagging supported resources

My requirements are:

  • Enforce few mandatory tags (say ApplicationName, ApplicationOwner, SupportContact, Environment & CostCenter) to all tagging supported resources. cfn-guard should raise error if any of these tags are found missing.
  • Developers can add any number of tags other than the mandatory tags. cfn-guard should not fail if such tags are defined.

Below shown is a sample ruleset that I used to validate the mandatory tags for VPC.

AWS::EC2::VPC Tags == /.*"Key":"Environment".*/ << the mandatory Tag: Environment is not specified
AWS::EC2::VPC Tags == /.*"Key":"ApplicationName".*/ << the mandatory Tag: ApplicationName is not specified
AWS::EC2::VPC Tags == /.*"Key":"SupportContact".*/ << the mandatory Tag: SupportContact is not specified
AWS::EC2::VPC Tags == /.*"Key":"ApplicationOwner".*/ << the mandatory Tag: ApplicationOwner is not specified
AWS::EC2::VPC Tags == /.*"Key":"CostCenter".*/ << the mandatory Tag: CostCenter is not specified

By evaluating a CFT which contains only tag keys Service, Name and ApplicationName, using this above ruleset, I received the below output.

[VPC] failed because [Tags] is [[{"Key":"Service","Value":"VPC"},{"Key":"Name","Value":"DemoVPC"},{"Key":"ApplicationName","Value":"webapp"}]] and the mandatory Tag: ApplicationOwner is not specified
[VPC] failed because [Tags] is [[{"Key":"Service","Value":"VPC"},{"Key":"Name","Value":"DemoVPC"},{"Key":"ApplicationName","Value":"webapp"}]] and the mandatory Tag: CostCenter is not specified
[VPC] failed because [Tags] is [[{"Key":"Service","Value":"VPC"},{"Key":"Name","Value":"DemoVPC"},{"Key":"ApplicationName","Value":"webapp"}]] and the mandatory Tag: Environment is not specified
[VPC] failed because [Tags] is [[{"Key":"Service","Value":"VPC"},{"Key":"Name","Value":"DemoVPC"},{"Key":"ApplicationName","Value":"webapp"}]] and the mandatory Tag: SupportContact is not specified

Got two questions here:

  1. I have used regex in ruleset to validate tags. Is there any other method suggested for checking the existence of mandatory tags?
  2. As per my understanding, cfn-guard currently supports only adding rulesets for each resource type individually. Is there a way to apply this rule set globally for all resources?

[BUG]

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Please supply:

  1. An example rule set and template that results in the error
  2. The commands you used to invoke the tool
  3. The output of a -vvv log level if it's not related to cfn-guard-lambda, or the relevant CloudWatch log messages if it is related to the lambda

NOTE: Please be sure that the templates, rule sets and logs you provide as part of your bug report do not contain any sensitive information.

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Operating System:
[eg, MacOS, Windows, Ubuntu, etc]

OS Version
[eg Catalina, 10, 18.04, etc]

Additional context
Add any other context about the problem here.

[Enhancement] Recursive scanning of templates in a folder

I am working on an usecase where I need to scan multiple cft files (yaml or json) against the rules.
But after reading the documentation on the cfn-guard I came to know that its an 1:1 validation, is there any way that i can do it recursively?

[BUG] Example payload in makefile failing to invoke cfn-guard-lambda

Describe the bug
When I execute any testing payload that comes with cfn-guard-lambda I receive a "An error occurred (InvalidRequestContentException) when calling the Invoke operation: Could not parse request body into json: Invalid UTF-8 start byte 0xab" error.

To Reproduce
Please supply:

  1. make install

The step will fail at "/Library/Developer/CommandLineTools/usr/bin/make fail" step. See error below:

➜  cfn-guard-lambda git:(master) ✗ make install
This is a Darwin machine...
env PKG_CONFIG_ALLOW_CROSS=1 cargo build --release --target x86_64-unknown-linux-musl
    Finished release [optimized] target(s) in 0.33s
cp target/x86_64-unknown-linux-musl/release/cfn-guard-lambda ./bootstrap
chmod +x bootstrap
zip lambda.zip bootstrap
  adding: bootstrap (deflated 67%)
aws lambda create-function --function-name cfn-guard-lambda --handler doesnt.matter --zip-file fileb://./lambda.zip --runtime provided --role arn:aws:iam::077927938888:role/lambda-cfn-guard  --environment Variables={RUST_BACKTRACE=1}
{
    "FunctionName": "cfn-guard-lambda",
    "FunctionArn": "arn:aws:lambda:us-west-2:077927938888:function:cfn-guard-lambda",
    "Runtime": "provided",
    "Role": "arn:aws:iam::077927938888:role/lambda-cfn-guard",
    "Handler": "doesnt.matter",
    "CodeSize": 2628443,
    "Description": "",
    "Timeout": 3,
    "MemorySize": 128,
    "LastModified": "2020-07-21T20:03:58.926+0000",
    "CodeSha256": "XFx2fanYnYCxLcYmBiRqeOYHPliM24MXo5F60WkrKSM=",
    "Version": "$LATEST",
    "Environment": {
        "Variables": {
            "RUST_BACKTRACE": "1"
        }
    },
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "74b3c713-5e6c-4c08-8c1b-58b24cb67a65",
    "State": "Active",
    "LastUpdateStatus": "Successful"
}
/Library/Developer/CommandLineTools/usr/bin/make fail
aws lambda invoke --function-name cfn-guard-lambda --payload '{ "template": "{\n    \"Resources\": {\n        \"NewVolume\" : {\n            \"Type\" : \"AWS::EC2::Volume\",\n            \"Properties\" : {\n                \"Size\" : 100,\n                \"Encrypted\": true,\n                \"AvailabilityZone\" : \"us-east-1b\"\n            }\n        },\n        \"NewVolume2\" : {\n            \"Type\" : \"AWS::EC2::Volume\",\n            \"Properties\" : {\n                \"Size\" : 99,\n                \"Encrypted\": true,\n                \"AvailabilityZone\" : \"us-east-1b\"\n            }\n        } }\n}", "ruleSet": "let require_encryption = true\nlet disallowed_azs = [us-east-1a,us-east-1b,us-east-1c]\n\nAWS::EC2::Volume AvailabilityZone NOT_IN %disallowed_azs\nAWS::EC2::Volume Encrypted != %require_encryption\nAWS::EC2::Volume Size == 101 |OR| AWS::EC2::Volume Size == 99\nAWS::IAM::Role AssumeRolePolicyDocument.Version == 2012-10-18\nAWS::EC2::Volume Lorem == true\nAWS::EC2::Volume Encrypted == %ipsum\nAWS::EC2::Volume AvailabilityZone != /us-east-.*/", "strict_checks": true}' output.json

An error occurred (InvalidRequestContentException) when calling the Invoke operation: Could not parse request body into json: Invalid UTF-8 start byte 0xab
 at [Source: (byte[])"�驕�^�����z��{h�鞝<�x�-��[�zs뢗��'��(�{]4�Iܯ*my�k������i���ܙ�w��欷V�s^�Z%�g��<�x�-��[�zs뢗��'��(�{�gw+ʛ^v��zp/j)Zn)b�&h��y�-չ瞻�y'v+�Z0yֳ�pH@�V�n��'r����"; line: 1, column: 6]�Y!�Z%�g����i���ܙ�w�92
make[1]: *** [fail] Error 254
make: *** [install] Error 2

Expected behavior
Install is finished with success.

Operating System:
MacOS

OS Version
10.13.6

More context

I can verify the lambda works, I tested with the following snippet:

Resources:
  lambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        ZipFile: |
          def handler(event,context):
            return {
              'body': 'Hello there {0}'.format(event['requestContext']['identity']['sourceIp']),
              'headers': {
                'Content-Type': 'text/plain'
              },
              'statusCode': 200
            }
      Description: "Governance Lambda Demo"
      FunctionName: !Ref "lambdaFunctionName"
      Handler: "index.handler"
      MemorySize: 128
      Role: !GetAtt "lambdaIAMRole.Arn"
      Runtime: "python2.7"
      Timeout: 10

against the rule: "AWS::Lambda::Function Runtime == 'python2.7'" and it passes. See request/response:

{"apiRequest": {"requestId": "8e621a12-a8ca-4a4b-a2cb-ae2d7d129131", "requestName": "InvokeRequest"}}CfnGuardEngine response: {"message":[],"exit_status":"PASS"}

I'm currently running a687d7a commit.

[BUG] installation instructions not working on cloud9

Describe the bug
I try to setup cloudformation guard on a fresh cloud9 IDE according to the current installation instructions for Linux from https://github.com/aws-cloudformation/cloudformation-guard
i.e.:

wget https://github.com/aws-cloudformation/cloudformation-guard/releases/download/VERSION/cfn-guard-linux-VERSION.tar.gz
tar -xvf cfn-guard-linux-1.0.0.tar.gz
cd ./cfn-guard-linux
./cfn-guard 

When I try to run ./cfn-guard I receive following error:

./cfn-guard: /lib64/libc.so.6: version `GLIBC_2.18' not found (required by ./cfn-guard)

[Enhancement] Introduce Parameter Inputs for checking the CF scripts

Is your feature request related to a problem? Please describe.
In its current form, cfn-guard checks the Cloudformation script without any input.

Example, Lets say I have a Cloudformation script similar to the following (simplified version)

Parameters:
  RDSEncryption:
    Type: String

Resources:
  RDSPostgressDB:
    Type: AWS::RDS::DBInstance
    Properties: 
      StorageEncrypted: !Ref RDSEncryption

I want to create an RDS Database and I have a CF parameter input for encryption (true/false). Currently there is no way in cfn-guard to verify that the RDS will be created using encryption. I can only verify that the StorageEncrypted will have the value "RDSEncryption" (please correct me if I'm wrong)

Describe the solution you'd like
I would prefer an option to provide a json document with the CF parameter inputs and be able to check the Cloudformation script after all those values have been implemented.
The example I gave above would look like:

rules:

AWS::RDS::DBInstance StorageEncrypted == true

input.json:

[
  {
    "ParameterKey": "RDSEncryption",
    "ParameterValue": "true"
  }
]

cfn-guard:

cargo run -- -t rds.yaml -r rules -i input.json

cfn-guard will change the RDSEncryption Rerefence with the input true and will be able to check against that

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.