Evaluate Security Group Rules
Problem Statement
A security group is a stateful, allow-only firewall: there are no deny rules, and anything not explicitly permitted is dropped. Given a group's inbound rules and a batch of attempted connections, decide each one.
Background
Evaluation is a pure OR over the rules — a connection is allowed if any rule matches, denied otherwise. Default-deny is what makes a security group safe: forgetting a rule closes a port, it never opens one.
A rule matches a connection when all three hold:
- Protocol: rule["protocol"] == conn["protocol"], or the rule's protocol is "-1" meaning all protocols.
- Port: rule["from_port"] <= conn["port"] <= rule["to_port"] — the range is inclusive at both ends. A rule for "-1" is given as from_port: 0, to_port: 65535.
- Source: the connection's source IP falls inside the rule's CIDR. 0.0.0.0/0 is the whole internet; a /32 is one host.
Rules are unordered, and order makes no difference to the outcome.
Your Task
Implement:
def evaluate_rules(rules, connections):
Return a list of "ALLOW" / "DENY" strings, one per connection, in input order.
Input Format
- rules: list of dicts with "protocol" ("tcp", "udp", "icmp" or "-1"), "from_port", "to_port", "cidr".
- connections: list of dicts with "protocol", "port", "source".
Output Format
- A list of strings, each "ALLOW" or "DENY".
Sample
rules = [{"protocol": "tcp", "from_port": 22, "to_port": 22, "cidr": "10.0.0.0/16"},
{"protocol": "tcp", "from_port": 8000, "to_port": 8010, "cidr": "0.0.0.0/0"}]
conns = [{"protocol": "tcp", "port": 22, "source": "10.0.3.4"},
{"protocol": "tcp", "port": 22, "source": "203.0.113.9"},
{"protocol": "tcp", "port": 8010, "source": "203.0.113.9"}]
print(evaluate_rules(rules, conns))
Output:
['ALLOW', 'DENY', 'ALLOW']
SSH is open only to the VPC, so the public source is dropped; port 8010 is the inclusive upper end of the serving range and is open to everyone.
Example
rules = [{"protocol": "tcp", "from_port": 22, "to_port": 22, "cidr": "10.0.0.0/16"}, {"protocol": "tcp", "from_port": 8000, "to_port": 8010, "cidr": "0.0.0.0/0"}]
conns = [{"protocol": "tcp", "port": 22, "source": "10.0.3.4"}, {"protocol": "tcp", "port": 22, "source": "203.0.113.9"}, {"protocol": "tcp", "port": 8010, "source": "203.0.113.9"}]
print(evaluate_rules(rules, conns))['ALLOW', 'DENY', 'ALLOW']
The first connection matches the SSH rule on all three axes. The second is the same port and protocol but its source is outside 10.0.0.0/16, and since no other rule covers port 22 it is denied by default. The third hits the inclusive upper bound of the 8000-8010 range from a source inside 0.0.0.0/0, so it is allowed.
Constraints
- 0 <= len(rules) <= 100, 0 <= len(connections) <= 100
- Security groups are allow-only: a connection matching no rule is DENY
- Port ranges are inclusive at both ends
- Protocol
"-1"matches any protocol and is given with ports 0-65535 - Source matching is by CIDR containment;
0.0.0.0/0matches every address - Return one verdict per connection, in input order