diff --git a/lib/config/impact.yaml b/lib/config/impact.yaml index 7121bcc..7b87ca3 100644 --- a/lib/config/impact.yaml +++ b/lib/config/impact.yaml @@ -7,7 +7,7 @@ # Score: Score for this value status: - weight: 10 + weight: 50 values: - attached: score: 1 @@ -21,43 +21,45 @@ status: score: 0 exposure: - weight: 1 + weight: 25 values: - effectively-public: score: 1 - restricted-public: score: 0.4 - - unknown-public: - score: 0 - unrestricted-private: - score: 0.5 + score: 0.3 + - launch-public: + score: 0.1 - restricted: score: 0 - unknown: score: 0 access: - weight: 1 + weight: 25 values: - unrestricted: score: 1 - untrusted-principal: - score: 0.8 + score: 0.7 - unrestricted-principal: - score: 0.5 + score: 0.4 - cross-account-principal: - score: 0.5 - - unrestricted-actions: - score: 0.5 + score: 0.3 - dangerous-actions: - score: 0.5 + score: 0.3 + - unrestricted-actions: + score: 0.3 + - unrestricted-service: + score: 0.1 - restricted: score: 0 - unknown: score: 0 encryption: - weight: 0.1 + weight: 10 values: - unencrypted: score: 1 @@ -67,7 +69,7 @@ encryption: score: 0 environment: - weight: 1 + weight: 15 values: - production: score: 1 diff --git a/lib/context/resources/AwsEc2Subnet.py b/lib/context/resources/AwsEc2Subnet.py index e2c0fa2..7bbf4f0 100644 --- a/lib/context/resources/AwsEc2Subnet.py +++ b/lib/context/resources/AwsEc2Subnet.py @@ -25,6 +25,9 @@ def __init__( self.subnet = self.describe_subnets() if not self.subnet: return False + self.all_network_interfaces = self.describe_network_interfaces() + self.network_interfaces = self._describe_network_interfaces_interfaces() + self.instances = self._describe_network_interfaces_instances() # Associated MetaChecks self.route_tables = self.describe_route_tables() @@ -98,6 +101,52 @@ def describe_route_tables(self): return route_tables + def describe_network_interfaces(self): + response = self.client.describe_network_interfaces( + Filters=[ + { + "Name": "subnet-id", + "Values": [ + self.resource_id, + ], + }, + ], + ) + return response["NetworkInterfaces"] + + def _describe_network_interfaces_interfaces(self): + network_interfaces = {} + if self.all_network_interfaces: + for network_interface in self.all_network_interfaces: + arn = generate_arn( + network_interface["NetworkInterfaceId"], + "ec2", + "network_interface", + self.region, + self.account, + self.partition, + ) + network_interfaces[arn] = {} + return network_interfaces + + def _describe_network_interfaces_instances(self): + instances = {} + if self.all_network_interfaces: + for network_interface in self.all_network_interfaces: + if network_interface.get("Attachment") and network_interface.get( + "Attachment" + ).get("InstanceId"): + arn = generate_arn( + network_interface.get("Attachment").get("InstanceId"), + "ec2", + "instance", + self.region, + self.account, + self.partition, + ) + instances[arn] = {} + return instances + # Context Config def cidr(self): @@ -129,9 +178,35 @@ def public(self): return True return False + def attached(self): + if self.subnet: + if self.network_interfaces: + return True + return False + + def managed_services(self): + managed_services = [] + if self.all_network_interfaces: + for network_interface in self.all_network_interfaces: + if network_interface.get("RequesterManaged"): + managed_services.append(network_interface.get("Description")) + return managed_services + + def public_ips(self): + public_ips = [] + if self.all_network_interfaces: + for network_interface in self.all_network_interfaces: + if network_interface.get("Association"): + public_ips.append( + network_interface.get("Association").get("PublicIp") + ) + return public_ips + def associations(self): associations = { "route_tables": self.route_tables, + "network_interfaces": self.network_interfaces, + "instances": self.instances, } return associations @@ -142,5 +217,8 @@ def checks(self): "default": self.default(), "public": self.public(), "resource_policy": self.resource_policy(), + "public_ips": self.public_ips(), + "managed_services": self.managed_services(), + "attached": self.attached(), } return checks diff --git a/lib/impact/access.py b/lib/impact/access.py index 1cee51c..92acfb4 100644 --- a/lib/impact/access.py +++ b/lib/impact/access.py @@ -118,6 +118,16 @@ def check_bucket_acl_and_update(policy_json, policy_name): ): return {"unknown": access_checks} + # Resources without policies are unknown. + if ( + bucket_acl is None + and resource_policy is None + and inline_policies is None + and iam_policies is None + and iam_roles is None + ): + return {"unknown": access_checks} + # We return the most critical access check if "unrestricted" in access_checks: return {"unrestricted": access_checks} diff --git a/lib/impact/encryption.py b/lib/impact/encryption.py index 74f2790..7359ca8 100644 --- a/lib/impact/encryption.py +++ b/lib/impact/encryption.py @@ -79,6 +79,10 @@ def get_encryption(self, resource_arn, resource_values): ): return {"unknown": encryption_checks} + # Resources without unencrypted resources and no encryption config are unknown. + if not unencrypted_resources and resource_encryption_config is None: + return {"unknown": encryption_checks} + if unencrypted_resources or resource_encryption_config is False: return {"unencrypted": encryption_checks} diff --git a/lib/impact/exposure.py b/lib/impact/exposure.py index 2da9c92..8bb339e 100644 --- a/lib/impact/exposure.py +++ b/lib/impact/exposure.py @@ -127,6 +127,14 @@ def get_exposure(self, resource_arn, resource_values): ): return {"unknown": exposure_checks} + # Resources without public config, security groups or resource policy are unknown. + if ( + resource_public_config is None + and not unrestricted_ingress_rules + and not unrestricted_policy_access + ): + return {"unknown": exposure_checks} + # Effectively Public If: # 1. Public config and unrestricted SG ingress rules # 2. Public config and no SG and no resource policy @@ -154,7 +162,7 @@ def get_exposure(self, resource_arn, resource_values): # Restricted Private If: # 1. No public config and unrestricted SG ingress rules or unrestricted policy access - if not resource_public_config and ( + if resource_public_config is False and ( unrestricted_ingress_rules or unrestricted_policy_access ): return {"unrestricted-private": exposure_checks}