Repository: cloud-custodian/cloud-custodian

Issue:
AWS Config - type: config-compliance not working 
Use Case: Use the AWS Config rule of security hub for filtering the non-compliant resources. 

Description or Steps to reproduce 
1) Identify the config rule name via security hub 
2) Create the policy using the config rule name and verify the results of the config rule via policy and security hub match each other. 

Policy sample : 
policies:
#This policy is to mark the IAM users who has keys active and not being used from more than 80 days
- name: iam-user-remediate-non-compliant
  resource: aws.iam-user
  filters:
  - type: config-compliance
    rules: [securityhub-iam-user-unused-credentials-check-nv6rb]
    states: [NON_COMPLIANT]



Discussion:

Resolving patch:
diff --git a/c7n/filters/config.py b/c7n/filters/config.py
--- a/c7n/filters/config.py
+++ b/c7n/filters/config.py
@@ -109,11 +109,26 @@ def process(self, resources, event=None):
         resource_model = self.manager.get_model()
         resource_map = self.get_resource_map(filters, resource_model, resources)
 
+        # Avoid static/import time dep on boto in filters package
+        from c7n.resources.aws import Arn
         results = []
-        for r in resources:
-            if r[resource_model.id] not in resource_map:
+        for arn, r in zip(self.manager.get_arns(resources), resources):
+            # many aws provided rules are inconsistent in their
+            # treatment of resource ids, some use arns, some use names
+            # as identifiers for the same resource type. security
+            # hub in particular is bad at consistency.
+            rid = None
+            if arn in resource_map:
+                rid = arn
+            elif r[resource_model.id] in resource_map:
+                rid = r[resource_model.id]
+            if arn == r[resource_model.id] and not rid:
+                rid = Arn.parse(arn).resource
+                if rid not in resource_map:
+                    rid = None
+            if rid is None:
                 continue
-            r[self.annotation_key] = resource_map[r[resource_model.id]]
+            r[self.annotation_key] = resource_map[rid]
             results.append(r)
         return results
 


Test patch:
diff --git a/tests/data/placebo/test_config_inconsistent_hub_rule/cloudtrail.DescribeTrails_1.json b/tests/data/placebo/test_config_inconsistent_hub_rule/cloudtrail.DescribeTrails_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_config_inconsistent_hub_rule/cloudtrail.DescribeTrails_1.json
@@ -0,0 +1,41 @@
+{
+    "status_code": 200,
+    "data": {
+        "trailList": [
+            {
+                "Name": "S3-DataEvents",
+                "IncludeGlobalServiceEvents": true,
+                "IsMultiRegionTrail": true,
+                "HomeRegion": "us-east-1",
+                "TrailARN": "arn:aws:cloudtrail:us-east-1:644160558196:trail/S3-DataEvents",
+                "LogFileValidationEnabled": true,
+                "HasCustomEventSelectors": true,
+                "HasInsightSelectors": false,
+                "IsOrganizationTrail": false
+            },
+            {
+                "Name": "orgTrail",
+                "IncludeGlobalServiceEvents": true,
+                "IsMultiRegionTrail": true,
+                "HomeRegion": "us-east-1",
+                "TrailARN": "arn:aws:cloudtrail:us-east-1:644160558196:trail/orgTrail",
+                "LogFileValidationEnabled": true,
+                "HasCustomEventSelectors": false,
+                "HasInsightSelectors": false,
+                "IsOrganizationTrail": true
+            },
+            {
+                "Name": "skunk-trails",
+                "IncludeGlobalServiceEvents": true,
+                "IsMultiRegionTrail": true,
+                "HomeRegion": "us-east-1",
+                "TrailARN": "arn:aws:cloudtrail:us-east-1:644160558196:trail/skunk-trails",
+                "LogFileValidationEnabled": true,
+                "HasCustomEventSelectors": false,
+                "HasInsightSelectors": false,
+                "IsOrganizationTrail": false
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
diff --git a/tests/data/placebo/test_config_inconsistent_hub_rule/config.GetComplianceDetailsByConfigRule_1.json b/tests/data/placebo/test_config_inconsistent_hub_rule/config.GetComplianceDetailsByConfigRule_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_config_inconsistent_hub_rule/config.GetComplianceDetailsByConfigRule_1.json
@@ -0,0 +1,128 @@
+{
+    "status_code": 200,
+    "data": {
+        "EvaluationResults": [
+            {
+                "EvaluationResultIdentifier": {
+                    "EvaluationResultQualifier": {
+                        "ConfigRuleName": "securityhub-cloud-trail-encryption-enabled-dadfg6",
+                        "ResourceType": "AWS::CloudTrail::Trail",
+                        "ResourceId": "S3-DataEvents"
+                    },
+                    "OrderingTimestamp": {
+                        "__class__": "datetime",
+                        "year": 2020,
+                        "month": 5,
+                        "day": 27,
+                        "hour": 4,
+                        "minute": 15,
+                        "second": 11,
+                        "microsecond": 900000
+                    }
+                },
+                "ComplianceType": "NON_COMPLIANT",
+                "ResultRecordedTime": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 5,
+                    "day": 27,
+                    "hour": 4,
+                    "minute": 15,
+                    "second": 25,
+                    "microsecond": 613000
+                },
+                "ConfigRuleInvokedTime": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 5,
+                    "day": 27,
+                    "hour": 4,
+                    "minute": 15,
+                    "second": 25,
+                    "microsecond": 376000
+                }
+            },
+            {
+                "EvaluationResultIdentifier": {
+                    "EvaluationResultQualifier": {
+                        "ConfigRuleName": "securityhub-cloud-trail-encryption-enabled-dadfg6",
+                        "ResourceType": "AWS::CloudTrail::Trail",
+                        "ResourceId": "arn:aws:cloudtrail:us-east-1:644160558196:trail/orgTrail"
+                    },
+                    "OrderingTimestamp": {
+                        "__class__": "datetime",
+                        "year": 2020,
+                        "month": 5,
+                        "day": 27,
+                        "hour": 4,
+                        "minute": 15,
+                        "second": 11,
+                        "microsecond": 900000
+                    }
+                },
+                "ComplianceType": "NON_COMPLIANT",
+                "ResultRecordedTime": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 5,
+                    "day": 27,
+                    "hour": 4,
+                    "minute": 15,
+                    "second": 25,
+                    "microsecond": 619000
+                },
+                "ConfigRuleInvokedTime": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 5,
+                    "day": 27,
+                    "hour": 4,
+                    "minute": 15,
+                    "second": 25,
+                    "microsecond": 376000
+                }
+            },
+            {
+                "EvaluationResultIdentifier": {
+                    "EvaluationResultQualifier": {
+                        "ConfigRuleName": "securityhub-cloud-trail-encryption-enabled-dadfg6",
+                        "ResourceType": "AWS::CloudTrail::Trail",
+                        "ResourceId": "skunk-trails"
+                    },
+                    "OrderingTimestamp": {
+                        "__class__": "datetime",
+                        "year": 2020,
+                        "month": 5,
+                        "day": 27,
+                        "hour": 4,
+                        "minute": 15,
+                        "second": 11,
+                        "microsecond": 900000
+                    }
+                },
+                "ComplianceType": "NON_COMPLIANT",
+                "ResultRecordedTime": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 5,
+                    "day": 27,
+                    "hour": 4,
+                    "minute": 15,
+                    "second": 25,
+                    "microsecond": 624000
+                },
+                "ConfigRuleInvokedTime": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 5,
+                    "day": 27,
+                    "hour": 4,
+                    "minute": 15,
+                    "second": 25,
+                    "microsecond": 376000
+                }
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_config_inconsistent_hub_rule/tagging.GetResources_1.json b/tests/data/placebo/test_config_inconsistent_hub_rule/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_config_inconsistent_hub_rule/tagging.GetResources_1.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_config.py b/tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -34,6 +34,19 @@ def test_config_recorder(self):
 
 class ConfigComplianceTest(BaseTest):
 
+    def test_config_with_inconsistent_hub_rule(self):
+        factory = self.replay_flight_data('test_config_inconsistent_hub_rule')
+        p = self.load_policy({
+            'name': 'compliance',
+            'resource': 'aws.cloudtrail',
+            'filters': [
+                {'type': 'config-compliance',
+                 'states': ['NON_COMPLIANT'],
+                 'rules': ['securityhub-cloud-trail-encryption-enabled-dadfg6']}]},
+            session_factory=factory)
+        resources = p.run()
+        self.assertEqual(len(resources), 3)
+
     def test_compliance(self):
         factory = self.replay_flight_data('test_config_compliance')
         p = self.load_policy({