Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d64353f
added AWS::EC2::SecurityGroupIngress support for http_port_open
cx-andre-pereira Oct 6, 2025
f9cbe6f
added AWS::EC2::SecurityGroupIngress support for remote_desktop_port_…
cx-andre-pereira Oct 6, 2025
44600af
added 'AWS::EC2::SecurityGroupEgress' support for security_groups_all…
cx-andre-pereira Oct 6, 2025
dcded6d
added AWS::EC2::SecurityGroupIngress support to security_groups_with_…
cx-andre-pereira Oct 6, 2025
f982d92
added ipv6 and inline ingress support for fully_open_ingress
cx-andre-pereira Oct 6, 2025
719dc2b
fix results
cx-andre-pereira Oct 7, 2025
a8080b0
cloudformation common library
cx-andre-pereira Oct 7, 2025
65599fd
ec2 and elb copyed from part one
cx-andre-pereira Oct 7, 2025
72b8e87
dockerfile update
cx-andre-pereira Oct 7, 2025
3479027
final expected results fix
cx-andre-pereira Oct 7, 2025
8a68064
e2e fix
cx-andre-pereira Oct 7, 2025
f316bc4
fixed secur.group. ingress/egress with all protocols checking for -1 …
cx-andre-pereira Oct 7, 2025
7877e0b
added support for outdated RDS DBsec group for security_group_rule_wi…
cx-andre-pereira Oct 7, 2025
0735b11
e2e update, fix positive sample in fixtures, updated tests for http_p…
cx-andre-pereira Oct 7, 2025
7bfb595
Merge branch 'master' into AST-115332-FN-For-cloudformation-ingress_e…
cx-andre-pereira Oct 7, 2025
86e66ab
missing ipv6 testing for http_port_open and remote_desktop_port_open_…
cx-andre-pereira Oct 8, 2025
370eea8
Merge branch 'AST-115332-FN-For-cloudformation-ingress_egress_queries…
cx-andre-pereira Oct 8, 2025
0f8faba
changing simId transition to part1 PR
cx-andre-pereira Oct 8, 2025
8eaa06f
expected results fix
cx-andre-pereira Oct 8, 2025
d0c2413
Merge branch 'master' into AST-115332-FN-For-cloudformation-ingress_e…
cx-andre-pereira Oct 8, 2025
d8bf0a3
Merge branch 'master' into AST-115332-FN-For-cloudformation-ingress_e…
cx-andre-pereira Oct 8, 2025
4135b05
minor adjustments to most queries
cx-andre-pereira Oct 9, 2025
944cfb2
Merge branch 'master' into AST-115332-FN-For-cloudformation-ingress_e…
cx-andre-pereira Oct 9, 2025
49f3134
riskscore placed
cx-andre-pereira Oct 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 29 additions & 15 deletions assets/libraries/cloudformation.rego
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,37 @@ checkAction(currentAction, actionToCompare) {
contains(lower(action), actionToCompare)
}

# Dictionary of UDP ports
udpPortsMap = {
53: "DNS",
137: "NetBIOS Name Service",
138: "NetBIOS Datagram Service",
139: "NetBIOS Session Service",
161: "SNMP",
389: "LDAP",
1434: "MSSQL Browser",
2483: "Oracle DB SSL",
2484: "Oracle DB SSL",
5432: "PostgreSQL",
11211: "Memcached",
11214: "Memcached SSL",
11215: "Memcached SSL",
get_ingress_list(resource) = result {
ingress_array_types := ["AWS::EC2::SecurityGroup","AWS::RDS::DBSecurityGroup"]
resource.Type == ingress_array_types[_]
result := resource.Properties[get_field(resource.Type)]
} else = result {
# assumes it is a "AWS::EC2::SecurityGroupIngress" or "AWS::EC2::SecurityGroupEgress" resource
result := [resource.Properties]
}

get_field("AWS::EC2::SecurityGroup") = "SecurityGroupIngress"
get_field("AWS::RDS::DBSecurityGroup") = "DBSecurityGroupIngress" #legacy

containsPort(from_port, to_port, port) {
from_port <= port
to_port >= port
}

entireNetwork(rule) {
rule.CidrIp == "0.0.0.0/0"
} else {
rule.CidrIpv6 == common_lib.unrestricted_ipv6[_]
}

getProtocolList(protocol) = list {
protocol == "-1"
list = ["TCP", "UDP"]
} else = list {
common_lib.inArray(["TCP", "UDP"],upper(protocol))
list = [upper(protocol)]
} else = []

# Get content of the resource(s) based on the type
getResourcesByType(resources, type) = list {
list = [resource | resources[i].Type == type; resource := resources[i]]
Expand Down
3 changes: 3 additions & 0 deletions assets/libraries/common.rego
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ emptyOrNull("") = true

emptyOrNull(null) = true

# List of valid forms for the "::/0" ipv6 address
unrestricted_ipv6 := ["::/0","0000:0000:0000:0000:0000:0000:0000:0000/0","0:0:0:0:0:0:0:0/0"]

# Checks if an IP is private
isPrivateIP(ipVal) {
private_ips := ["10.0.0.0/8", "192.168.0.0/16", "172.16.0.0/12", "fc00::/8", "fd00::/8"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,95 @@ package Cx
import data.generic.common as common_lib
import data.generic.cloudformation as cf_lib

isAccessibleFromEntireNetwork(ingress) {
endswith(ingress.CidrIp, "/0")
}

getProtocolList(protocol) = list {
sprintf("%v", [protocol]) == "-1"
list = ["TCP", "UDP"]
} else = list {
upper(protocol) == "TCP"
list = ["TCP"]
} else = list {
upper(protocol) == "UDP"
list = ["UDP"]
}

CxPolicy[result] {
############# document and resource
resources := input.document[i].Resources
doc := input.document[i]
resources := doc.Resources

ec2Instance = resources[ec2_instance_name]
ec2Instance.Type == "AWS::EC2::Instance"

ec2InstanceList = [{"name": key, "properties": ec2Instance} |
ec2Instance := resources[key]
ec2Instance.Type == "AWS::EC2::Instance"
]
sec_group := resources[sec_group_name]
sec_group.Type == "AWS::EC2::SecurityGroup"

ec2Instance := ec2InstanceList[_]
cf_lib.get_name(ec2Instance.Properties.SecurityGroupIds[_]) == sec_group_name
ingresses_with_names := search_for_standalone_ingress(sec_group_name, doc)

securityGroupList = [{"name": key, "properties": secGroup} |
secGroup := resources[key]
secGroup.Type == "AWS::EC2::SecurityGroup"
]
ingress_list := array.concat(ingresses_with_names.ingress_list, get_inline_ingress_list(sec_group))
ingress := ingress_list[ing_index]

secGroup := securityGroupList[_]
# check that it is exposed
cidr_fields := {"CidrIp", "CidrIpv6"}
endswith(ingress[cidr_fields[c]], "/0")

ec2Instance.properties.Properties.SecurityGroups[_] == secGroup.name
#check which sensitive port numbers are included
ports := get_sensitive_ports(ingress)

ingress := secGroup.properties.Properties.SecurityGroupIngress[l]
results := get_search_values(ing_index, sec_group_name, ingresses_with_names.names)

protocols := getProtocolList(ingress.IpProtocol)
protocol := protocols[m]
portsMap := {
"TCP": common_lib.tcpPortsMap,
"UDP": cf_lib.udpPortsMap,
result := {
"documentId": doc.id,
"resourceType": results.type,
"resourceName": cf_lib.get_resource_name(sec_group, sec_group_name),
"searchKey": results.searchKey,
"searchValue": ports[x].searchValue,
"issueType": "IncorrectValue",
"keyExpectedValue": sprintf("%s should not be allowed in EC2 security group for instance '%s'", [ports[x].value, ec2_instance_name]),
"keyActualValue": sprintf("%s is allowed in EC2 security group for instance '%s'", [ports[x].value, ec2_instance_name]),
"searchLine": results.searchLine,
}
}

get_sensitive_ports(ingress) = ports {
ingress.IpProtocol == "-1"
ports := [{
"value" : "ALL PORTS (ALL PROTOCOLS:0-65535)",
"searchValue" : "ALL PROTOCOLS,0-65535"
}]
} else = ports {
portName := common_lib.tcpPortsMap[portNumber]
protocol := upper(ingress.IpProtocol)
protocol == ["TCP", "UDP"][_]
cf_lib.containsPort(ingress.FromPort, ingress.ToPort, portNumber)

############# Checks
isAccessibleFromEntireNetwork(ingress)
ports := [x | x := {
"value" : sprintf("%s (%s:%d)", [portName, protocol, portNumber]),
"searchValue" : sprintf("%s,%d", [protocol, portNumber]),
}]
}

# is in ports range
portRange := numbers.range(ingress.FromPort, ingress.ToPort)
portNumber := portRange[idx]
portName := portsMap[protocol][portNumber]
search_for_standalone_ingress(sec_group_name, doc) = ingresses_with_names {
resources := doc.Resources

############# Result
result := {
"documentId": input.document[i].id,
"resourceType": "AWS::EC2::SecurityGroup",
"resourceName": cf_lib.get_resource_name(secGroup.properties, secGroup.name),
"searchKey": sprintf("Resources.%s.SecurityGroupIngress", [secGroup.name]),
"searchValue": sprintf("%s/%s:%d", [ec2Instance.name, protocol, portNumber]),
"issueType": "IncorrectValue",
"keyExpectedValue": sprintf("%s (%s:%d) should not be allowed in EC2 security group for instance %s", [portName, protocol, portNumber, ec2Instance.name]),
"keyActualValue": sprintf("%s (%s:%d) is allowed in EC2 security group for instance %s", [portName, protocol, portNumber, ec2Instance.name]),
"searchLine": common_lib.build_search_line(["Resources", secGroup.name, "Properties", "SecurityGroupIngress", l], []),
names := [name |
ingress := resources[name]
ingress.Type == "AWS::EC2::SecurityGroupIngress"
cf_lib.get_name(ingress.Properties.GroupId) == sec_group_name
]

ingresses_with_names := {
"ingress_list": [resources[name].Properties | name := names[_]],
"names": names
}
} else = { "ingress_list": [], "names": []}


get_search_values(ing_index, sec_group_name, names_list) = results {
ing_index < count(names_list) # if ingress is standalone

results := {
"searchKey" : sprintf("Resources.%s.Properties", [names_list[ing_index]]),
"searchLine" : common_lib.build_search_line(["Resources", names_list[ing_index], "Properties"], []),
"type" : "AWS::EC2::SecurityGroupIngress"
}
} else = results {

results := {
"searchKey" : sprintf("Resources.%s.Properties.SecurityGroupIngress[%d]", [sec_group_name, ing_index-count(names_list)]),
"searchLine" : common_lib.build_search_line(["Resources", sec_group_name, "Properties", "SecurityGroupIngress", ing_index-count(names_list)], []),
"type" : "AWS::EC2::SecurityGroup"
}
}

get_inline_ingress_list(group) = [] {
not common_lib.valid_key(group.Properties,"SecurityGroupIngress")
} else = group.Properties.SecurityGroupIngress
Original file line number Diff line number Diff line change
@@ -1,29 +1,50 @@
AWSTemplateFormatVersion: 2010-09-09T00:00:00Z
Resources:
SafeSecGroup:
EC2Instance01:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-79fd7eee
InstanceType: t3.medium
SecurityGroupIds:
- !Ref Negative1IPv4_1
- !Ref Negative1IPv4_2
- !Ref Negative1ArrayTestIPv4
KeyName: my-new-rsa-key

Negative1IPv4_1:
Type: AWS::EC2::SecurityGroup
Properties:
SecurityGroupEgress:
- IpProtocol: tcp
GroupDescription: "Incorrect protocol: ICMP"
VpcId: !Ref MyVPC
SecurityGroupIngress:
- IpProtocol: "icmp"
FromPort: 22
ToPort: 22
CidrIp: 127.0.0.1/32
GroupDescription: Allow http and ssh
VpcId: my-vpc
CidrIp: "10.0.0.0/0"

Negative1IPv4_2:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Unknown port: port 5000"
VpcId: !Ref MyVPC
SecurityGroupIngress:
- FromPort: 80
ToPort: 80
CidrIp: 127.0.0.1/32
IpProtocol: tcp
- ToPort: 77
CidrIp: 127.0.0.1/32
IpProtocol: all
FromPort: 77
MyNegativeEC2Instance:
Type: AWS::EC2::Instance
- IpProtocol: "tcp"
FromPort: 5000
ToPort: 5000
CidrIp: "192.168.0.0/0"

Negative1ArrayTestIPv4:
Type: AWS::EC2::SecurityGroup
Properties:
SecurityGroups:
- SafeSecGroup
KeyName: my-new-rsa-key
ImageId: ami-79fd7eee
InstanceType: t3.medium
GroupDescription: "Mixed incorrect CIDRs and protocols"
VpcId: !Ref MyVPC
SecurityGroupIngress:
# incorrect cidr (not exposed)
- IpProtocol: "udp"
FromPort: 22
ToPort: 22
CidrIp: "8.8.0.0/16"
# all fields "incorrect"
- IpProtocol: "icmp"
FromPort: 5000
ToPort: 5000
CidrIp: "10.68.0.0/14"

This file was deleted.

Loading
Loading