

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Escrevendo cláusulas para realizar avaliações contextuais
<a name="context-aware-evaluations"></a>

AWS CloudFormation Guard as cláusulas são avaliadas em relação aos dados hierárquicos. O mecanismo de avaliação do Guard resolve consultas em relação aos dados recebidos seguindo os dados hierárquicos conforme especificado, usando uma notação pontilhada simples. Freqüentemente, várias cláusulas são necessárias para avaliar em relação a um mapa de dados ou a uma coleção. O Guard fornece uma sintaxe conveniente para escrever essas cláusulas. O mecanismo está ciente do contexto e usa os dados correspondentes associados às avaliações.

Veja a seguir um exemplo de uma configuração do Kubernetes Pod com contêineres, na qual você pode aplicar avaliações contextuais.

```
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
    - name: app
      image: 'images.my-company.example/app:v4'
      resources:
        requests:
          memory: 64Mi
          cpu: 0.25
        limits:
          memory: 128Mi
          cpu: 0.5
    - name: log-aggregator
      image: 'images.my-company.example/log-aggregator:v6'
      resources:
        requests:
          memory: 64Mi
          cpu: 0.25
        limits:
          memory: 128Mi
          cpu: 0.75
```

Você pode criar cláusulas do Guard para avaliar esses dados. Ao avaliar um arquivo de regras, o contexto é todo o documento de entrada. Veja a seguir exemplos de cláusulas que validam a aplicação de limites para contêineres especificados em um pod.

```
#
# At this level, the root document is available for evaluation
#

#
# Our rule only evaluates for apiVersion == v1 and K8s kind is Pod
#
rule ensure_container_limits_are_enforced
    when apiVersion == 'v1'
        kind == 'Pod' 
{
    spec.containers[*] {
        resources.limits {
            #
            # Ensure that cpu attribute is set
            #
            cpu exists
            <<
                Id: K8S_REC_18
                Description: CPU limit must be set for the container
            >> 

            #
            # Ensure that memory attribute is set
            #
            memory exists
            <<
                Id: K8S_REC_22
                Description: Memory limit must be set for the container
            >>
        }
    }
}
```

## Compreensão `context` nas avaliações
<a name="context"></a>

No nível do bloco de regras, o contexto de entrada é o documento completo. As avaliações da `when` condição ocorrem nesse contexto raiz de entrada em que os `kind` atributos `apiVersion` e estão localizados. No exemplo anterior, essas condições são avaliadas como`true`.

Agora, percorra a hierarquia `spec.containers[*]` mostrada no exemplo anterior. Para cada travessia da hierarquia, o valor do contexto muda de acordo. Depois que a travessia do `spec` bloco é concluída, o contexto muda, conforme mostrado no exemplo a seguir.

```
containers:
  - name: app
    image: 'images.my-company.example/app:v4'
    resources:
      requests:
        memory: 64Mi
        cpu: 0.25
      limits:
        memory: 128Mi
        cpu: 0.5
  - name: log-aggregator
    image: 'images.my-company.example/log-aggregator:v6'
    resources:
      requests:
        memory: 64Mi
        cpu: 0.25
      limits:
        memory: 128Mi
        cpu: 0.75
```

Depois de percorrer o `containers` atributo, o contexto é mostrado no exemplo a seguir.

```
- name: app
  image: 'images.my-company.example/app:v4'
  resources:
    requests:
      memory: 64Mi
      cpu: 0.25
    limits:
      memory: 128Mi
      cpu: 0.5
- name: log-aggregator
  image: 'images.my-company.example/log-aggregator:v6'
  resources:
    requests:
      memory: 64Mi
      cpu: 0.25
    limits:
      memory: 128Mi
      cpu: 0.75
```

## Entendendo os loops
<a name="loops"></a>

Você pode usar a expressão `[*]` para definir um loop para todos os valores contidos na matriz do `containers` atributo. O bloco é avaliado para cada elemento interno`containers`. No trecho de regra do exemplo anterior, as cláusulas contidas no bloco definem as verificações a serem validadas em relação a uma definição de contêiner. O bloco de cláusulas contido nele é avaliado duas vezes, uma para cada definição de contêiner.

```
{
    spec.containers[*] {
       ...
    }
}
```

Para cada iteração, o valor do contexto é o valor no índice correspondente.

**nota**  
O único formato de acesso ao índice suportado é `[<integer>]` ou`[*]`. Atualmente, o Guard não suporta faixas como`[2..4]`.

## Matrizes
<a name="arrays"></a>

Geralmente, em locais onde uma matriz é aceita, valores únicos também são aceitos. Por exemplo, se houver apenas um contêiner, a matriz poderá ser descartada e a entrada a seguir será aceita.

```
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
    name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: 0.25
      limits:
        memory: "128Mi"
        cpu: 0.5
```

Se um atributo puder aceitar uma matriz, certifique-se de que sua regra use o formato de matriz. No exemplo anterior, você usa `containers[*]` e não`containers`. O Guard avalia corretamente ao percorrer os dados quando encontra somente o formulário de valor único.

**nota**  
Sempre use o formulário de matriz ao expressar acesso a uma cláusula de regra quando um atributo aceita uma matriz. O Guard avalia corretamente mesmo no caso de um único valor ser usado.

## Usando o formulário `spec.containers[*]` em vez de `spec.containers`
<a name="containers"></a>

As consultas de proteção retornam uma coleção de valores resolvidos. Quando você usa o formulário`spec.containers`, os valores resolvidos para a consulta contêm a matriz referida por`containers`, não os elementos dentro dela. Ao usar o formulário`spec.containers[*]`, você se refere a cada elemento individual contido. Lembre-se de usar o `[*]` formulário sempre que quiser avaliar cada elemento contido na matriz.

## Usando `this` para referenciar o valor do contexto atual
<a name="this"></a>

Ao criar uma regra do Guard, você pode referenciar o valor do contexto usando`this`. Muitas vezes, `this` está implícito porque está vinculado ao valor do contexto. Por exemplo,`this.apiVersion`,`this.kind`, e `this.spec` estão vinculados à raiz ou ao documento. Por outro lado, `this.resources` está vinculado a cada valor para`containers`, como `/spec/containers/0/` `/spec/containers/1` e. Da mesma forma, `this.cpu` e `this.memory` mapeie os limites, especificamente `/spec/containers/0/resources/limits` `/spec/containers/1/resources/limits` e. 

No próximo exemplo, a regra anterior para a configuração do Kubernetes Pod foi reescrita para ser usada explicitamente. `this`

```
rule ensure_container_limits_are_enforced
    when this.apiVersion == 'v1'
         this.kind == 'Pod' 
{
    this.spec.containers[*] {
        this.resources.limits {
            #
            # Ensure that cpu attribute is set
            #
            this.cpu exists
            <<
                Id: K8S_REC_18
                Description: CPU limit must be set for the container
            >> 

            #
            # Ensure that memory attribute is set
            #
            this.memory exists
            <<
                Id: K8S_REC_22
                Description: Memory limit must be set for the container
            >>
        }
    }
}
```

Você não precisa usar `this` explicitamente. No entanto, a `this` referência pode ser útil ao trabalhar com escalares, conforme mostrado no exemplo a seguir.

```
InputParameters.TcpBlockedPorts[*] {
    this in r[0, 65535) 
    <<
        result: NON_COMPLIANT
        message: TcpBlockedPort not in range (0, 65535)
    >>
}
```

No exemplo anterior, `this` é usado para se referir a cada número de porta.

## Possíveis erros com o uso de implícito `this`
<a name="common-errors"></a>

Ao criar regras e cláusulas, há alguns erros comuns ao referenciar elementos do valor de contexto implícito`this`. Por exemplo, considere o seguinte dado de entrada para avaliar (isso deve ser aprovado).

```
resourceType: 'AWS::EC2::SecurityGroup'
InputParameters:
  TcpBlockedPorts: [21, 22, 110]
configuration:
  ipPermissions:
  - fromPort: 172
    ipProtocol: tcp
    ipv6Ranges: []
    prefixListIds: []
    toPort: 172
    userIdGroupPairs: []
    ipv4Ranges:
      - cidrIp: "0.0.0.0/0"   
  - fromPort: 89
    ipProtocol: tcp
    ipv6Ranges:
      - cidrIpv6: "::/0"
    prefixListIds: []
    toPort: 109
    userIdGroupPairs: []
    ipv4Ranges:
      - cidrIp: 10.2.0.0/24
```

Quando testada em relação ao modelo anterior, a regra a seguir resulta em um erro porque faz uma suposição incorreta de aproveitar o implícito. `this`

```
rule check_ip_procotol_and_port_range_validity
{
    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            ipProtocol != '-1' # this here refers to each ipPermission instance
            InputParameters.TcpBlockedPorts[*] {
                fromPort > this or 
                toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }                
        }
    }
}
```

Para ver esse exemplo, salve o arquivo de regras anterior com o nome `any_ip_ingress_check.guard` e os dados com o nome `ip_ingress.yaml` do arquivo. Em seguida, execute o `validate` comando a seguir com esses arquivos.

```
cfn-guard validate -r any_ip_ingress_check.guard -d ip_ingress.yaml --show-clause-failures
```

Na saída a seguir, o mecanismo indica que sua tentativa de recuperar uma propriedade `InputParameters.TcpBlockedPorts[*]` no valor `/configuration/ipPermissions/0` `/configuration/ipPermissions/1` falhou.

```
Clause #2     FAIL(Block[Location[file:any_ip_ingress_check.guard, line:17, column:13]])

              Attempting to retrieve array index or key from map at Path = /configuration/ipPermissions/0, Type was not an array/object map, Remaining Query = InputParameters.TcpBlockedPorts[*]

Clause #3     FAIL(Block[Location[file:any_ip_ingress_check.guard, line:17, column:13]])

              Attempting to retrieve array index or key from map at Path = /configuration/ipPermissions/1, Type was not an array/object map, Remaining Query = InputParameters.TcpBlockedPorts[*]
```

Para ajudar a entender esse resultado, reescreva a regra usando referências `this` explícitas.

```
rule check_ip_procotol_and_port_range_validity
{
    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = this.configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            this.ipProtocol != '-1' # this here refers to each ipPermission instance
            this.InputParameters.TcpBlockedPorts[*] {
                this.fromPort > this or 
                this.toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }                
        }
    }
}
```

`this.InputParameters`faz referência a cada valor contido na variável`any_ip_permissions`. A consulta atribuída à variável seleciona `configuration.ipPermissions` valores que correspondem. O erro indica uma tentativa de recuperação `InputParamaters` nesse contexto, mas `InputParameters` estava no contexto raiz.

O bloco interno também faz referência a variáveis que estão fora do escopo, conforme mostrado no exemplo a seguir.

```
{
    this.ipProtocol != '-1' # this here refers to each ipPermission instance
    this.InputParameter.TcpBlockedPorts[*] { # ERROR referencing InputParameter off /configuration/ipPermissions[*]
        this.fromPort > this or # ERROR: implicit this refers to values inside /InputParameter/TcpBlockedPorts[*]
        this.toPort   < this 
        <<
            result: NON_COMPLIANT
            message: Blocked TCP port was allowed in range
        >>
    }
}
```

`this`se refere a cada valor de porta em`[21, 22, 110]`, mas também se refere a `fromPort` `toPort` e. Ambos pertencem ao escopo do bloco externo.

### Resolvendo erros com o uso implícito de `this`
<a name="common-errors-resolution"></a>

Use variáveis para atribuir e referenciar valores explicitamente. Primeiro, `InputParameter.TcpBlockedPorts` faz parte do contexto de entrada (raiz). `InputParameter.TcpBlockedPorts`Saia do bloco interno e atribua-o explicitamente, conforme mostrado no exemplo a seguir.

```
rule check_ip_procotol_and_port_range_validity
{
     let ports = InputParameters.TcpBlockedPorts[*]
    # ... cut off for illustrating change
}
```

Em seguida, consulte essa variável explicitamente.

```
rule check_ip_procotol_and_port_range_validity
{
    #
    # Important: Assigning InputParameters.TcpBlockedPorts results in an ERROR. 
    # We need to extract each port inside the array. The difference is the query
    # InputParameters.TcpBlockedPorts returns [[21, 20, 110]] whereas the query 
    # InputParameters.TcpBlockedPorts[*] returns [21, 20, 110]. 
    #
    let ports = InputParameters.TcpBlockedPorts[*]

    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            this.ipProtocol != '-1' # this here refers to each ipPermission instance
            %ports {
                this.fromPort > this or 
                this.toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }
        }
    }        
}
```

Faça o mesmo com `this` as referências internas internas`%ports`.

No entanto, todos os erros ainda não foram corrigidos porque o loop interno `ports` ainda tem uma referência incorreta. O exemplo a seguir mostra a remoção da referência incorreta.

```
rule check_ip_procotol_and_port_range_validity
{
    #
    # Important: Assigning InputParameters.TcpBlockedPorts results in an ERROR. 
    # We need to extract each port inside the array. The difference is the query
    # InputParameters.TcpBlockedPorts returns [[21, 20, 110]] whereas the query 
    # InputParameters.TcpBlockedPorts[*] returns [21, 20, 110].
    #
    let ports = InputParameters.TcpBlockedPorts[*]

    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[
        #
        # if either ipv4 or ipv6 that allows access from any address
        #
        some ipv4Ranges[*].cidrIp == '0.0.0.0/0' or
        some ipv6Ranges[*].cidrIpv6 == '::/0'

        #
        # the ipProtocol is not UDP
        #
        ipProtocol != 'udp' ]
        
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            ipProtocol != '-1'
            <<
              result: NON_COMPLIANT
              check_id: HUB_ID_2334
              message: Any IP Protocol is allowed
            >>

            when fromPort exists 
                 toPort exists 
            {
                let each_any_ip_perm = this
                %ports {
                    this < %each_any_ip_perm.fromPort or
                    this > %each_any_ip_perm.toPort
                    <<
                        result: NON_COMPLIANT
                        check_id: HUB_ID_2340
                        message: Blocked TCP port was allowed in range
                    >>
                }
            }
        }       
    }   
}
```

Em seguida, execute o `validate` comando novamente. Desta vez, passa.

```
cfn-guard validate -r any_ip_ingress_check.guard -d ip_ingress.yaml --show-clause-failures
```

A seguir está a saída do `validate` comando.

```
ip_ingress.yaml Status = PASS
PASS rules
check_ip_procotol_and_port_range_validity    PASS
```

Para testar essa abordagem em busca de falhas, o exemplo a seguir usa uma alteração na carga útil.

```
resourceType: 'AWS::EC2::SecurityGroup'
InputParameters:
  TcpBlockedPorts: [21, 22, 90, 110]
configuration:
  ipPermissions:
    - fromPort: 172
      ipProtocol: tcp
      ipv6Ranges: []
      prefixListIds: []
      toPort: 172
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: "0.0.0.0/0"   
    - fromPort: 89
      ipProtocol: tcp
      ipv6Ranges:
        - cidrIpv6: "::/0"
      prefixListIds: []
      toPort: 109
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 10.2.0.0/24
```

90 está dentro do intervalo de 89 a 109 que tem qualquer IPv6 endereço permitido. A seguir está a saída do `validate` comando depois de executá-lo novamente.

```
Clause #3           FAIL(Clause(Location[file:any_ip_ingress_check.guard, line:43, column:21], Check: _  LESS THAN %each_any_ip_perm.fromPort))
                    Comparing Int((Path("/InputParameters/TcpBlockedPorts/2"), 90)) with Int((Path("/configuration/ipPermissions/1/fromPort"), 89)) failed
                    (DEFAULT: NO_MESSAGE)
Clause #4           FAIL(Clause(Location[file:any_ip_ingress_check.guard, line:44, column:21], Check: _  GREATER THAN %each_any_ip_perm.toPort))
                    Comparing Int((Path("/InputParameters/TcpBlockedPorts/2"), 90)) with Int((Path("/configuration/ipPermissions/1/toPort"), 109)) failed

                                            result: NON_COMPLIANT
                                            check_id: HUB_ID_2340
                                            message: Blocked TCP port was allowed in range
```