# Guard: Clauses

Clauses are the foundational underpinning of Guard rules. Clauses are boolean statements which evaluate to a `true` (`PASS`)/ `false` (`FAIL`) and take the following format:

```
  <query> <operator> [query|value literal] 
```

You must specify a `query` and an `operator` in the clause section:

* `query` in its most simple form is a decimal dot (`.`) formatted expressions written to traverse hierarchical data. More information on this section of the clause can be found in the [Guard: Query and Filtering](QUERY_AND_FILTERING.md) document.

* `operator` can use *unary* or *binary* operators. Both of these operators will be discussed in-depth later in this document:

  * *Unary Operators:* `exists`, `empty`, `is_string`, `is_list`, `is_struct`, `not(!)`
  * *Binary Operators:* `==`, `!=`, `>`, `>=`, `<`, `<=`, `IN`

The `query|value literal` section of the clause is optional:

* `query|value literal` the third section of a clause is needed when a binary operator is used. It can be a `query` as defined above or it can be any supported value literal like `string`, `integer(64)`, etc.

Below are some examples for clauses:

* Clauses using unary operator:

```
# The collection TcpBlockedPorts cannot be empty
InputParameters.TcpBlockedPorts not empty  
```

```
# ExecutionRoleArn must be a string
Properties.ExecutionRoleArn is_string
```

* Clauses using binary operator:

```
# BucketName must not contain the string "encrypt" irrespective of casing
Properties.BucketName != /(?i)encrypted/
```

```
# ReadCapacityUnits must be less than or equal to 5000
Properties.ProvisionedThroughtput.ReadCapacityUnits <= 5000
```

## Operators

Let us look at Guard supported `operators` in-depth. Information on each operator will be accompanied by examples based on the following example CloudFormation template to help you understand the various operators. All examples in this section will be based on the following template:

```yaml
# Template-1
Resources:
  S3Bucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: "MyServiceS3Bucket"
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: 'aws:kms'
              KMSMasterKeyID: 'arn:aws:kms:us-east-1:123456789:key/056ea50b-1013-3907-8617-c93e474e400'
      Tags:
        - Key: "stage"
          Value: "prod"
        - Key: "service"
          Value: "myService"
```

### Unary Operators

#### `empty` and `exists` operators

Guard introduces `empty` and `exists` operators to assist authors verify states of the `query` in a clause. You can use the  `not(!)` operator with both these operators to check the inverse state.

`empty`- Checks if a collection is empty. It can also be used to check if a query has values in a hierarchical data as all queries result in a collection, more about queries is mentioned in the [Guard: Query and Filtering](QUERY_AND_FILTERING.md) document.

```
# Checks if the template has resources defined
Resources !empty
```

```
# Checks if one or more tags defined
Resources.S3Bucket.Properties.Tags !empty
```

The above two clauses will `PASS` for the example template. 

* The first clause checks to see if the template has resources defined under `Resources` , and the template has one resource with `S3Bucket` as the logical resource ID. 
* The second clause checks to see if one or more tags are defined in `S3Bucket`. It has two tags defined under `Tags`.

`empty` can’t be used to check if string value queries have an empty string (`""`) defined.

`exists` - Checks if each occurrence of the query has a value and can be used in place of `!= null`.

```
# Checks if BucketEncryption is defined
Resources.S3Bucket.Properties.BucketEncryption exists
```

The above clause will `PASS` for the example template as `BucketEncryption` is defined for `S3Bucket`.

> **IMPORTANT**: `empty` and `not exists` checks evaluate to true for missing property keys when traversing the input data. E.g. if we check `Resources.S3Bucket.Properties.Tags empty` if `Properties` was not present in the template for S3Bucket, then `empty` evaluates to true. 

#### `is_string`, `is_list`, and `is_struct` operators

`is_string` - Checks if each occurrence of the query is of `string` type.

```
# Checks if BucketName is defined as a string
Resources.S3Bucket.Properties.BucketName is_string
```

`is_list` - Checks if each occurrence of the query is of `list` type.

```
# Checks if Tags is defined as a list
Resources.S3Bucket.Properties.Tags is_list
```

`is_struct` - Checks if each occurrence of the query is a structured data.

```
# Checks if BucketEncryption is defined as a structured data
Resources.S3Bucket.Properties.BucketEncryption is_struct
```

All the above example clauses will `PASS` for the example template. You can use the  `not(!)` operator with the above operators to check the inverse state.

### Binary Operators

Below are binary operators supported in Guard. The left-hand side (LHS) to the operator as mentioned above must always be a query and the right-hand side (RHS) can be a query or a value literal:

```
  ==    Equal
  !=    Not Equal
  >     Greater Than
  >=    Greater Than Or Equal To
  <     Less Than
  <=    Less Than Or Equal To
  IN    In a list of form [x, y, z]
```

 A value literal can be from any of the following supported categories,

* all primitives `string`, `integer(64)`, `float(64)`, `bool`, `char`, `regex`
* a specialized range type for expressing `integer`, `float`, `char` ranges, expressed as,
    *  `r[<lower_limit>, <upper_limit>]`, which translates to any value `k` that satisfies the following expression: `lower_limit` <= k <= `upper_limit`
    *  `r[<lower_limit>, <upper_limit>)`, which translates to any value `k` that satisfies the following expression: `lower_limit` <= k < `upper_limit`
    *  `r(<lower_limit>, <upper_limit>]`, which translates to any value `k` that satisfies the following expression: `lower_limit` < k <= `upper_limit`
    *  `r(<lower_limit>, <upper_limit>)`, which translates to any value `k` that satisfies the following expression: `lower_limit` < k < `upper_limit`
* associative arrays (a.k.a map) for nested key value structured data like:
```
{ "my-map": { "nested-maps": [ { "key": 10, "value": 20 } ] } }
```
* arrays of primitive/associative array types

Below are a couple of examples of clauses using binary operators:

* Based on the Template-1 example template:

```
# Checks if BucketName does not contain the string "encrypt" irrespective of casing
Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
```

* Consider the following CloudFormation template:

```yaml
# Template-2
Resources:
  NewVolume:
    Type: AWS::EC2::Volume
    Properties: 
      Size: 100
      VolumeType: io1
      Iops: 100
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: us-east-1
      Tags:
        - Key: environment
          Value: test
    DeletionPolicy: Snapshot
```

Below are a few Guard clauses based on the above template:

```
# Checks Size of the EC2 volume is within a specific range, 50<= Size <= 200
Resources.NewVolume.Properties.Size IN r[50,200]
```

```
# Checks VolumeType is one of io1, io2 or gp3
Resources.NewVolume.Properties.VolumeType IN [ 'io1','io2','gp3' ]
```

> While these examples illustrate using `S3Bucket`, `NewVolume` in the query, often these are user defined and can be arbitrarily named in an IaC template. To write a rule that is generic and applies to all `AWS::S3::Bucket` resources defined in the template the most common form of query used is `Resources.*[ Type == ‘AWS::S3::Bucket’ ]` to select them. See [Guard: Query and Filtering](QUERY_AND_FILTERING.md) for details on usage and explore the examples directory.

## Custom Message

You can add a custom message to a clause. A custom message is added at the end of a clause as follows:

```
  <query> <operator> [query|value literal] [custom message]
```

`custom message` is expressed as `<<message>>` where message is any string which ideally provides information regarding the clause preceding it. This message is displayed in the verbose outputs of the validate (provide links) and test (provide links) commands and can be used to supply information helpful for understanding/debugging rules file evaluation on hierarchical data. The Template-2 example template clauses with custom message will look as follows:

```
Resources.NewVolume.Properties.Size IN r[50,200] 
<<
    EC2Volume size must be between 50 and 200, 
    not including 50 and 200
>>
Resources.NewVolume.Properties.VolumeType IN [ 'io1','io2','gp3' ] <<Allowed Volume Types are io1, io2, and gp3>>
```

### Combining Clauses

Now that we have a complete picture of what constitutes a clause, let us learn to combine clauses. In Guard, each clause written on a new line is combined implicitly with the next clause using conjunction (boolean `and` logic):

```
# clause_A ^ clause_B ^ clause_C
clause_A
clause_B
clause_C
```

You can also combine a clause with the next clause using disjunction by specifying `or|OR` at the end of the first clause.

```
  <query> <operator> [query|value literal] [custom message] [or|OR]
```

Disjunctions are evaluated first followed by conjunctions, and hence Guard rules can be defined as a conjunction of disjunction of clauses that evaluate to a `true` (`PASS`) / `false` (`FAIL`). This can be best explained with a few examples:

```
# (clause_E v clause_F) ^ clause_G
clause_E OR clause_F
clause_G

# (clause_H v clause_I) ^ (clause_J v clause_K)
clause_H OR
clause_I
clause_J OR
clause_K

# (clause_L v clause_M v clause_N) ^ clause_O
clause_L OR
clause_M OR
clause_N 
clause_O
```

This is similar to the [Conjunctive Normal Form (CNF)](https://en.wikipedia.org/wiki/Conjunctive_normal_form).

All clauses written based on the Template-1 example template can be combined as follows:

```
Resources.S3Bucket.Properties.BucketName is_string
Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
Resources.S3Bucket.Properties.BucketEncryption exists
Resources.S3Bucket.Properties.BucketEncryption is_struct
Resources.S3Bucket.Properties.Tags is_list
Resources.S3Bucket.Properties.Tags !empty
```

All clauses above are combined using conjunction. As you can see, there is repetition of part of the query expression in every clause. You can improve composability and remove verbosity and repetition from a set of related clauses with the same initial query path using a query block. 

## Blocks

### Query blocks

The above set of clauses can be written in block format as follows:

```
Resources.S3Bucket.Properties {
    BucketName is_string
    BucketName != /(?i)encrypt/
    BucketEncryption exists
    BucketEncryption is_struct
    Tags is_list
    Tags !empty
}
```

The above composition is referred to as query blocks as the query preceding the block sets the context for clauses inside the block. This improves composability and removes verbosity and repetition while writing multiple related clauses with the same initial query path.

### When blocks - When condition for conditional evaluation

Blocks can be evaluated conditionally using `when` blocks; `when` blocks take the following form:

```
  when <condition> {
      Guard_rule_1
      Guard_rule_2
      ...
  }
```

* The `when` keyword designates the start of the when block.

* `condition` can be any Guard rule. The block is evaluated only if the evaluation of the condition results in `true` (`PASS`).

Below is an example using the Template-1 example template:

```
when Resources.S3Bucket.Properties.BucketName is_string {
    Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
}
```

The clause within the when block will only execute if `BucketName` is a string. If the value for `BucketName` was being referenced from a Parameter as shown below, the clause within the when block will not be executed.

```
Parameters:
  S3BucketName:
    Type: String

Resources:
  S3Bucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: 
        Ref: S3BucketName
    ...
```

### Named rule blocks

Named rule blocks allow for re-usability, improved composition and remove verbosity and repetition. They take the following form:

```
  rule <rule name> [when <condition>] {
      Guard_rule_1
      Guard_rule_2
      ...
  }
```

* The `rule` keyword designates the start of a named rule block.

* `rule name` can be any string that is human-readable and ideally should uniquely identify a named rule block. It can be thought of as a label to the set of Guard rules it encapsulates where Guard rules is an umbrella term for clauses, query blocks, when blocks and named rule blocks. The `rule name` can be used to refer to the evaluation result of the set of Guard rules it encapsulates, this is what makes named rule blocks re-usable. It also helps provide context in the validate (provide links) and test (provide links) command output as to what exactly failed. The `rule name` is displayed along with it block’s evaluation status - `PASS`, `FAIL`, or `SKIP`, in the evaluation output of the rules file:

```
# Sample output of an evaluation where check1, check2, and check3 are rule names.
_Summary__ __Report_ Overall File Status = **FAIL**
**PASS/****SKIP** **rules**
check1 **SKIP**
check2 **PASS**
**FAILED rules**
check3 **FAIL**
```

* Named rule blocks can also be evaluated conditionally by specifying the `when` keyword followed with a `condition` after the `rule name`.

Reiterating the `when` block example below:

```
rule checkBucketNameStringValue when Resources.S3Bucket.Properties.BucketName is_string {
    Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
}

# The above can also be written as follows
rule checkBucketNameIsString {
    Resources.S3Bucket.Properties.BucketName is_string
}
rule checkBucketNameStringValue when checkBucketNameIsString {
    Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
}
```

Named rule blocks can be re-used and grouped together with other Guard rules. Below are a few examples:

```
rule rule_name_A {
    Guard_rule_1 OR
    Guard_rule_2
    ...
}

rule rule_name_B {
    Guard_rule_3
    Guard_rule_4
    ...
}

rule rule_name_C {
    rule_name_A OR rule_name_B
}

rule rule_name_D {
    rule_name_A
    rule_name_B
}

rule rule_name_E when rule_name_D {
    Guard_rule_5
    Guard_rule_6
    ...
}
```

The above styles of compositions will be discussed in-depth in the [Guard: Complex Composition](COMPLEX_COMPOSITION.md) document.
