

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Redacción de cláusulas para realizar evaluaciones sensibles al contexto
<a name="context-aware-evaluations"></a>

AWS CloudFormation Guard las cláusulas se evalúan en función de los datos jerárquicos. El motor de evaluación Guard resuelve las consultas con los datos entrantes siguiendo los datos jerárquicos según se especifique, mediante una simple notación punteada. Con frecuencia, se necesitan varias cláusulas para realizar la evaluación en función de un mapa de datos o de una colección. Guard proporciona una sintaxis práctica para escribir dichas cláusulas. El motor tiene en cuenta el contexto y utiliza los datos correspondientes asociados para las evaluaciones.

El siguiente es un ejemplo de una configuración de Kubernetes Pod con contenedores, a la que puedes aplicar evaluaciones contextuales.

```
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
```

Puedes crear cláusulas Guard para evaluar estos datos. Al evaluar un archivo de reglas, el contexto es todo el documento de entrada. A continuación, se muestran ejemplos de cláusulas que validan la aplicación de los límites a los contenedores especificados en un 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
            >>
        }
    }
}
```

## Comprensión `context` en las evaluaciones
<a name="context"></a>

A nivel de bloque de reglas, el contexto entrante es el documento completo. Las evaluaciones de la `when` condición se realizan en relación con este contexto raíz entrante en el que se encuentran `kind` los atributos `apiVersion` y. En el ejemplo anterior, estas condiciones se evalúan como`true`.

Ahora, recorra la jerarquía que `spec.containers[*]` se muestra en el ejemplo anterior. Para cada recorrido de la jerarquía, el valor del contexto cambia en consecuencia. Una vez finalizado el recorrido del `spec` bloque, el contexto cambia, como se muestra en el siguiente ejemplo.

```
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
```

Tras recorrer el `containers` atributo, el contexto se muestra en el siguiente ejemplo.

```
- 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
```

## Entender los bucles
<a name="loops"></a>

Puede utilizar la expresión `[*]` para definir un bucle para todos los valores contenidos en la matriz del `containers` atributo. El bloque se evalúa para cada uno de los elementos que contiene`containers`. En el fragmento de regla del ejemplo anterior, las cláusulas contenidas en el bloque definen las comprobaciones que deben validarse con respecto a una definición de contenedor. El bloque de cláusulas que contiene se evalúa dos veces, una para cada definición de contenedor.

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

Para cada iteración, el valor de contexto es el valor del índice correspondiente.

**nota**  
El único formato de acceso al índice admitido es `[<integer>]` o`[*]`. Actualmente, Guard no admite rangos como`[2..4]`.

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

A menudo, en los lugares donde se acepta una matriz, también se aceptan valores individuales. Por ejemplo, si solo hay un contenedor, se puede eliminar la matriz y aceptar la siguiente entrada.

```
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
```

Si un atributo puede aceptar una matriz, asegúrese de que la regla utilice la forma de matriz. En el ejemplo anterior, se utiliza `containers[*]` y no`containers`. Guard evalúa correctamente al recorrer los datos cuando encuentra solo el formulario de un solo valor.

**nota**  
Utilice siempre la forma matricial al expresar el acceso a una cláusula de regla cuando un atributo acepte una matriz. Guard evalúa correctamente incluso en el caso de que se utilice un único valor.

## Utilizar el formulario `spec.containers[*]` en lugar de `spec.containers`
<a name="containers"></a>

Las consultas de protección devuelven una colección de valores resueltos. Al utilizar el formulario`spec.containers`, los valores resueltos de la consulta contienen la matriz a la que se hace referencia`containers`, no los elementos que contiene. Al utilizar el formulario`spec.containers[*]`, se hace referencia a cada elemento individual contenido. Recuerde utilizar el `[*]` formulario siempre que desee evaluar cada elemento contenido en la matriz.

## Se utiliza `this` para hacer referencia al valor del contexto actual
<a name="this"></a>

Al crear una regla de protección, puede hacer referencia al valor de contexto mediante`this`. A menudo, `this` está implícita porque está vinculada al valor del contexto. Por ejemplo, `this.apiVersion``this.kind`, y `this.spec` están enlazados a la raíz o al documento. Por el contrario, `this.resources` está enlazado a cada valor de`containers`, como `/spec/containers/0/` y`/spec/containers/1`. Del mismo modo, `this.cpu` y `this.memory` mapea los límites, específicamente `/spec/containers/0/resources/limits` y`/spec/containers/1/resources/limits`. 

En el siguiente ejemplo, la regla anterior para la configuración del Kubernetes Pod se reescribió para utilizarla de forma explícita. `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
            >>
        }
    }
}
```

No es necesario que la utilices de forma explícita. `this` Sin embargo, la `this` referencia puede resultar útil cuando se trabaja con escalares, como se muestra en el siguiente ejemplo.

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

En el ejemplo anterior, `this` se utiliza para hacer referencia a cada número de puerto.

## Posibles errores relacionados con el uso del término implícito `this`
<a name="common-errors"></a>

Al crear reglas y cláusulas, hay algunos errores comunes al hacer referencia a elementos del valor de contexto implícito`this`. Por ejemplo, considere el siguiente dato de entrada para realizar la evaluación (debe aprobarse).

```
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
```

Cuando se compara con la plantilla anterior, la siguiente regla produce un error porque supone erróneamente que se aprovecha lo 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 este ejemplo, guarde el archivo de reglas anterior con el nombre `any_ip_ingress_check.guard` y los datos con el nombre del archivo. `ip_ingress.yaml` A continuación, ejecute el siguiente `validate` comando con estos archivos.

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

En el siguiente resultado, el motor indica que su intento de recuperar una propiedad `InputParameters.TcpBlockedPorts[*]` del valor `/configuration/ipPermissions/0` `/configuration/ipPermissions/1` ha fallado.

```
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 entender mejor este resultado, reescribe la regla utilizando referencias `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`hace referencia a cada valor contenido en la variable`any_ip_permissions`. La consulta asignada a la variable selecciona `configuration.ipPermissions` los valores que coinciden. El error indica un intento de recuperación `InputParamaters` en este contexto, pero `InputParameters` estaba en el contexto raíz.

El bloque interno también hace referencia a variables que están fuera del alcance, como se muestra en el siguiente ejemplo.

```
{
    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`hace referencia a cada valor de puerto de`[21, 22, 110]`, pero también hace referencia a `fromPort` y`toPort`. Ambos pertenecen al ámbito del bloque exterior.

### Resolver errores con el uso implícito de `this`
<a name="common-errors-resolution"></a>

Utilice variables para asignar valores y hacer referencia a ellos de forma explícita. En primer lugar, `InputParameter.TcpBlockedPorts` forma parte del contexto de entrada (raíz). `InputParameter.TcpBlockedPorts`Salga del bloque interno y asígnelo de forma explícita, como se muestra en el siguiente ejemplo.

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

A continuación, consulte esta variable de forma explícita.

```
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
                >>
            }
        }
    }        
}
```

Haga lo mismo con `this` las referencias internas internas`%ports`.

Sin embargo, aún no se han corregido todos los errores porque el bucle interior `ports` todavía tiene una referencia incorrecta. En el siguiente ejemplo, se muestra la eliminación de la referencia incorrecta.

```
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
                    >>
                }
            }
        }       
    }   
}
```

A continuación, vuelva a ejecutar el `validate` comando. Esta vez, pasa.

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

El siguiente es el resultado del `validate` comando.

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

Para probar este enfoque en busca de errores, en el siguiente ejemplo se utiliza un cambio de 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 del rango de 89 a 109, y todas IPv6 las direcciones están permitidas. El siguiente es el resultado del `validate` comando después de volver a ejecutarlo.

```
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
```