---
title: Convert AWS WAF ACLs to human-readable format
excerpt: Run the attached script to download and convert ACLs
date: 2021-06-27
updated: 2021-09-01
tags:
- aws
- security
---

I regularly need to audit my company's access control lists (ACLs) implemented in [AWS WAF](https://aws.amazon.com/waf/), as part of my job. Each ACL can be more than a thousand lines which is practically impossible to read. I wrote a script that downloads and summarises the ACLs into human-readable format; each one-thousand-line behemoth is transformed into a fifty-line summary that I can _actually_ audit.

The script is [available here](https://gitlab.com/curben/aws-scripts/-/blob/main/waf-acl.py). It currently only supports Cloudfront ACL, feel free to extend it to support regional ACL.

(Edit: 1 Sep 2021) regional ACL is now supported.

## ACL schema

The underlying format of a web ACL is JSON. In this use case, I'm only concern with two keys:

``` json
{
  "Name": "",
  "Rules": [
    {
      "Name": "",
      "Statement": {},
      "Action": {
        "Block": {}
      }
    },
    {
      "Name": "",
      "Statement": {},
      "Action": {
        "Allow": {}
      }
    }
  ]
}
```

The script names each ACL according to the value of "Name". "Rules" is an array of objects, where each object represents a rule. Each rule has an [action](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-action.html) of count, allow or block.

In each rule, there is a statement and it functions as a matching condition. Each [statement](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statements-list.html) can contain one or match statements combined using logical rule (AND, NOT, OR).

## Converted schema

A converted ACL has an array of objects, each object has three keys.

``` json
[
  {
    "Name": "",
    "Action": "",
    "Rule": ""
  }
]
```

## And/OrStatement

``` json Original
{
  "Name": "ruleA",
  "Statement": {
    "OrStatement": {
      "Statements": [
        {
          "foo": {}
        },
        {
          "bar": {}
        }
      ]
    }
  }
}
```

``` json Converted
{
  "ruleA": "foo OR bar"
}
```

## Nested And/OrStatement

``` json Original
{
  "Name": "ruleA",
  "Statement": {
    "AndStatement": {
      "Statements": [
        {
          "OrStatement": {
            "Statements": [
              {
                "foo": {}
              },
              {
                "bar": {}
              }
            ]
          }
        },
        {
          "baz": {}
        }
      ]
    }
  }
}
```

``` json Converted
{
  "ruleA": "(foo OR bar) AND baz"
}
```

## NotStatement

``` json Original
{
  "Name": "ruleA",
  "Statement": {
    "NotStatement": {
      "Statement": {
        "foo": {}
      }
    }
  }
}
```

``` json Converted
{
  "ruleA": "NOT foo"
}
```


## String match

``` json Orignal
{
  "ByteMatchStatement": {
    "SearchString": ".conf",
    "FieldToMatch": {
      "UriPath": {}
    },
    "PositionalConstraint": "ENDS_WITH"
  }
}
```

``` plain Converted
UriPath=ENDSWITH(.conf)
```
