Repository: cloud-custodian/cloud-custodian

Issue:
cloudtrail filter `is-shadow` always matches all resources



Discussion:
In the [`process()`](https://github.com/cloud-custodian/cloud-custodian/blob/master/c7n/resources/cloudtrail.py#L57) function, I guess these lines are to deal with the `is_shadow()` returning None if it falls through?
```python
        anded = lambda x: True # NOQA
        op = self.data.get('state', True) and anded or operator.__not__
```

changing the lambda seems to correct the issue:
```python
        anded = lambda x: x # NOQA
```


Although It might make the code easier to understand if `is_shadow()` returns False on the fall through, and then the comparison can be done as a slightly more straightforward boolean:
```python
        trails = [t for t in resources if (self.is_shadow(t) == self.data.get('state', True))]
```

Resolving patch:
diff --git a/c7n/resources/cloudtrail.py b/c7n/resources/cloudtrail.py
--- a/c7n/resources/cloudtrail.py
+++ b/c7n/resources/cloudtrail.py
@@ -14,7 +14,6 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 
 import logging
-import operator
 
 from c7n.actions import Action
 from c7n.exceptions import PolicyValidationError
@@ -55,10 +54,8 @@ class IsShadow(Filter):
     embedded = False
 
     def process(self, resources, event=None):
-        anded = lambda x: True # NOQA
-        op = self.data.get('state', True) and anded or operator.__not__
         rcount = len(resources)
-        trails = [t for t in resources if op(self.is_shadow(t))]
+        trails = [t for t in resources if (self.is_shadow(t) == self.data.get('state', True))]
         if len(trails) != rcount and self.embedded:
             self.log.info("implicitly filtering shadow trails %d -> %d",
                      rcount, len(trails))
@@ -67,8 +64,9 @@ def process(self, resources, event=None):
     def is_shadow(self, t):
         if t.get('IsOrganizationTrail') and self.manager.config.account_id not in t['TrailARN']:
             return True
-        if t.get('IsMultiRegionTrail') and t['HomeRegion'] not in t['TrailARN']:
+        if t.get('IsMultiRegionTrail') and t['HomeRegion'] != self.manager.config.region:
             return True
+        return False
 
 
 @CloudTrail.filter_registry.register('status')


Test patch:
diff --git a/tests/data/placebo/test_cloudtrail_is_shadow_or_not/cloudtrail.DescribeTrails_1.json b/tests/data/placebo/test_cloudtrail_is_shadow_or_not/cloudtrail.DescribeTrails_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloudtrail_is_shadow_or_not/cloudtrail.DescribeTrails_1.json
@@ -0,0 +1,48 @@
+{
+    "status_code": 200,
+    "data": {
+        "trailList": [
+            {
+                "Name": "MultiRegion1CloudTrail",
+                "S3BucketName": "cloudtrail-bucket-123456789012",
+                "IncludeGlobalServiceEvents": true,
+                "IsMultiRegionTrail": true,
+                "HomeRegion": "us-east-1",
+                "TrailARN": "arn:aws:cloudtrail:us-east-1:123456789012:trail/MultiRegion1CloudTrail",
+                "LogFileValidationEnabled": true,
+                "HasCustomEventSelectors": true
+            },
+            {
+                "Name": "MultiRegion2CloudTrail",
+                "S3BucketName": "cloudtrail-bucket-123456789012",
+                "IncludeGlobalServiceEvents": true,
+                "IsMultiRegionTrail": true,
+                "HomeRegion": "us-east-2",
+                "TrailARN": "arn:aws:cloudtrail:us-east-2:123456789012:trail/MultiRegion2CloudTrail",
+                "LogFileValidationEnabled": true,
+                "HasCustomEventSelectors": true
+            },
+            {
+                "Name": "SingleCloudTrail",
+                "S3BucketName": "cloudtrail-bucket-123456789012",
+                "IncludeGlobalServiceEvents": false,
+                "IsMultiRegionTrail": false,
+                "HomeRegion": "us-east-1",
+                "TrailARN": "arn:aws:cloudtrail:us-east-1:123456789012:trail/SingleCloudTrail",
+                "LogFileValidationEnabled": true,
+                "HasCustomEventSelectors": true
+            }
+        ],
+        "ResponseMetadata": {
+            "RequestId": "eb7e5f92-ea5b-4a4a-b5e4-2cbef4d30dad",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "x-amzn-requestid": "eb7e5f92-ea5b-4a4a-b5e4-2cbef4d30dad",
+                "content-type": "application/x-amz-json-1.1",
+                "content-length": "756",
+                "date": "Wed, 23 Jan 2019 01:27:00 GMT"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
diff --git a/tests/test_cloudtrail.py b/tests/test_cloudtrail.py
--- a/tests/test_cloudtrail.py
+++ b/tests/test_cloudtrail.py
@@ -90,6 +90,48 @@ def test_is_shadow(self):
             resources[0]['TrailARN'],
             'arn:aws:cloudtrail:us-east-1:644160558196:trail/orgTrail')
 
+    def test_is_shadow_or_not(self):
+        factory = self.replay_flight_data('test_cloudtrail_is_shadow_or_not')
+        p = self.load_policy({
+            'name': 'resource',
+            'resource': 'cloudtrail',
+            'filters': ['is-shadow']},
+            session_factory=factory, config={'region': 'us-east-1'})
+        resources = p.run()
+        self.assertEqual(1, len(resources))
+        self.assertEqual(
+            'arn:aws:cloudtrail:us-east-2:123456789012:trail/MultiRegion2CloudTrail',
+            resources[0]['TrailARN'])
+
+    def test_is_shadow_not(self):
+        factory = self.replay_flight_data('test_cloudtrail_is_shadow_or_not')
+        p = self.load_policy({
+            'name': 'resource',
+            'resource': 'cloudtrail',
+            'filters': [{'type': 'is-shadow', 'state': False}]},
+            session_factory=factory, config={'region': 'us-east-1'})
+        resources = p.run()
+        self.assertEqual(2, len(resources))
+        self.assertEqual(
+            'arn:aws:cloudtrail:us-east-1:123456789012:trail/MultiRegion1CloudTrail',
+            resources[0]['TrailARN'])
+        self.assertEqual(
+            'arn:aws:cloudtrail:us-east-1:123456789012:trail/SingleCloudTrail',
+            resources[1]['TrailARN'])
+
+    def test_is_shadow_multiregion(self):
+        factory = self.replay_flight_data('test_cloudtrail_is_shadow_or_not')
+        p = self.load_policy({
+            'name': 'resource',
+            'resource': 'cloudtrail',
+            'filters': ['is-shadow']},
+            session_factory=factory, config={'region': 'us-east-2'})
+        resources = p.run()
+        self.assertEqual(1, len(resources))
+        self.assertEqual(
+            'arn:aws:cloudtrail:us-east-1:123456789012:trail/MultiRegion1CloudTrail',
+            resources[0]['TrailARN'])
+
     def test_cloudtrail_resource_with_not_filter(self):
         factory = self.replay_flight_data("test_cloudtrail_resource_with_not_filter")
         p = self.load_policy(