

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Scrittura di clausole per eseguire valutazioni basate sul contesto
<a name="context-aware-evaluations"></a>

AWS CloudFormation Guard le clausole vengono valutate sulla base di dati gerarchici. Il motore di valutazione Guard risolve le interrogazioni sui dati in entrata seguendo i dati gerarchici come specificato, utilizzando una semplice notazione punteggiata. Spesso sono necessarie più clausole per la valutazione rispetto a una mappa di dati o a una raccolta. Guard fornisce una sintassi comoda per scrivere tali clausole. Il motore è contestualmente consapevole e utilizza i dati corrispondenti associati per le valutazioni.

Di seguito è riportato un esempio di configurazione di Kubernetes Pod con contenitori, a cui è possibile applicare valutazioni sensibili al contesto.

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

Puoi creare clausole Guard per valutare questi dati. Quando si valuta un file di regole, il contesto è l'intero documento di input. Di seguito sono riportate alcune clausole di esempio che convalidano l'applicazione dei limiti per i contenitori specificati in 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
            >>
        }
    }
}
```

## Comprensione nelle valutazioni `context`
<a name="context"></a>

A livello di blocco di regole, il contesto in entrata è il documento completo. Le valutazioni della `when` condizione vengono eseguite in base a questo contesto radice in entrata in cui si trovano gli attributi `apiVersion` and`kind`. Nell'esempio precedente, queste condizioni restituiscono a`true`.

Ora, attraversate la gerarchia `spec.containers[*]` mostrata nell'esempio precedente. Per ogni attraversamento della gerarchia, il valore del contesto cambia di conseguenza. Al termine dell'attraversamento del `spec` blocco, il contesto cambia, come illustrato nell'esempio seguente.

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

Dopo aver attraversato l'`containers`attributo, il contesto viene mostrato nell'esempio seguente.

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

## Comprensione dei loop
<a name="loops"></a>

È possibile utilizzare l'espressione `[*]` per definire un ciclo per tutti i valori contenuti nell'array per l'`containers`attributo. Il blocco viene valutato per ogni elemento al suo interno`containers`. Nel frammento di regola dell'esempio precedente, le clausole contenute all'interno del blocco definiscono i controlli da convalidare rispetto a una definizione di contenitore. Il blocco di clausole contenuto all'interno viene valutato due volte, una volta per ogni definizione di contenitore.

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

Per ogni iterazione, il valore di contesto è il valore in corrispondenza dell'indice corrispondente.

**Nota**  
L'unico formato di accesso all'indice supportato è `[<integer>]` o`[*]`. Attualmente, Guard non supporta intervalli come`[2..4]`.

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

Spesso nei luoghi in cui viene accettato un array, vengono accettati anche valori singoli. Ad esempio, se è presente un solo contenitore, l'array può essere eliminato e viene accettato il seguente input.

```
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 un attributo può accettare un array, assicurati che la regola utilizzi il modulo di matrice. Nell'esempio precedente, si usa `containers[*]` e non`containers`. Guard valuta correttamente quando attraversa i dati quando incontra solo la forma a valore singolo.

**Nota**  
Usa sempre il modulo di matrice quando esprimi l'accesso a una clausola di regola quando un attributo accetta un array. Guard valuta correttamente anche nel caso in cui venga utilizzato un solo valore.

## Utilizzando il modulo `spec.containers[*]` invece di `spec.containers`
<a name="containers"></a>

Le query Guard restituiscono una raccolta di valori risolti. Quando si utilizza il modulo`spec.containers`, i valori risolti per la query contengono l'array a cui si fa riferimento`containers`, non gli elementi al suo interno. Quando si utilizza il modulo`spec.containers[*]`, si fa riferimento a ogni singolo elemento contenuto. Ricordatevi di usare il `[*]` modulo ogni volta che intendete valutare ogni elemento contenuto nell'array.

## Usato `this` per fare riferimento al valore di contesto corrente
<a name="this"></a>

Quando si crea una regola Guard, è possibile fare riferimento al valore di contesto utilizzando`this`. Spesso `this` è implicito perché è legato al valore del contesto. Ad esempio, `this.apiVersion``this.kind`, e `this.spec` sono associati alla radice o al documento. Al contrario, `this.resources` è associato a ciascun valore per`containers`, ad esempio `/spec/containers/0/` e`/spec/containers/1`. Allo stesso modo, `this.cpu` e `this.memory` mappa fino ai limiti, in particolare `/spec/containers/0/resources/limits` e`/spec/containers/1/resources/limits`. 

Nel prossimo esempio, la regola precedente per la configurazione di Kubernetes Pod viene riscritta per essere utilizzata in modo esplicito. `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
            >>
        }
    }
}
```

Non è necessario utilizzarlo in modo esplicito. `this` Tuttavia, il `this` riferimento può essere utile quando si lavora con gli scalari, come mostrato nell'esempio seguente.

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

Nell'esempio precedente, `this` viene utilizzato per fare riferimento a ciascun numero di porta.

## Potenziali errori con l'uso di dati impliciti `this`
<a name="common-errors"></a>

Quando si creano regole e clausole, si verificano alcuni errori comuni quando si fa riferimento a elementi del valore di contesto implicito. `this` Ad esempio, considerate il seguente dato di input rispetto al quale effettuare la valutazione (questo deve passare).

```
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 viene testata rispetto al modello precedente, la regola seguente genera un errore perché presuppone erroneamente di sfruttare l'implicito. `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
                >>
            }                
        }
    }
}
```

Per illustrare questo esempio, salvate il file delle regole precedenti con il nome `any_ip_ingress_check.guard` e i dati con il nome del file. `ip_ingress.yaml` Quindi, esegui il `validate` comando seguente con questi file.

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

Nell'output seguente, il motore indica che il suo tentativo di recuperare una proprietà `InputParameters.TcpBlockedPorts[*]` sul valore `/configuration/ipPermissions/1` è `/configuration/ipPermissions/0` fallito.

```
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[*]
```

Per facilitare la comprensione di questo risultato, riscrivi la regola utilizzando riferimenti `this` espliciti.

```
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`fa riferimento a ogni valore contenuto nella variabile. `any_ip_permissions` L'interrogazione assegnata alla variabile seleziona `configuration.ipPermissions` i valori che corrispondono. L'errore indica un tentativo di recupero `InputParamaters` in questo contesto, ma si `InputParameters` è verificato nel contesto principale.

Il blocco interno fa riferimento anche a variabili che non rientrano nell'ambito, come illustrato nell'esempio seguente.

```
{
    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`si riferisce a ogni valore di porta in`[21, 22, 110]`, ma si riferisce anche a `fromPort` and`toPort`. Entrambi appartengono all'ambito del blocco esterno.

### Risoluzione degli errori con l'uso implicito di `this`
<a name="common-errors-resolution"></a>

Utilizzate le variabili per assegnare e fare riferimento in modo esplicito ai valori. Innanzitutto, `InputParameter.TcpBlockedPorts` fa parte del contesto di input (root). `InputParameter.TcpBlockedPorts`Esci dal blocco interno e assegnalo in modo esplicito, come mostrato nell'esempio seguente.

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

Quindi, fate riferimento a questa variabile in modo esplicito.

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

Fate lo stesso per i `this` riferimenti interni`%ports`.

Tuttavia, non tutti gli errori sono ancora stati corretti perché il loop interno contiene `ports` ancora un riferimento errato. L'esempio seguente mostra la rimozione del riferimento errato.

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

Quindi, esegui nuovamente il `validate` comando. Questa volta, passa.

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

Quanto segue è l'output del `validate` comando.

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

Per testare questo approccio in caso di errori, l'esempio seguente utilizza una modifica del payload.

```
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 rientra nell'intervallo compreso tra 89 e 109 a cui è consentito qualsiasi indirizzo. IPv6 Di seguito è riportato l'output del `validate` comando dopo averlo eseguito nuovamente.

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