Search code examples
open-policy-agent

OPA REGO: How to find all not matching items in another dictionary?


Given input as follow:

{
    "source": "serverA",
    "destination": "serverB",
    "rules": {
        "tcp": {
            "ssh": [
                22
            ],
            "https": [
                8443
            ]
        },
        "udp": [
            53
        ]
    }
}

and data source:

{
    "allowedProtocolTypeToPortMapping": {
        "tcp": {
            "ssh": [22],
            "https": [443]
        },
        "udp": {
            "dns": [53]
        },
        "icmp": {
            "type": [0]
        }
    }
}

I want to create a policy that checks all rules and shows the ones that are not compliant to data source. In this example that would be port 8443 with protocol type https that is not compliant (allowed only 443). What is the best way to achieve it using rego language?


Solution

  • Here's one way of doing it.

    package play
    
    violations[msg] {
        # Traverse input object stopping only at array values
        walk(input.rules, [path, value])
        is_array(value)
        
        # Get corresponding list of allowed ports from matching path
        allowedPorts := select(data.allowedProtocolTypeToPortMapping, path)
        
        # At this point, we have one array of ports from the input (value)
        # and one array of allowed ports. Converting both to sets would allow
        # us to compare the two using simple set intersection - i.e. checking
        # that all ports in the input are also in the allowed ports.
        ip := {port | port := value[_]}
        ap := {port | port := allowedPorts[_]}
        
        # Unless all ports in the input array are in both the input and the
        # allowed ports, it's a violation
        count(ip) != count(ip & ap)
        
        msg := concat(".", path)
    }
     
    select(o, path) = value {
        walk(o, [path, value])
    }
    
    

    Rego Playground example available here.