Repository: cloud-custodian/cloud-custodian

Issue:
[SecurityHub] When there are multiple resource matches display each resource in SecurityHub
In my setup cloud custodian checks for existence of some tags on ec2 instances and has SecurityHub as target of the notify action. When a match occurs the SecurityHub GUI only displays the first matched resource instance in the event json in the ResourceId field and the Resources/resource detail  panel. This is confusing (and unhelpful). Can the ingestion into SecurityHub be altered to provide one distinct record per instance match, or can the SecurityHub display be amended to display list of matching resource instances (appreciate this is probably out of scope here).  

I notice that the same thing happens with checks for eg. unencrypted S3 buckets, so I guess it is generalisable to all policy matches against multiple resource instances.

I was hoping to generate alerts from a metrics repository to let AWS account owners know that they have n resources matching some rule, and have them look in SecurityHub to tell easily which are the offending instances (I don't want to go down the c7n-mailer route, as I have no easy way to control repeats of essentially the same email, and I don't want to have to configure relevant email addresses into the policy definitions).  However, drilling down into the 'Finding JSON' isn't going to work.

As always, apologies if I have completely misunderstood something fundamental here.


Discussion:
I've reached out to the Security Hub Product Manager asking for any plans for how the Console display's findings which have multiple resources, as I agree the user experience is not what it should be, and I've sent him screenshots.  It would require a code change to CloudCustodian to only submit 1, or a configurable number <=10, of resources per finding.  I'd like to hold off on coding a CloudCustodian change until hearing back from him, but if it's a real pain for you and need a fix right now, you could change the value of 10 to 1, in line 191 of c7n/actions/securityhub.py
SecHub Console (aka GUI) behavior of only showing the first resource is now an acknowledged bug, and I've submitted an internal (to AWS) bug report for tracking purposes.  
do we need to have an option on the post-finding action to post each resource as a separate finding as a temporary work around?
Thanks @FireballDWF , I will try the code fix you suggest for now. 
Yes, I was thinking of a maxitems per finding attribute where the example
would show the value as 1.  I won’t be getting to this until Friday at the
earliest.

On Mon, Feb 4, 2019 at 5:54 PM Kapil Thangavelu <notifications@github.com>
wrote:

> do we need to have an option on the post-finding action to post each
> resource as a separate finding as a temporary work around?
>
> —
> You are receiving this because you commented.
>
>
> Reply to this email directly, view it on GitHub
> <https://github.com/cloud-custodian/cloud-custodian/issues/3495#issuecomment-460445947>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AABLSUYJY72GnRpQZ69S98xqa0MernAcks5vKLoOgaJpZM4ahVNA>
> .
>
-- 
Sent from Gmail Mobile


Resolving patch:
diff --git a/c7n/actions/securityhub.py b/c7n/actions/securityhub.py
--- a/c7n/actions/securityhub.py
+++ b/c7n/actions/securityhub.py
@@ -140,6 +140,7 @@ class PostFinding(BaseAction):
         recommendation={"type": "string"},
         recommendation_url={"type": "string"},
         fields={"type": "object"},
+        batch_size={'type': 'integer', 'minimum': 1, 'maximum': 10},
         types={
             "type": "array",
             "items": {"type": "string", "enum": build_vocabulary()},
@@ -185,10 +186,10 @@ def process(self, resources, event=None):
                 "securityhub", region_name=region_name)
 
         now = datetime.utcnow().replace(tzinfo=tzutc()).isoformat()
-
+        batch_size = self.data.get('batch_size', 10)
         stats = Counter()
         for key, grouped_resources in self.group_resources(resources).items():
-            for resource_set in chunks(grouped_resources, 10):
+            for resource_set in chunks(grouped_resources, batch_size):
                 stats['Finding'] += 1
                 if key == self.NEW_FINDING:
                     finding_id = None
@@ -290,10 +291,15 @@ def get_finding(self, resources, existing_finding_id, created_at, updated_at):
         if "compliance_status" in self.data:
             finding["Compliance"] = {"Status": self.data["compliance_status"]}
 
-        fields = {}
+        fields = {
+            'resource': policy.resource_type,
+            'ProviderName': 'CloudCustodian',
+            'ProviderVersion': version
+        }
+
         if "fields" in self.data:
             fields.update(self.data["fields"])
-        if not fields:
+        else:
             tags = {}
             for t in policy.tags:
                 if ":" in t:
@@ -301,10 +307,7 @@ def get_finding(self, resources, existing_finding_id, created_at, updated_at):
                 else:
                     k, v = t, ""
                 tags[k] = v
-            fields = tags
-            fields["resource"] = policy.resource_type
-            fields["ProviderName"] = "CloudCustodian"
-            fields["ProviderVersion"] = version
+            fields.update(tags)
         if fields:
             finding["ProductFields"] = fields
 


Test patch: