Repository: cloud-custodian/cloud-custodian

Issue:
the unused filter for aws.security-group needs to ignore default IpPermissions
### Describe the bug

A [default security group](https://docs.aws.amazon.com/vpc/latest/userguide/default-security-group.html) has the following IpPermission when the group is created:

```
                {
                    "IpProtocol": "-1",
                    "IpRanges": [],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": [
                        {
                            "GroupId": "sg-<ID number>",
                            "UserId": <account ID>"
                        }
                    ]
                }
```

This is to allow same-account traffic to members of that security group. This poses a problem for us when we try to use the `type: unused` filter for default security groups. When the filter [scans for used groups](https://github.com/cloud-custodian/cloud-custodian/blob/da894ebc7b4a1b829845860378b58e714c6fb53f/c7n/resources/vpc.py#L885-L887), it [uses the get_sg_refs() method as a scanner](https://github.com/cloud-custodian/cloud-custodian/blob/da894ebc7b4a1b829845860378b58e714c6fb53f/c7n/resources/vpc.py#L877), which [iterates through these rules](https://github.com/cloud-custodian/cloud-custodian/blob/da894ebc7b4a1b829845860378b58e714c6fb53f/c7n/resources/vpc.py#L933-L934). If any of these rules [has a UserGroupIdPair at all](https://github.com/cloud-custodian/cloud-custodian/blob/da894ebc7b4a1b829845860378b58e714c6fb53f/c7n/resources/vpc.py#L936C49-L936C49), then the group [will be considered as used](https://github.com/cloud-custodian/cloud-custodian/blob/da894ebc7b4a1b829845860378b58e714c6fb53f/c7n/resources/vpc.py#L937-L938). The filter needs to take such rules into account because it is not necessarily true that a group with these rules (which AWS generates automatically) is used.


### What did you expect to happen?

I expect a policy similar to the following:

```yaml
policies:
  - name: enforce-empty-unused-default-sgs-all-vpcs
    description: ensure that the unused default security groups of all VPCs are empty
    resource: aws.security-group
    filters:
      - type: unused
      - type: value
        key: GroupName
        op: equal
        value: "default"
```

to match all of the security groups named `default` that are unused, regardless of the fact that they have rules automatically generated by AWS.

### Cloud Provider

Amazon Web Services (AWS)

### Cloud Custodian version and dependency information

```shell
c7n==0.9.26
```


### Policy

```shell
policies:
  - name: enforce-empty-unused-default-sgs-all-vpcs
    description: ensure that the unused default security groups of all VPCs are empty
    resource: aws.security-group
    filters:
      - type: unused
      - type: value
        key: GroupName
        op: equal
        value: "default"
```


### Relevant log/traceback output

_No response_

### Extra information or context

_No response_


Discussion:
Thanks for the detailed report :+1: . Good callout too. I think this generalizes to "ignore self-references" when determining usage, since those self-referencing rules are often user-managed outside the default group. But your point about them being a circular reference seems to still hold.
yeah, just realized that..

```
security_group_name = "launch-wizard-0"

group_id = ec2_client.create_security_group(
    GroupName=security_group_name,
    Description=security_group_name,
    VpcId=vpc_id,
)["GroupId"]

self.assertEqual(
    0,
    len(
        ec2_client.describe_security_group_rules(
            Filters=[
                {
                    "Name": "group-id",
                    "Values": [group_id],
                },
            ],
        )["SecurityGroupRules"],
    ),
)
```

```
    self.assertEqual(
E   AssertionError: 0 != 1
```


Resolving patch:
diff --git a/c7n/resources/vpc.py b/c7n/resources/vpc.py
--- a/c7n/resources/vpc.py
+++ b/c7n/resources/vpc.py
@@ -908,6 +908,8 @@ def process_rules(self, client, rule_type, group, delta):
 
 class SGUsage(Filter):
 
+    nics = ()
+
     def get_permissions(self):
         return list(itertools.chain(
             *[self.manager.get_resource_manager(m).get_permissions()
@@ -993,7 +995,9 @@ def get_sg_refs(self):
             for perm_type in ('IpPermissions', 'IpPermissionsEgress'):
                 for p in sg.get(perm_type, []):
                     for g in p.get('UserIdGroupPairs', ()):
-                        sg_ids.add(g['GroupId'])
+                        # self references aren't usage.
+                        if g['GroupId'] != sg['GroupId']:
+                            sg_ids.add(g['GroupId'])
         return sg_ids
 
     def get_ecs_cwe_sgs(self):


Test patch:
diff --git a/tests/data/placebo/sg_used_cross_ref/ec2.DescribeSecurityGroups_1.json b/tests/data/placebo/sg_used_cross_ref/ec2.DescribeSecurityGroups_1.json
--- a/tests/data/placebo/sg_used_cross_ref/ec2.DescribeSecurityGroups_1.json
+++ b/tests/data/placebo/sg_used_cross_ref/ec2.DescribeSecurityGroups_1.json
@@ -2,6 +2,37 @@
     "status_code": 200,
     "data": {
         "SecurityGroups": [
+            {
+                "Description": "sincere-amoeba",
+                "GroupName": "sincere-amoeba",
+                "IpPermissions": [],
+                "OwnerId": "644160558196",
+                "GroupId": "sg-01ba3c57af54ef22c",
+                "IpPermissionsEgress": [],
+                "VpcId": "vpc-03a8ce8bb254b625b"
+            },
+            {
+                "Description": "oriented-sole",
+                "GroupName": "oriented-sole",
+                "IpPermissions": [],
+                "OwnerId": "644160558196",
+                "GroupId": "sg-073c8f4fb3b58731d",
+                "IpPermissionsEgress": [
+                    {
+                        "IpProtocol": "-1",
+                        "IpRanges": [],
+                        "Ipv6Ranges": [],
+                        "PrefixListIds": [],
+                        "UserIdGroupPairs": [
+                            {
+                                "GroupId": "sg-01ba3c57af54ef22c",
+                                "UserId": "644160558196"
+                            }
+                        ]
+                    }
+                ],
+                "VpcId": "vpc-03a8ce8bb254b625b"
+            },
             {
                 "Description": "default VPC security group",
                 "GroupName": "default",
@@ -47,14 +78,14 @@
                         "PrefixListIds": [],
                         "UserIdGroupPairs": [
                             {
-                                "GroupId": "sg-0c947f3b4bda97fd3",
+                                "GroupId": "sg-0d1564b340cac910f",
                                 "UserId": "644160558196"
                             }
                         ]
                     }
                 ],
                 "OwnerId": "644160558196",
-                "GroupId": "sg-0c947f3b4bda97fd3",
+                "GroupId": "sg-0d1564b340cac910f",
                 "IpPermissionsEgress": [
                     {
                         "IpProtocol": "-1",
@@ -68,38 +99,7 @@
                         "UserIdGroupPairs": []
                     }
                 ],
-                "VpcId": "vpc-0763c6964bb24f0ea"
-            },
-            {
-                "Description": "moved-sawfly",
-                "GroupName": "moved-sawfly",
-                "IpPermissions": [],
-                "OwnerId": "644160558196",
-                "GroupId": "sg-0826c578f515cc65e",
-                "IpPermissionsEgress": [],
-                "VpcId": "vpc-0763c6964bb24f0ea"
-            },
-            {
-                "Description": "engaged-airedale",
-                "GroupName": "engaged-airedale",
-                "IpPermissions": [],
-                "OwnerId": "644160558196",
-                "GroupId": "sg-0c2f2b3dd6a9935aa",
-                "IpPermissionsEgress": [
-                    {
-                        "IpProtocol": "-1",
-                        "IpRanges": [],
-                        "Ipv6Ranges": [],
-                        "PrefixListIds": [],
-                        "UserIdGroupPairs": [
-                            {
-                                "GroupId": "sg-0826c578f515cc65e",
-                                "UserId": "644160558196"
-                            }
-                        ]
-                    }
-                ],
-                "VpcId": "vpc-0763c6964bb24f0ea"
+                "VpcId": "vpc-03a8ce8bb254b625b"
             }
         ],
         "ResponseMetadata": {}
diff --git a/tests/data/placebo/sg_used_cross_ref/ec2.DescribeSecurityGroups_2.json b/tests/data/placebo/sg_used_cross_ref/ec2.DescribeSecurityGroups_2.json
--- a/tests/data/placebo/sg_used_cross_ref/ec2.DescribeSecurityGroups_2.json
+++ b/tests/data/placebo/sg_used_cross_ref/ec2.DescribeSecurityGroups_2.json
@@ -2,6 +2,37 @@
     "status_code": 200,
     "data": {
         "SecurityGroups": [
+            {
+                "Description": "sincere-amoeba",
+                "GroupName": "sincere-amoeba",
+                "IpPermissions": [],
+                "OwnerId": "644160558196",
+                "GroupId": "sg-01ba3c57af54ef22c",
+                "IpPermissionsEgress": [],
+                "VpcId": "vpc-03a8ce8bb254b625b"
+            },
+            {
+                "Description": "oriented-sole",
+                "GroupName": "oriented-sole",
+                "IpPermissions": [],
+                "OwnerId": "644160558196",
+                "GroupId": "sg-073c8f4fb3b58731d",
+                "IpPermissionsEgress": [
+                    {
+                        "IpProtocol": "-1",
+                        "IpRanges": [],
+                        "Ipv6Ranges": [],
+                        "PrefixListIds": [],
+                        "UserIdGroupPairs": [
+                            {
+                                "GroupId": "sg-01ba3c57af54ef22c",
+                                "UserId": "644160558196"
+                            }
+                        ]
+                    }
+                ],
+                "VpcId": "vpc-03a8ce8bb254b625b"
+            },
             {
                 "Description": "default VPC security group",
                 "GroupName": "default",
@@ -47,14 +78,14 @@
                         "PrefixListIds": [],
                         "UserIdGroupPairs": [
                             {
-                                "GroupId": "sg-0c947f3b4bda97fd3",
+                                "GroupId": "sg-0d1564b340cac910f",
                                 "UserId": "644160558196"
                             }
                         ]
                     }
                 ],
                 "OwnerId": "644160558196",
-                "GroupId": "sg-0c947f3b4bda97fd3",
+                "GroupId": "sg-0d1564b340cac910f",
                 "IpPermissionsEgress": [
                     {
                         "IpProtocol": "-1",
@@ -68,38 +99,7 @@
                         "UserIdGroupPairs": []
                     }
                 ],
-                "VpcId": "vpc-0763c6964bb24f0ea"
-            },
-            {
-                "Description": "moved-sawfly",
-                "GroupName": "moved-sawfly",
-                "IpPermissions": [],
-                "OwnerId": "644160558196",
-                "GroupId": "sg-0826c578f515cc65e",
-                "IpPermissionsEgress": [],
-                "VpcId": "vpc-0763c6964bb24f0ea"
-            },
-            {
-                "Description": "engaged-airedale",
-                "GroupName": "engaged-airedale",
-                "IpPermissions": [],
-                "OwnerId": "644160558196",
-                "GroupId": "sg-0c2f2b3dd6a9935aa",
-                "IpPermissionsEgress": [
-                    {
-                        "IpProtocol": "-1",
-                        "IpRanges": [],
-                        "Ipv6Ranges": [],
-                        "PrefixListIds": [],
-                        "UserIdGroupPairs": [
-                            {
-                                "GroupId": "sg-0826c578f515cc65e",
-                                "UserId": "644160558196"
-                            }
-                        ]
-                    }
-                ],
-                "VpcId": "vpc-0763c6964bb24f0ea"
+                "VpcId": "vpc-03a8ce8bb254b625b"
             }
         ],
         "ResponseMetadata": {}
diff --git a/tests/terraform/sg_used_cross_ref/tf_resources.json b/tests/terraform/sg_used_cross_ref/tf_resources.json
--- a/tests/terraform/sg_used_cross_ref/tf_resources.json
+++ b/tests/terraform/sg_used_cross_ref/tf_resources.json
@@ -4,49 +4,49 @@
     "resources": {
         "aws_security_group": {
             "n1": {
-                "arn": "arn:aws:ec2:us-west-2:644160558196:security-group/sg-0c2f2b3dd6a9935aa",
-                "description": "engaged-airedale",
+                "arn": "arn:aws:ec2:us-west-2:644160558196:security-group/sg-073c8f4fb3b58731d",
+                "description": "oriented-sole",
                 "egress": [],
-                "id": "sg-0c2f2b3dd6a9935aa",
+                "id": "sg-073c8f4fb3b58731d",
                 "ingress": [],
-                "name": "engaged-airedale",
+                "name": "oriented-sole",
                 "name_prefix": "",
                 "owner_id": "644160558196",
                 "revoke_rules_on_delete": false,
                 "tags": null,
                 "tags_all": {},
                 "timeouts": null,
-                "vpc_id": "vpc-0763c6964bb24f0ea"
+                "vpc_id": "vpc-03a8ce8bb254b625b"
             },
             "n2": {
-                "arn": "arn:aws:ec2:us-west-2:644160558196:security-group/sg-0826c578f515cc65e",
-                "description": "moved-sawfly",
+                "arn": "arn:aws:ec2:us-west-2:644160558196:security-group/sg-01ba3c57af54ef22c",
+                "description": "sincere-amoeba",
                 "egress": [],
-                "id": "sg-0826c578f515cc65e",
+                "id": "sg-01ba3c57af54ef22c",
                 "ingress": [],
-                "name": "moved-sawfly",
+                "name": "sincere-amoeba",
                 "name_prefix": "",
                 "owner_id": "644160558196",
                 "revoke_rules_on_delete": false,
                 "tags": null,
                 "tags_all": {},
                 "timeouts": null,
-                "vpc_id": "vpc-0763c6964bb24f0ea"
+                "vpc_id": "vpc-03a8ce8bb254b625b"
             }
         },
         "aws_vpc": {
             "main": {
-                "arn": "arn:aws:ec2:us-west-2:644160558196:vpc/vpc-0763c6964bb24f0ea",
+                "arn": "arn:aws:ec2:us-west-2:644160558196:vpc/vpc-03a8ce8bb254b625b",
                 "assign_generated_ipv6_cidr_block": false,
                 "cidr_block": "10.0.0.0/16",
-                "default_network_acl_id": "acl-09ff2173a8e138e6b",
-                "default_route_table_id": "rtb-0a6835a58583e41b0",
-                "default_security_group_id": "sg-0c947f3b4bda97fd3",
+                "default_network_acl_id": "acl-0cf4be4b7ee779858",
+                "default_route_table_id": "rtb-0cd4ccc7f199ce1df",
+                "default_security_group_id": "sg-0d1564b340cac910f",
                 "dhcp_options_id": "dopt-a13309d9",
                 "enable_dns_hostnames": false,
                 "enable_dns_support": true,
                 "enable_network_address_usage_metrics": false,
-                "id": "vpc-0763c6964bb24f0ea",
+                "id": "vpc-03a8ce8bb254b625b",
                 "instance_tenancy": "default",
                 "ipv4_ipam_pool_id": null,
                 "ipv4_netmask_length": null,
@@ -55,7 +55,7 @@
                 "ipv6_cidr_block_network_border_group": "",
                 "ipv6_ipam_pool_id": "",
                 "ipv6_netmask_length": 0,
-                "main_route_table_id": "rtb-0a6835a58583e41b0",
+                "main_route_table_id": "rtb-0cd4ccc7f199ce1df",
                 "owner_id": "644160558196",
                 "tags": null,
                 "tags_all": {}
@@ -63,17 +63,17 @@
         },
         "aws_vpc_security_group_egress_rule": {
             "n1_to_n2": {
-                "arn": "arn:aws:ec2:us-west-2:644160558196:security-group-rule/sgr-0db35d7248709c4c0",
+                "arn": "arn:aws:ec2:us-west-2:644160558196:security-group-rule/sgr-0d334cb8911194bf0",
                 "cidr_ipv4": null,
                 "cidr_ipv6": null,
                 "description": null,
                 "from_port": null,
-                "id": "sgr-0db35d7248709c4c0",
+                "id": "sgr-0d334cb8911194bf0",
                 "ip_protocol": "-1",
                 "prefix_list_id": null,
-                "referenced_security_group_id": "sg-0826c578f515cc65e",
-                "security_group_id": "sg-0c2f2b3dd6a9935aa",
-                "security_group_rule_id": "sgr-0db35d7248709c4c0",
+                "referenced_security_group_id": "sg-01ba3c57af54ef22c",
+                "security_group_id": "sg-073c8f4fb3b58731d",
+                "security_group_rule_id": "sgr-0d334cb8911194bf0",
                 "tags": null,
                 "tags_all": {},
                 "to_port": null
@@ -81,14 +81,14 @@
         },
         "random_pet": {
             "name1": {
-                "id": "engaged-airedale",
+                "id": "oriented-sole",
                 "keepers": null,
                 "length": 2,
                 "prefix": null,
                 "separator": "-"
             },
             "name2": {
-                "id": "moved-sawfly",
+                "id": "sincere-amoeba",
                 "keepers": null,
                 "length": 2,
                 "prefix": null,
diff --git a/tests/test_vpc.py b/tests/test_vpc.py
--- a/tests/test_vpc.py
+++ b/tests/test_vpc.py
@@ -11,6 +11,7 @@
 
 import pytest
 
+
 @pytest.mark.audited
 @terraform('sg_used_cross_ref')
 def test_sg_used_cross_ref(test, sg_used_cross_ref):
@@ -19,7 +20,7 @@ def test_sg_used_cross_ref(test, sg_used_cross_ref):
     p = test.load_policy({
         'name': 'sg_used_cross_ref',
         'resource': 'security-group',
-        'filters': ['unused']
+        'filters': ['used']
     }, session_factory=factory)
     unused = p.resource_manager.filters[0]
     test.patch(
@@ -30,7 +31,7 @@ def test_sg_used_cross_ref(test, sg_used_cross_ref):
     resources = p.run()
     assert len(resources) == 1
     assert resources[0]['GroupName'] == sg_used_cross_ref[
-        'aws_security_group.n1.name']
+        'aws_security_group.n2.name']
 
 
 @terraform('ec2_igw_subnet')
@@ -1524,14 +1525,14 @@ def test_used(self):
             session_factory=factory,
         )
         resources = p.run()
-        self.assertEqual(len(resources), 5)
+        self.assertEqual(len(resources), 3)
         self.assertEqual(
-            {"sg-f9cc4d9f", "sg-13de8f75", "sg-ce548cb7", "sg-0a2cb503a229c31c1", "sg-1c8a186c"},
+            {"sg-f9cc4d9f", "sg-0a2cb503a229c31c1", "sg-1c8a186c"},
             {r["GroupId"] for r in resources},
         )
-        self.assertIn("amazon-aws", resources[2]["c7n:InstanceOwnerIds"])
-        self.assertIn("vpc_endpoint", resources[2]["c7n:InterfaceTypes"])
-        self.assertIn("ec2", resources[2]["c7n:InterfaceResourceTypes"])
+        self.assertIn("amazon-aws", resources[0]["c7n:InstanceOwnerIds"])
+        self.assertIn("vpc_endpoint", resources[0]["c7n:InterfaceTypes"])
+        self.assertIn("ec2", resources[0]["c7n:InterfaceResourceTypes"])
 
     def test_unused_ecs(self):
         factory = self.replay_flight_data("test_security_group_ecs_unused")
@@ -1578,7 +1579,7 @@ def test_unused(self):
             session_factory=factory,
         )
         resources = p.run()
-        self.assertEqual(len(resources), 1)
+        self.assertEqual(len(resources), 2)
 
     def test_match_resource_validator(self):