We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
E2057 False positive for IAM policy Effect conditionals
*cfn-lint version: (`cfn-lint --version`)*
cfn-lint 0.21.4

*Description of issue.*
cfn-lint reports "E2507 IAM Policy Effect should be Allow or Deny" when an IAM policy statement contains an intrinsic statement, even if that intrinsic returns Allow or Deny eg:

`Effect: !If [ IsUsEast1, 'Allow', 'Deny' ]`

I would guess this probably affects all IAM policy statements and intrinsics, I have observed it for the following policy types:
-  AWS::S3::BucketPolicy
-  AWS::IAM::ManagedPolicy
-  AWS::IAM::User policies

And the following intrinsics
- !If
- !Sub
- !Ref

A simple reproduce example termplate is attached: [E2507-false-positive.yml.txt](https://github.com/aws-cloudformation/cfn-python-lint/files/3269255/E2507-false-positive.yml.txt)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/cfnlint/rules/resources/iam/Policy.py`
Content:
```
1 """
2   Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 
4   Permission is hereby granted, free of charge, to any person obtaining a copy of this
5   software and associated documentation files (the "Software"), to deal in the Software
6   without restriction, including without limitation the rights to use, copy, modify,
7   merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8   permit persons to whom the Software is furnished to do so.
9 
10   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12   PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 """
17 import json
18 from datetime import date
19 import six
20 from cfnlint.helpers import convert_dict
21 from cfnlint import CloudFormationLintRule
22 from cfnlint import RuleMatch
23 
24 
25 class Policy(CloudFormationLintRule):
26     """Check if IAM Policy JSON is correct"""
27     id = 'E2507'
28     shortdesc = 'Check if IAM Policies are properly configured'
29     description = 'See if there elements inside an IAM policy ' + \
30                   'are correct'
31     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html'
32     tags = ['properties', 'iam']
33 
34     def __init__(self):
35         """Init"""
36         super(Policy, self).__init__()
37         self.resource_exceptions = {
38             'AWS::ECR::Repository': 'RepositoryPolicyText',
39         }
40         self.resources_and_keys = {
41             'AWS::SNS::TopicPolicy': 'PolicyDocument',
42             'AWS::S3::BucketPolicy': 'PolicyDocument',
43             'AWS::KMS::Key': 'KeyPolicy',
44             'AWS::SQS::QueuePolicy': 'PolicyDocument',
45             'AWS::ECR::Repository': 'RepositoryPolicyText',
46             'AWS::Elasticsearch::Domain': 'AccessPolicies',
47         }
48         self.idp_and_keys = {
49             'AWS::IAM::Group': 'Policies',
50             'AWS::IAM::ManagedPolicy': 'PolicyDocument',
51             'AWS::IAM::Policy': 'PolicyDocument',
52             'AWS::IAM::Role': 'Policies',
53             'AWS::IAM::User': 'Policies',
54         }
55         for resource_type in self.resources_and_keys:
56             self.resource_property_types.append(resource_type)
57         for resource_type in self.idp_and_keys:
58             self.resource_property_types.append(resource_type)
59 
60     def check_policy_document(self, value, path, is_identity_policy, resource_exceptions, start_mark, end_mark):
61         """Check policy document"""
62         matches = []
63 
64         valid_keys = [
65             'Version',
66             'Id',
67             'Statement',
68         ]
69         valid_versions = ['2012-10-17', '2008-10-17', date(2012, 10, 17), date(2008, 10, 17)]
70 
71         if isinstance(value, six.string_types):
72             try:
73                 value = convert_dict(json.loads(value), start_mark, end_mark)
74             except Exception as ex:  # pylint: disable=W0703,W0612
75                 message = 'IAM Policy Documents need to be JSON'
76                 matches.append(RuleMatch(path[:], message))
77                 return matches
78 
79         if not isinstance(value, dict):
80             message = 'IAM Policy Documents needs to be JSON'
81             matches.append(
82                 RuleMatch(path[:], message))
83             return matches
84 
85         for p_vs, p_p in value.items_safe(path[:], (dict)):
86             for parent_key, parent_value in p_vs.items():
87                 if parent_key not in valid_keys:
88                     message = 'IAM Policy key %s doesn\'t exist.' % (parent_key)
89                     matches.append(
90                         RuleMatch(path[:] + p_p + [parent_key], message))
91                 if parent_key == 'Version':
92                     if parent_value not in valid_versions:
93                         message = 'IAM Policy Version needs to be one of (%s).' % (
94                             ', '.join(map(str, ['2012-10-17', '2008-10-17'])))
95                         matches.append(
96                             RuleMatch(p_p + [parent_key], message))
97                 if parent_key == 'Statement':
98                     if isinstance(parent_value, list):
99                         for i_s_v, i_s_p in parent_value.items_safe(p_p + ['Statement'], (dict)):
100                             matches.extend(
101                                 self._check_policy_statement(
102                                     i_s_p, i_s_v, is_identity_policy, resource_exceptions
103                                 )
104                             )
105                     elif isinstance(parent_value, dict):
106                         for i_s_v, i_s_p in parent_value.items_safe(p_p + ['Statement']):
107                             matches.extend(
108                                 self._check_policy_statement(
109                                     i_s_p, i_s_v, is_identity_policy, resource_exceptions
110                                 )
111                             )
112                     else:
113                         message = 'IAM Policy statement should be of list.'
114                         matches.append(
115                             RuleMatch(p_p + [parent_key], message))
116         return matches
117 
118     def _check_policy_statement(self, branch, statement, is_identity_policy, resource_exceptions):
119         """Check statements"""
120         matches = []
121         statement_valid_keys = [
122             'Effect',
123             'Principal',
124             'NotPrincipal',
125             'Action',
126             'NotAction',
127             'Resource',
128             'NotResource',
129             'Condition',
130             'Sid',
131         ]
132 
133         for key, _ in statement.items():
134             if key not in statement_valid_keys:
135                 message = 'IAM Policy statement key %s isn\'t valid' % (key)
136                 matches.append(
137                     RuleMatch(branch[:] + [key], message))
138         if 'Effect' not in statement:
139             message = 'IAM Policy statement missing Effect'
140             matches.append(
141                 RuleMatch(branch[:], message))
142         else:
143             effect = statement.get('Effect')
144             if effect not in ['Allow', 'Deny']:
145                 message = 'IAM Policy Effect should be Allow or Deny'
146                 matches.append(
147                     RuleMatch(branch[:] + ['Effect'], message))
148         if 'Action' not in statement and 'NotAction' not in statement:
149             message = 'IAM Policy statement missing Action or NotAction'
150             matches.append(
151                 RuleMatch(branch[:], message))
152         if is_identity_policy:
153             if 'Principal' in statement or 'NotPrincipal' in statement:
154                 message = 'IAM Resource Policy statement shouldn\'t have Principal or NotPrincipal'
155                 matches.append(
156                     RuleMatch(branch[:], message))
157         else:
158             if 'Principal' not in statement and 'NotPrincipal' not in statement:
159                 message = 'IAM Resource Policy statement should have Principal or NotPrincipal'
160                 matches.append(
161                     RuleMatch(branch[:] + ['Principal'], message))
162         if not resource_exceptions:
163             if 'Resource' not in statement and 'NotResource' not in statement:
164                 message = 'IAM Policy statement missing Resource or NotResource'
165                 matches.append(
166                     RuleMatch(branch[:], message))
167 
168         return(matches)
169 
170     def match_resource_properties(self, properties, resourcetype, path, cfn):
171         """Check CloudFormation Properties"""
172         matches = []
173 
174         is_identity_policy = True
175         if resourcetype in self.resources_and_keys:
176             is_identity_policy = False
177 
178         key = None
179         if resourcetype in self.resources_and_keys:
180             key = self.resources_and_keys.get(resourcetype)
181         else:
182             key = self.idp_and_keys.get(resourcetype)
183 
184         if not key:
185             # Key isn't defined return nothing
186             return matches
187 
188         resource_exceptions = False
189         if key == self.resource_exceptions.get(resourcetype):
190             resource_exceptions = True
191 
192         other_keys = []
193         for key, value in self.resources_and_keys.items():
194             if value != 'Policies':
195                 other_keys.append(key)
196         for key, value in self.idp_and_keys.items():
197             if value != 'Policies':
198                 other_keys.append(key)
199 
200         for key, value in properties.items():
201             if key == 'Policies' and isinstance(value, list):
202                 for index, policy in enumerate(properties.get(key, [])):
203                     matches.extend(
204                         cfn.check_value(
205                             obj=policy, key='PolicyDocument',
206                             path=path[:] + ['Policies', index],
207                             check_value=self.check_policy_document,
208                             is_identity_policy=is_identity_policy,
209                             resource_exceptions=resource_exceptions,
210                             start_mark=key.start_mark, end_mark=key.end_mark,
211                         ))
212             elif key in ['KeyPolicy', 'PolicyDocument', 'RepositoryPolicyText', 'AccessPolicies']:
213                 matches.extend(
214                     cfn.check_value(
215                         obj=properties, key=key,
216                         path=path[:],
217                         check_value=self.check_policy_document,
218                         is_identity_policy=is_identity_policy,
219                         resource_exceptions=resource_exceptions,
220                         start_mark=key.start_mark, end_mark=key.end_mark,
221                     ))
222 
223         return matches
224
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/cfnlint/rules/resources/iam/Policy.py b/src/cfnlint/rules/resources/iam/Policy.py
--- a/src/cfnlint/rules/resources/iam/Policy.py
+++ b/src/cfnlint/rules/resources/iam/Policy.py
@@ -140,11 +140,12 @@
             matches.append(
                 RuleMatch(branch[:], message))
         else:
-            effect = statement.get('Effect')
-            if effect not in ['Allow', 'Deny']:
-                message = 'IAM Policy Effect should be Allow or Deny'
-                matches.append(
-                    RuleMatch(branch[:] + ['Effect'], message))
+            for effect, effect_path in statement.get_safe('Effect'):
+                if isinstance(effect, six.string_types):
+                    if effect not in ['Allow', 'Deny']:
+                        message = 'IAM Policy Effect should be Allow or Deny'
+                        matches.append(
+                            RuleMatch(branch[:] + effect_path, message))
         if 'Action' not in statement and 'NotAction' not in statement:
             message = 'IAM Policy statement missing Action or NotAction'
             matches.append(

{"golden_diff": "diff --git a/src/cfnlint/rules/resources/iam/Policy.py b/src/cfnlint/rules/resources/iam/Policy.py\n--- a/src/cfnlint/rules/resources/iam/Policy.py\n+++ b/src/cfnlint/rules/resources/iam/Policy.py\n@@ -140,11 +140,12 @@\n             matches.append(\n                 RuleMatch(branch[:], message))\n         else:\n-            effect = statement.get('Effect')\n-            if effect not in ['Allow', 'Deny']:\n-                message = 'IAM Policy Effect should be Allow or Deny'\n-                matches.append(\n-                    RuleMatch(branch[:] + ['Effect'], message))\n+            for effect, effect_path in statement.get_safe('Effect'):\n+                if isinstance(effect, six.string_types):\n+                    if effect not in ['Allow', 'Deny']:\n+                        message = 'IAM Policy Effect should be Allow or Deny'\n+                        matches.append(\n+                            RuleMatch(branch[:] + effect_path, message))\n         if 'Action' not in statement and 'NotAction' not in statement:\n             message = 'IAM Policy statement missing Action or NotAction'\n             matches.append(\n", "issue": "E2057 False positive for IAM policy Effect conditionals\n*cfn-lint version: (`cfn-lint --version`)*\r\ncfn-lint 0.21.4\r\n\r\n*Description of issue.*\r\ncfn-lint reports \"E2507 IAM Policy Effect should be Allow or Deny\" when an IAM policy statement contains an intrinsic statement, even if that intrinsic returns Allow or Deny eg:\r\n\r\n`Effect: !If [ IsUsEast1, 'Allow', 'Deny' ]`\r\n\r\nI would guess this probably affects all IAM policy statements and intrinsics, I have observed it for the following policy types:\r\n-  AWS::S3::BucketPolicy\r\n-  AWS::IAM::ManagedPolicy\r\n-  AWS::IAM::User policies\r\n\r\nAnd the following intrinsics\r\n- !If\r\n- !Sub\r\n- !Ref\r\n\r\nA simple reproduce example termplate is attached: [E2507-false-positive.yml.txt](https://github.com/aws-cloudformation/cfn-python-lint/files/3269255/E2507-false-positive.yml.txt)\r\n\n", "before_files": [{"content": "\"\"\"\n  Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this\n  software and associated documentation files (the \"Software\"), to deal in the Software\n  without restriction, including without limitation the rights to use, copy, modify,\n  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\nimport json\nfrom datetime import date\nimport six\nfrom cfnlint.helpers import convert_dict\nfrom cfnlint import CloudFormationLintRule\nfrom cfnlint import RuleMatch\n\n\nclass Policy(CloudFormationLintRule):\n    \"\"\"Check if IAM Policy JSON is correct\"\"\"\n    id = 'E2507'\n    shortdesc = 'Check if IAM Policies are properly configured'\n    description = 'See if there elements inside an IAM policy ' + \\\n                  'are correct'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html'\n    tags = ['properties', 'iam']\n\n    def __init__(self):\n        \"\"\"Init\"\"\"\n        super(Policy, self).__init__()\n        self.resource_exceptions = {\n            'AWS::ECR::Repository': 'RepositoryPolicyText',\n        }\n        self.resources_and_keys = {\n            'AWS::SNS::TopicPolicy': 'PolicyDocument',\n            'AWS::S3::BucketPolicy': 'PolicyDocument',\n            'AWS::KMS::Key': 'KeyPolicy',\n            'AWS::SQS::QueuePolicy': 'PolicyDocument',\n            'AWS::ECR::Repository': 'RepositoryPolicyText',\n            'AWS::Elasticsearch::Domain': 'AccessPolicies',\n        }\n        self.idp_and_keys = {\n            'AWS::IAM::Group': 'Policies',\n            'AWS::IAM::ManagedPolicy': 'PolicyDocument',\n            'AWS::IAM::Policy': 'PolicyDocument',\n            'AWS::IAM::Role': 'Policies',\n            'AWS::IAM::User': 'Policies',\n        }\n        for resource_type in self.resources_and_keys:\n            self.resource_property_types.append(resource_type)\n        for resource_type in self.idp_and_keys:\n            self.resource_property_types.append(resource_type)\n\n    def check_policy_document(self, value, path, is_identity_policy, resource_exceptions, start_mark, end_mark):\n        \"\"\"Check policy document\"\"\"\n        matches = []\n\n        valid_keys = [\n            'Version',\n            'Id',\n            'Statement',\n        ]\n        valid_versions = ['2012-10-17', '2008-10-17', date(2012, 10, 17), date(2008, 10, 17)]\n\n        if isinstance(value, six.string_types):\n            try:\n                value = convert_dict(json.loads(value), start_mark, end_mark)\n            except Exception as ex:  # pylint: disable=W0703,W0612\n                message = 'IAM Policy Documents need to be JSON'\n                matches.append(RuleMatch(path[:], message))\n                return matches\n\n        if not isinstance(value, dict):\n            message = 'IAM Policy Documents needs to be JSON'\n            matches.append(\n                RuleMatch(path[:], message))\n            return matches\n\n        for p_vs, p_p in value.items_safe(path[:], (dict)):\n            for parent_key, parent_value in p_vs.items():\n                if parent_key not in valid_keys:\n                    message = 'IAM Policy key %s doesn\\'t exist.' % (parent_key)\n                    matches.append(\n                        RuleMatch(path[:] + p_p + [parent_key], message))\n                if parent_key == 'Version':\n                    if parent_value not in valid_versions:\n                        message = 'IAM Policy Version needs to be one of (%s).' % (\n                            ', '.join(map(str, ['2012-10-17', '2008-10-17'])))\n                        matches.append(\n                            RuleMatch(p_p + [parent_key], message))\n                if parent_key == 'Statement':\n                    if isinstance(parent_value, list):\n                        for i_s_v, i_s_p in parent_value.items_safe(p_p + ['Statement'], (dict)):\n                            matches.extend(\n                                self._check_policy_statement(\n                                    i_s_p, i_s_v, is_identity_policy, resource_exceptions\n                                )\n                            )\n                    elif isinstance(parent_value, dict):\n                        for i_s_v, i_s_p in parent_value.items_safe(p_p + ['Statement']):\n                            matches.extend(\n                                self._check_policy_statement(\n                                    i_s_p, i_s_v, is_identity_policy, resource_exceptions\n                                )\n                            )\n                    else:\n                        message = 'IAM Policy statement should be of list.'\n                        matches.append(\n                            RuleMatch(p_p + [parent_key], message))\n        return matches\n\n    def _check_policy_statement(self, branch, statement, is_identity_policy, resource_exceptions):\n        \"\"\"Check statements\"\"\"\n        matches = []\n        statement_valid_keys = [\n            'Effect',\n            'Principal',\n            'NotPrincipal',\n            'Action',\n            'NotAction',\n            'Resource',\n            'NotResource',\n            'Condition',\n            'Sid',\n        ]\n\n        for key, _ in statement.items():\n            if key not in statement_valid_keys:\n                message = 'IAM Policy statement key %s isn\\'t valid' % (key)\n                matches.append(\n                    RuleMatch(branch[:] + [key], message))\n        if 'Effect' not in statement:\n            message = 'IAM Policy statement missing Effect'\n            matches.append(\n                RuleMatch(branch[:], message))\n        else:\n            effect = statement.get('Effect')\n            if effect not in ['Allow', 'Deny']:\n                message = 'IAM Policy Effect should be Allow or Deny'\n                matches.append(\n                    RuleMatch(branch[:] + ['Effect'], message))\n        if 'Action' not in statement and 'NotAction' not in statement:\n            message = 'IAM Policy statement missing Action or NotAction'\n            matches.append(\n                RuleMatch(branch[:], message))\n        if is_identity_policy:\n            if 'Principal' in statement or 'NotPrincipal' in statement:\n                message = 'IAM Resource Policy statement shouldn\\'t have Principal or NotPrincipal'\n                matches.append(\n                    RuleMatch(branch[:], message))\n        else:\n            if 'Principal' not in statement and 'NotPrincipal' not in statement:\n                message = 'IAM Resource Policy statement should have Principal or NotPrincipal'\n                matches.append(\n                    RuleMatch(branch[:] + ['Principal'], message))\n        if not resource_exceptions:\n            if 'Resource' not in statement and 'NotResource' not in statement:\n                message = 'IAM Policy statement missing Resource or NotResource'\n                matches.append(\n                    RuleMatch(branch[:], message))\n\n        return(matches)\n\n    def match_resource_properties(self, properties, resourcetype, path, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = []\n\n        is_identity_policy = True\n        if resourcetype in self.resources_and_keys:\n            is_identity_policy = False\n\n        key = None\n        if resourcetype in self.resources_and_keys:\n            key = self.resources_and_keys.get(resourcetype)\n        else:\n            key = self.idp_and_keys.get(resourcetype)\n\n        if not key:\n            # Key isn't defined return nothing\n            return matches\n\n        resource_exceptions = False\n        if key == self.resource_exceptions.get(resourcetype):\n            resource_exceptions = True\n\n        other_keys = []\n        for key, value in self.resources_and_keys.items():\n            if value != 'Policies':\n                other_keys.append(key)\n        for key, value in self.idp_and_keys.items():\n            if value != 'Policies':\n                other_keys.append(key)\n\n        for key, value in properties.items():\n            if key == 'Policies' and isinstance(value, list):\n                for index, policy in enumerate(properties.get(key, [])):\n                    matches.extend(\n                        cfn.check_value(\n                            obj=policy, key='PolicyDocument',\n                            path=path[:] + ['Policies', index],\n                            check_value=self.check_policy_document,\n                            is_identity_policy=is_identity_policy,\n                            resource_exceptions=resource_exceptions,\n                            start_mark=key.start_mark, end_mark=key.end_mark,\n                        ))\n            elif key in ['KeyPolicy', 'PolicyDocument', 'RepositoryPolicyText', 'AccessPolicies']:\n                matches.extend(\n                    cfn.check_value(\n                        obj=properties, key=key,\n                        path=path[:],\n                        check_value=self.check_policy_document,\n                        is_identity_policy=is_identity_policy,\n                        resource_exceptions=resource_exceptions,\n                        start_mark=key.start_mark, end_mark=key.end_mark,\n                    ))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/iam/Policy.py"}], "after_files": [{"content": "\"\"\"\n  Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this\n  software and associated documentation files (the \"Software\"), to deal in the Software\n  without restriction, including without limitation the rights to use, copy, modify,\n  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\nimport json\nfrom datetime import date\nimport six\nfrom cfnlint.helpers import convert_dict\nfrom cfnlint import CloudFormationLintRule\nfrom cfnlint import RuleMatch\n\n\nclass Policy(CloudFormationLintRule):\n    \"\"\"Check if IAM Policy JSON is correct\"\"\"\n    id = 'E2507'\n    shortdesc = 'Check if IAM Policies are properly configured'\n    description = 'See if there elements inside an IAM policy ' + \\\n                  'are correct'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html'\n    tags = ['properties', 'iam']\n\n    def __init__(self):\n        \"\"\"Init\"\"\"\n        super(Policy, self).__init__()\n        self.resource_exceptions = {\n            'AWS::ECR::Repository': 'RepositoryPolicyText',\n        }\n        self.resources_and_keys = {\n            'AWS::SNS::TopicPolicy': 'PolicyDocument',\n            'AWS::S3::BucketPolicy': 'PolicyDocument',\n            'AWS::KMS::Key': 'KeyPolicy',\n            'AWS::SQS::QueuePolicy': 'PolicyDocument',\n            'AWS::ECR::Repository': 'RepositoryPolicyText',\n            'AWS::Elasticsearch::Domain': 'AccessPolicies',\n        }\n        self.idp_and_keys = {\n            'AWS::IAM::Group': 'Policies',\n            'AWS::IAM::ManagedPolicy': 'PolicyDocument',\n            'AWS::IAM::Policy': 'PolicyDocument',\n            'AWS::IAM::Role': 'Policies',\n            'AWS::IAM::User': 'Policies',\n        }\n        for resource_type in self.resources_and_keys:\n            self.resource_property_types.append(resource_type)\n        for resource_type in self.idp_and_keys:\n            self.resource_property_types.append(resource_type)\n\n    def check_policy_document(self, value, path, is_identity_policy, resource_exceptions, start_mark, end_mark):\n        \"\"\"Check policy document\"\"\"\n        matches = []\n\n        valid_keys = [\n            'Version',\n            'Id',\n            'Statement',\n        ]\n        valid_versions = ['2012-10-17', '2008-10-17', date(2012, 10, 17), date(2008, 10, 17)]\n\n        if isinstance(value, six.string_types):\n            try:\n                value = convert_dict(json.loads(value), start_mark, end_mark)\n            except Exception as ex:  # pylint: disable=W0703,W0612\n                message = 'IAM Policy Documents need to be JSON'\n                matches.append(RuleMatch(path[:], message))\n                return matches\n\n        if not isinstance(value, dict):\n            message = 'IAM Policy Documents needs to be JSON'\n            matches.append(\n                RuleMatch(path[:], message))\n            return matches\n\n        for p_vs, p_p in value.items_safe(path[:], (dict)):\n            for parent_key, parent_value in p_vs.items():\n                if parent_key not in valid_keys:\n                    message = 'IAM Policy key %s doesn\\'t exist.' % (parent_key)\n                    matches.append(\n                        RuleMatch(path[:] + p_p + [parent_key], message))\n                if parent_key == 'Version':\n                    if parent_value not in valid_versions:\n                        message = 'IAM Policy Version needs to be one of (%s).' % (\n                            ', '.join(map(str, ['2012-10-17', '2008-10-17'])))\n                        matches.append(\n                            RuleMatch(p_p + [parent_key], message))\n                if parent_key == 'Statement':\n                    if isinstance(parent_value, list):\n                        for i_s_v, i_s_p in parent_value.items_safe(p_p + ['Statement'], (dict)):\n                            matches.extend(\n                                self._check_policy_statement(\n                                    i_s_p, i_s_v, is_identity_policy, resource_exceptions\n                                )\n                            )\n                    elif isinstance(parent_value, dict):\n                        for i_s_v, i_s_p in parent_value.items_safe(p_p + ['Statement']):\n                            matches.extend(\n                                self._check_policy_statement(\n                                    i_s_p, i_s_v, is_identity_policy, resource_exceptions\n                                )\n                            )\n                    else:\n                        message = 'IAM Policy statement should be of list.'\n                        matches.append(\n                            RuleMatch(p_p + [parent_key], message))\n        return matches\n\n    def _check_policy_statement(self, branch, statement, is_identity_policy, resource_exceptions):\n        \"\"\"Check statements\"\"\"\n        matches = []\n        statement_valid_keys = [\n            'Effect',\n            'Principal',\n            'NotPrincipal',\n            'Action',\n            'NotAction',\n            'Resource',\n            'NotResource',\n            'Condition',\n            'Sid',\n        ]\n\n        for key, _ in statement.items():\n            if key not in statement_valid_keys:\n                message = 'IAM Policy statement key %s isn\\'t valid' % (key)\n                matches.append(\n                    RuleMatch(branch[:] + [key], message))\n        if 'Effect' not in statement:\n            message = 'IAM Policy statement missing Effect'\n            matches.append(\n                RuleMatch(branch[:], message))\n        else:\n            for effect, effect_path in statement.get_safe('Effect'):\n                if isinstance(effect, six.string_types):\n                    if effect not in ['Allow', 'Deny']:\n                        message = 'IAM Policy Effect should be Allow or Deny'\n                        matches.append(\n                            RuleMatch(branch[:] + effect_path, message))\n        if 'Action' not in statement and 'NotAction' not in statement:\n            message = 'IAM Policy statement missing Action or NotAction'\n            matches.append(\n                RuleMatch(branch[:], message))\n        if is_identity_policy:\n            if 'Principal' in statement or 'NotPrincipal' in statement:\n                message = 'IAM Resource Policy statement shouldn\\'t have Principal or NotPrincipal'\n                matches.append(\n                    RuleMatch(branch[:], message))\n        else:\n            if 'Principal' not in statement and 'NotPrincipal' not in statement:\n                message = 'IAM Resource Policy statement should have Principal or NotPrincipal'\n                matches.append(\n                    RuleMatch(branch[:] + ['Principal'], message))\n        if not resource_exceptions:\n            if 'Resource' not in statement and 'NotResource' not in statement:\n                message = 'IAM Policy statement missing Resource or NotResource'\n                matches.append(\n                    RuleMatch(branch[:], message))\n\n        return(matches)\n\n    def match_resource_properties(self, properties, resourcetype, path, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = []\n\n        is_identity_policy = True\n        if resourcetype in self.resources_and_keys:\n            is_identity_policy = False\n\n        key = None\n        if resourcetype in self.resources_and_keys:\n            key = self.resources_and_keys.get(resourcetype)\n        else:\n            key = self.idp_and_keys.get(resourcetype)\n\n        if not key:\n            # Key isn't defined return nothing\n            return matches\n\n        resource_exceptions = False\n        if key == self.resource_exceptions.get(resourcetype):\n            resource_exceptions = True\n\n        other_keys = []\n        for key, value in self.resources_and_keys.items():\n            if value != 'Policies':\n                other_keys.append(key)\n        for key, value in self.idp_and_keys.items():\n            if value != 'Policies':\n                other_keys.append(key)\n\n        for key, value in properties.items():\n            if key == 'Policies' and isinstance(value, list):\n                for index, policy in enumerate(properties.get(key, [])):\n                    matches.extend(\n                        cfn.check_value(\n                            obj=policy, key='PolicyDocument',\n                            path=path[:] + ['Policies', index],\n                            check_value=self.check_policy_document,\n                            is_identity_policy=is_identity_policy,\n                            resource_exceptions=resource_exceptions,\n                            start_mark=key.start_mark, end_mark=key.end_mark,\n                        ))\n            elif key in ['KeyPolicy', 'PolicyDocument', 'RepositoryPolicyText', 'AccessPolicies']:\n                matches.extend(\n                    cfn.check_value(\n                        obj=properties, key=key,\n                        path=path[:],\n                        check_value=self.check_policy_document,\n                        is_identity_policy=is_identity_policy,\n                        resource_exceptions=resource_exceptions,\n                        start_mark=key.start_mark, end_mark=key.end_mark,\n                    ))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/iam/Policy.py"}]}