Repository: cloud-custodian/cloud-custodian

Issue:
Secuity-group doesn't support  filters on "description" attached to permission 
Trying to filter SG with open ports and ignore some based on Descrption. 
resource: security-group
filters:
  - and:
    - type: ingress
      Cidr:
        value: "0.0.0.0/0"
    - type: value
      key: "Description"
      value: "Approved"
      op: not-equal


Discussion:
note the actual use case that was expressed in gitter is Description filter on permission not on sg itself.
Yes, Kapil you are right. The description is attached to the permission. 

Resolving patch:
diff --git a/c7n/resources/vpc.py b/c7n/resources/vpc.py
--- a/c7n/resources/vpc.py
+++ b/c7n/resources/vpc.py
@@ -877,7 +877,7 @@ class SGPermission(Filter):
     perm_attrs = set((
         'IpProtocol', 'FromPort', 'ToPort', 'UserIdGroupPairs',
         'IpRanges', 'PrefixListIds'))
-    filter_attrs = set(('Cidr', 'CidrV6', 'Ports', 'OnlyPorts', 'SelfReference'))
+    filter_attrs = set(('Cidr', 'CidrV6', 'Ports', 'OnlyPorts', 'SelfReference', 'Description'))
     attrs = perm_attrs.union(filter_attrs)
     attrs.add('match-operator')
 
@@ -956,6 +956,22 @@ def process_cidrs(self, perm):
             return None
         return match_op(cidr_match)
 
+    def process_description(self, perm):
+        if 'Description' not in self.data:
+            return None
+
+        d = dict(self.data['Description'])
+        d['key'] = 'Description'
+
+        vf = ValueFilter(d)
+        vf.annotate = False
+
+        for k in ('Ipv6Ranges', 'IpRanges', 'UserIdGroupPairs', 'PrefixListIds'):
+            if k not in perm or not perm[k]:
+                continue
+            return vf(perm[k][0])
+        return False
+
     def process_self_reference(self, perm, sg_id):
         found = None
         ref_match = self.data.get('SelfReference')
@@ -1002,6 +1018,7 @@ def __call__(self, resource):
             perm_matches = {}
             for idx, f in enumerate(self.vfilters):
                 perm_matches[idx] = bool(f(perm))
+            perm_matches['description'] = self.process_description(perm)
             perm_matches['ports'] = self.process_ports(perm)
             perm_matches['cidrs'] = self.process_cidrs(perm)
             perm_matches['self-refs'] = self.process_self_reference(perm, sg_id)


Test patch:
diff --git a/tests/test_vpc.py b/tests/test_vpc.py
--- a/tests/test_vpc.py
+++ b/tests/test_vpc.py
@@ -1670,6 +1670,46 @@ def test_multi_attribute_ingress(self):
         manager = p.load_resource_manager()
         self.assertEqual(len(manager.filter_resources(resources)), 1)
 
+    def test_description_ingress(self):
+        p = self.load_policy(
+            {
+                "name": "ingress-access",
+                "resource": "security-group",
+                "filters": [
+                    {"type": "ingress",
+                     "Description": {
+                         "value": "Approved",
+                         "op": "not-equal",
+                     },
+                     "Cidr": {"value": "0.0.0.0/0"}, "Ports": [22]}
+                ],
+            }
+        )
+
+        resources = [{
+            "Description": "allows inbound 0.0.0.0/0:22",
+            "GroupName": "ssh",
+            "IpPermissions": [
+                {
+                    "FromPort": 22,
+                    "IpProtocol": "tcp",
+                    "IpRanges": [
+                        {
+                            "CidrIp": "0.0.0.0/0",
+                            "Description": "ssh",
+                        }
+                    ],
+                    "Ipv6Ranges": []
+                }
+            ],
+            "OwnerId": "644160558196",
+            "GroupId": "sg-0b090df1c1f95bc13",
+            "IpPermissionsEgress": [],
+            "VpcId": "vpc-f1516b97"
+        }]
+        manager = p.load_resource_manager()
+        self.assertEqual(len(manager.filter_resources(resources)), 1)
+
     def test_ports_ingress(self):
         p = self.load_policy(
             {