Repository: getmoto/moto

Issue:
IAM: create_policy does not complain about invalid JSON in policy document
Versions:

```
$ pip freeze|grep oto
boto==2.48.0
boto3==1.5.14
botocore==1.8.28
moto==1.2.0
```

Note that the python code in #1434 contains invalid json (the final comma on the third line from the bottom of the JSON should not be there). Calling AWS with this json fails, but moto lets me create this policy. Should it at least try to convert the json to a python object to see if it's valid?

Thanks.




Discussion:

Resolving patch:
diff --git a/moto/iam/exceptions.py b/moto/iam/exceptions.py
--- a/moto/iam/exceptions.py
+++ b/moto/iam/exceptions.py
@@ -34,6 +34,14 @@ def __init__(self, cert):
             'MalformedCertificate', 'Certificate {cert} is malformed'.format(cert=cert))
 
 
+class MalformedPolicyDocument(RESTError):
+    code = 400
+
+    def __init__(self, message=""):
+        super(MalformedPolicyDocument, self).__init__(
+            'MalformedPolicyDocument', message)
+
+
 class DuplicateTags(RESTError):
     code = 400
 
diff --git a/moto/iam/models.py b/moto/iam/models.py
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -11,6 +11,7 @@
 from moto.core.exceptions import RESTError
 from moto.core import BaseBackend, BaseModel
 from moto.core.utils import iso_8601_datetime_without_milliseconds, iso_8601_datetime_with_milliseconds
+from moto.iam.policy_validation import IAMPolicyDocumentValidator
 
 from .aws_managed_policies import aws_managed_policies_data
 from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException, MalformedCertificate, \
@@ -568,6 +569,9 @@ def detach_user_policy(self, policy_arn, user_name):
         policy.detach_from(self.get_user(user_name))
 
     def create_policy(self, description, path, policy_document, policy_name):
+        iam_policy_document_validator = IAMPolicyDocumentValidator(policy_document)
+        iam_policy_document_validator.validate()
+
         policy = ManagedPolicy(
             policy_name,
             description=description,
@@ -660,6 +664,9 @@ def get_roles(self):
 
     def put_role_policy(self, role_name, policy_name, policy_json):
         role = self.get_role(role_name)
+
+        iam_policy_document_validator = IAMPolicyDocumentValidator(policy_json)
+        iam_policy_document_validator.validate()
         role.put_policy(policy_name, policy_json)
 
     def delete_role_policy(self, role_name, policy_name):
@@ -757,9 +764,13 @@ def untag_role(self, role_name, tag_keys):
             role.tags.pop(ref_key, None)
 
     def create_policy_version(self, policy_arn, policy_document, set_as_default):
+        iam_policy_document_validator = IAMPolicyDocumentValidator(policy_document)
+        iam_policy_document_validator.validate()
+
         policy = self.get_policy(policy_arn)
         if not policy:
             raise IAMNotFoundException("Policy not found")
+
         version = PolicyVersion(policy_arn, policy_document, set_as_default)
         policy.versions.append(version)
         version.version_id = 'v{0}'.format(policy.next_version_num)
@@ -901,6 +912,9 @@ def get_groups_for_user(self, user_name):
 
     def put_group_policy(self, group_name, policy_name, policy_json):
         group = self.get_group(group_name)
+
+        iam_policy_document_validator = IAMPolicyDocumentValidator(policy_json)
+        iam_policy_document_validator.validate()
         group.put_policy(policy_name, policy_json)
 
     def list_group_policies(self, group_name, marker=None, max_items=None):
@@ -1061,6 +1075,9 @@ def list_user_policies(self, user_name):
 
     def put_user_policy(self, user_name, policy_name, policy_json):
         user = self.get_user(user_name)
+
+        iam_policy_document_validator = IAMPolicyDocumentValidator(policy_json)
+        iam_policy_document_validator.validate()
         user.put_policy(policy_name, policy_json)
 
     def delete_user_policy(self, user_name, policy_name):
diff --git a/moto/iam/policy_validation.py b/moto/iam/policy_validation.py
new file mode 100644
--- /dev/null
+++ b/moto/iam/policy_validation.py
@@ -0,0 +1,450 @@
+import json
+import re
+
+from six import string_types
+
+from moto.iam.exceptions import MalformedPolicyDocument
+
+
+VALID_TOP_ELEMENTS = [
+    "Version",
+    "Id",
+    "Statement",
+    "Conditions"
+]
+
+VALID_VERSIONS = [
+    "2008-10-17",
+    "2012-10-17"
+]
+
+VALID_STATEMENT_ELEMENTS = [
+    "Sid",
+    "Action",
+    "NotAction",
+    "Resource",
+    "NotResource",
+    "Effect",
+    "Condition"
+]
+
+VALID_EFFECTS = [
+    "Allow",
+    "Deny"
+]
+
+VALID_CONDITIONS = [
+    "StringEquals",
+    "StringNotEquals",
+    "StringEqualsIgnoreCase",
+    "StringNotEqualsIgnoreCase",
+    "StringLike",
+    "StringNotLike",
+    "NumericEquals",
+    "NumericNotEquals",
+    "NumericLessThan",
+    "NumericLessThanEquals",
+    "NumericGreaterThan",
+    "NumericGreaterThanEquals",
+    "DateEquals",
+    "DateNotEquals",
+    "DateLessThan",
+    "DateLessThanEquals",
+    "DateGreaterThan",
+    "DateGreaterThanEquals",
+    "Bool",
+    "BinaryEquals",
+    "IpAddress",
+    "NotIpAddress",
+    "ArnEquals",
+    "ArnLike",
+    "ArnNotEquals",
+    "ArnNotLike",
+    "Null"
+]
+
+VALID_CONDITION_PREFIXES = [
+    "ForAnyValue:",
+    "ForAllValues:"
+]
+
+VALID_CONDITION_POSTFIXES = [
+    "IfExists"
+]
+
+SERVICE_TYPE_REGION_INFORMATION_ERROR_ASSOCIATIONS = {
+    "iam": 'IAM resource {resource} cannot contain region information.',
+    "s3": 'Resource {resource} can not contain region information.'
+}
+
+VALID_RESOURCE_PATH_STARTING_VALUES = {
+    "iam": {
+        "values": ["user/", "federated-user/", "role/", "group/", "instance-profile/", "mfa/", "server-certificate/",
+                   "policy/", "sms-mfa/", "saml-provider/", "oidc-provider/", "report/", "access-report/"],
+        "error_message": 'IAM resource path must either be "*" or start with {values}.'
+    }
+}
+
+
+class IAMPolicyDocumentValidator:
+
+    def __init__(self, policy_document):
+        self._policy_document = policy_document
+        self._policy_json = {}
+        self._statements = []
+        self._resource_error = ""  # the first resource error found that does not generate a legacy parsing error
+
+    def validate(self):
+        try:
+            self._validate_syntax()
+        except Exception:
+            raise MalformedPolicyDocument("Syntax errors in policy.")
+        try:
+            self._validate_version()
+        except Exception:
+            raise MalformedPolicyDocument("Policy document must be version 2012-10-17 or greater.")
+        try:
+            self._perform_first_legacy_parsing()
+            self._validate_resources_for_formats()
+            self._validate_not_resources_for_formats()
+        except Exception:
+            raise MalformedPolicyDocument("The policy failed legacy parsing")
+        try:
+            self._validate_sid_uniqueness()
+        except Exception:
+            raise MalformedPolicyDocument("Statement IDs (SID) in a single policy must be unique.")
+        try:
+            self._validate_action_like_exist()
+        except Exception:
+            raise MalformedPolicyDocument("Policy statement must contain actions.")
+        try:
+            self._validate_resource_exist()
+        except Exception:
+            raise MalformedPolicyDocument("Policy statement must contain resources.")
+
+        if self._resource_error != "":
+            raise MalformedPolicyDocument(self._resource_error)
+
+        self._validate_actions_for_prefixes()
+        self._validate_not_actions_for_prefixes()
+
+    def _validate_syntax(self):
+        self._policy_json = json.loads(self._policy_document)
+        assert isinstance(self._policy_json, dict)
+        self._validate_top_elements()
+        self._validate_version_syntax()
+        self._validate_id_syntax()
+        self._validate_statements_syntax()
+
+    def _validate_top_elements(self):
+        top_elements = self._policy_json.keys()
+        for element in top_elements:
+            assert element in VALID_TOP_ELEMENTS
+
+    def _validate_version_syntax(self):
+        if "Version" in self._policy_json:
+            assert self._policy_json["Version"] in VALID_VERSIONS
+
+    def _validate_version(self):
+        assert self._policy_json["Version"] == "2012-10-17"
+
+    def _validate_sid_uniqueness(self):
+        sids = []
+        for statement in self._statements:
+            if "Sid" in statement:
+                assert statement["Sid"] not in sids
+                sids.append(statement["Sid"])
+
+    def _validate_statements_syntax(self):
+        assert "Statement" in self._policy_json
+        assert isinstance(self._policy_json["Statement"], (dict, list))
+
+        if isinstance(self._policy_json["Statement"], dict):
+            self._statements.append(self._policy_json["Statement"])
+        else:
+            self._statements += self._policy_json["Statement"]
+
+        assert self._statements
+        for statement in self._statements:
+            self._validate_statement_syntax(statement)
+
+    @staticmethod
+    def _validate_statement_syntax(statement):
+        assert isinstance(statement, dict)
+        for statement_element in statement.keys():
+            assert statement_element in VALID_STATEMENT_ELEMENTS
+
+        assert ("Resource" not in statement or "NotResource" not in statement)
+        assert ("Action" not in statement or "NotAction" not in statement)
+
+        IAMPolicyDocumentValidator._validate_effect_syntax(statement)
+        IAMPolicyDocumentValidator._validate_action_syntax(statement)
+        IAMPolicyDocumentValidator._validate_not_action_syntax(statement)
+        IAMPolicyDocumentValidator._validate_resource_syntax(statement)
+        IAMPolicyDocumentValidator._validate_not_resource_syntax(statement)
+        IAMPolicyDocumentValidator._validate_condition_syntax(statement)
+        IAMPolicyDocumentValidator._validate_sid_syntax(statement)
+
+    @staticmethod
+    def _validate_effect_syntax(statement):
+        assert "Effect" in statement
+        assert isinstance(statement["Effect"], string_types)
+        assert statement["Effect"].lower() in [allowed_effect.lower() for allowed_effect in VALID_EFFECTS]
+
+    @staticmethod
+    def _validate_action_syntax(statement):
+        IAMPolicyDocumentValidator._validate_string_or_list_of_strings_syntax(statement, "Action")
+
+    @staticmethod
+    def _validate_not_action_syntax(statement):
+        IAMPolicyDocumentValidator._validate_string_or_list_of_strings_syntax(statement, "NotAction")
+
+    @staticmethod
+    def _validate_resource_syntax(statement):
+        IAMPolicyDocumentValidator._validate_string_or_list_of_strings_syntax(statement, "Resource")
+
+    @staticmethod
+    def _validate_not_resource_syntax(statement):
+        IAMPolicyDocumentValidator._validate_string_or_list_of_strings_syntax(statement, "NotResource")
+
+    @staticmethod
+    def _validate_string_or_list_of_strings_syntax(statement, key):
+        if key in statement:
+            assert isinstance(statement[key], (string_types, list))
+            if isinstance(statement[key], list):
+                for resource in statement[key]:
+                    assert isinstance(resource, string_types)
+
+    @staticmethod
+    def _validate_condition_syntax(statement):
+        if "Condition" in statement:
+            assert isinstance(statement["Condition"], dict)
+            for condition_key, condition_value in statement["Condition"].items():
+                assert isinstance(condition_value, dict)
+                for condition_element_key, condition_element_value in condition_value.items():
+                    assert isinstance(condition_element_value, (list, string_types))
+
+                if IAMPolicyDocumentValidator._strip_condition_key(condition_key) not in VALID_CONDITIONS:
+                    assert not condition_value  # empty dict
+
+    @staticmethod
+    def _strip_condition_key(condition_key):
+        for valid_prefix in VALID_CONDITION_PREFIXES:
+            if condition_key.startswith(valid_prefix):
+                condition_key = condition_key[len(valid_prefix):]
+                break  # strip only the first match
+
+        for valid_postfix in VALID_CONDITION_POSTFIXES:
+            if condition_key.endswith(valid_postfix):
+                condition_key = condition_key[:-len(valid_postfix)]
+                break  # strip only the first match
+
+        return condition_key
+
+    @staticmethod
+    def _validate_sid_syntax(statement):
+        if "Sid" in statement:
+            assert isinstance(statement["Sid"], string_types)
+
+    def _validate_id_syntax(self):
+        if "Id" in self._policy_json:
+            assert isinstance(self._policy_json["Id"], string_types)
+
+    def _validate_resource_exist(self):
+        for statement in self._statements:
+            assert ("Resource" in statement or "NotResource" in statement)
+            if "Resource" in statement and isinstance(statement["Resource"], list):
+                assert statement["Resource"]
+            elif "NotResource" in statement and isinstance(statement["NotResource"], list):
+                assert statement["NotResource"]
+
+    def _validate_action_like_exist(self):
+        for statement in self._statements:
+            assert ("Action" in statement or "NotAction" in statement)
+            if "Action" in statement and isinstance(statement["Action"], list):
+                assert statement["Action"]
+            elif "NotAction" in statement and isinstance(statement["NotAction"], list):
+                assert statement["NotAction"]
+
+    def _validate_actions_for_prefixes(self):
+        self._validate_action_like_for_prefixes("Action")
+
+    def _validate_not_actions_for_prefixes(self):
+        self._validate_action_like_for_prefixes("NotAction")
+
+    def _validate_action_like_for_prefixes(self, key):
+        for statement in self._statements:
+            if key in statement:
+                if isinstance(statement[key], string_types):
+                    self._validate_action_prefix(statement[key])
+                else:
+                    for action in statement[key]:
+                        self._validate_action_prefix(action)
+
+    @staticmethod
+    def _validate_action_prefix(action):
+        action_parts = action.split(":")
+        if len(action_parts) == 1 and action_parts[0] != "*":
+            raise MalformedPolicyDocument("Actions/Conditions must be prefaced by a vendor, e.g., iam, sdb, ec2, etc.")
+        elif len(action_parts) > 2:
+            raise MalformedPolicyDocument("Actions/Condition can contain only one colon.")
+
+        vendor_pattern = re.compile(r'[^a-zA-Z0-9\-.]')
+        if action_parts[0] != "*" and vendor_pattern.search(action_parts[0]):
+            raise MalformedPolicyDocument("Vendor {vendor} is not valid".format(vendor=action_parts[0]))
+
+    def _validate_resources_for_formats(self):
+        self._validate_resource_like_for_formats("Resource")
+
+    def _validate_not_resources_for_formats(self):
+        self._validate_resource_like_for_formats("NotResource")
+
+    def _validate_resource_like_for_formats(self, key):
+        for statement in self._statements:
+            if key in statement:
+                if isinstance(statement[key], string_types):
+                    self._validate_resource_format(statement[key])
+                else:
+                    for resource in sorted(statement[key], reverse=True):
+                        self._validate_resource_format(resource)
+                if self._resource_error == "":
+                    IAMPolicyDocumentValidator._legacy_parse_resource_like(statement, key)
+
+    def _validate_resource_format(self, resource):
+        if resource != "*":
+            resource_partitions = resource.partition(":")
+
+            if resource_partitions[1] == "":
+                self._resource_error = 'Resource {resource} must be in ARN format or "*".'.format(resource=resource)
+                return
+
+            resource_partitions = resource_partitions[2].partition(":")
+            if resource_partitions[0] != "aws":
+                remaining_resource_parts = resource_partitions[2].split(":")
+
+                arn1 = remaining_resource_parts[0] if remaining_resource_parts[0] != "" or len(remaining_resource_parts) > 1 else "*"
+                arn2 = remaining_resource_parts[1] if len(remaining_resource_parts) > 1 else "*"
+                arn3 = remaining_resource_parts[2] if len(remaining_resource_parts) > 2 else "*"
+                arn4 = ":".join(remaining_resource_parts[3:]) if len(remaining_resource_parts) > 3 else "*"
+                self._resource_error = 'Partition "{partition}" is not valid for resource "arn:{partition}:{arn1}:{arn2}:{arn3}:{arn4}".'.format(
+                    partition=resource_partitions[0],
+                    arn1=arn1,
+                    arn2=arn2,
+                    arn3=arn3,
+                    arn4=arn4
+                )
+                return
+
+            if resource_partitions[1] != ":":
+                self._resource_error = "Resource vendor must be fully qualified and cannot contain regexes."
+                return
+
+            resource_partitions = resource_partitions[2].partition(":")
+
+            service = resource_partitions[0]
+
+            if service in SERVICE_TYPE_REGION_INFORMATION_ERROR_ASSOCIATIONS.keys() and not resource_partitions[2].startswith(":"):
+                self._resource_error = SERVICE_TYPE_REGION_INFORMATION_ERROR_ASSOCIATIONS[service].format(resource=resource)
+                return
+
+            resource_partitions = resource_partitions[2].partition(":")
+            resource_partitions = resource_partitions[2].partition(":")
+
+            if service in VALID_RESOURCE_PATH_STARTING_VALUES.keys():
+                valid_start = False
+                for valid_starting_value in VALID_RESOURCE_PATH_STARTING_VALUES[service]["values"]:
+                    if resource_partitions[2].startswith(valid_starting_value):
+                        valid_start = True
+                        break
+                if not valid_start:
+                    self._resource_error = VALID_RESOURCE_PATH_STARTING_VALUES[service]["error_message"].format(
+                        values=", ".join(VALID_RESOURCE_PATH_STARTING_VALUES[service]["values"])
+                    )
+
+    def _perform_first_legacy_parsing(self):
+        """This method excludes legacy parsing resources, since that have to be done later."""
+        for statement in self._statements:
+            self._legacy_parse_statement(statement)
+
+    @staticmethod
+    def _legacy_parse_statement(statement):
+        assert statement["Effect"] in VALID_EFFECTS  # case-sensitive matching
+        if "Condition" in statement:
+            for condition_key, condition_value in statement["Condition"].items():
+                IAMPolicyDocumentValidator._legacy_parse_condition(condition_key, condition_value)
+
+    @staticmethod
+    def _legacy_parse_resource_like(statement, key):
+        if isinstance(statement[key], string_types):
+            if statement[key] != "*":
+                assert statement[key].count(":") >= 5 or "::" not in statement[key]
+                assert statement[key].split(":")[2] != ""
+        else:  # list
+            for resource in statement[key]:
+                if resource != "*":
+                    assert resource.count(":") >= 5 or "::" not in resource
+                    assert resource[2] != ""
+
+    @staticmethod
+    def _legacy_parse_condition(condition_key, condition_value):
+        stripped_condition_key = IAMPolicyDocumentValidator._strip_condition_key(condition_key)
+
+        if stripped_condition_key.startswith("Date"):
+            for condition_element_key, condition_element_value in condition_value.items():
+                if isinstance(condition_element_value, string_types):
+                    IAMPolicyDocumentValidator._legacy_parse_date_condition_value(condition_element_value)
+                else:  # it has to be a list
+                    for date_condition_value in condition_element_value:
+                        IAMPolicyDocumentValidator._legacy_parse_date_condition_value(date_condition_value)
+
+    @staticmethod
+    def _legacy_parse_date_condition_value(date_condition_value):
+        if "t" in date_condition_value.lower() or "-" in date_condition_value:
+            IAMPolicyDocumentValidator._validate_iso_8601_datetime(date_condition_value.lower())
+        else:  # timestamp
+            assert 0 <= int(date_condition_value) <= 9223372036854775807
+
+    @staticmethod
+    def _validate_iso_8601_datetime(datetime):
+        datetime_parts = datetime.partition("t")
+        negative_year = datetime_parts[0].startswith("-")
+        date_parts = datetime_parts[0][1:].split("-") if negative_year else datetime_parts[0].split("-")
+        year = "-" + date_parts[0] if negative_year else date_parts[0]
+        assert -292275054 <= int(year) <= 292278993
+        if len(date_parts) > 1:
+            month = date_parts[1]
+            assert 1 <= int(month) <= 12
+        if len(date_parts) > 2:
+            day = date_parts[2]
+            assert 1 <= int(day) <= 31
+        assert len(date_parts) < 4
+
+        time_parts = datetime_parts[2].split(":")
+        if time_parts[0] != "":
+            hours = time_parts[0]
+            assert 0 <= int(hours) <= 23
+        if len(time_parts) > 1:
+            minutes = time_parts[1]
+            assert 0 <= int(minutes) <= 59
+        if len(time_parts) > 2:
+            if "z" in time_parts[2]:
+                seconds_with_decimal_fraction = time_parts[2].partition("z")[0]
+                assert time_parts[2].partition("z")[2] == ""
+            elif "+" in time_parts[2]:
+                seconds_with_decimal_fraction = time_parts[2].partition("+")[0]
+                time_zone_data = time_parts[2].partition("+")[2].partition(":")
+                time_zone_hours = time_zone_data[0]
+                assert len(time_zone_hours) == 2
+                assert 0 <= int(time_zone_hours) <= 23
+                if time_zone_data[1] == ":":
+                    time_zone_minutes = time_zone_data[2]
+                    assert len(time_zone_minutes) == 2
+                    assert 0 <= int(time_zone_minutes) <= 59
+            else:
+                seconds_with_decimal_fraction = time_parts[2]
+            seconds_with_decimal_fraction_partition = seconds_with_decimal_fraction.partition(".")
+            seconds = seconds_with_decimal_fraction_partition[0]
+            assert 0 <= int(seconds) <= 59
+            if seconds_with_decimal_fraction_partition[1] == ".":
+                decimal_seconds = seconds_with_decimal_fraction_partition[2]
+                assert 0 <= int(decimal_seconds) <= 999999999


Test patch:
diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py
--- a/tests/test_iam/test_iam.py
+++ b/tests/test_iam/test_iam.py
@@ -1,5 +1,6 @@
 from __future__ import unicode_literals
 import base64
+import json
 
 import boto
 import boto3
@@ -29,6 +30,44 @@
 8vUH+pBa6IBLbvBp+szli51V3TLSWcoyy4ceJNQU2vCkTLoFdS0RLd/7tQ==
 -----END CERTIFICATE-----"""
 
+MOCK_POLICY = """
+{
+  "Version": "2012-10-17",
+  "Statement":
+    {
+      "Effect": "Allow",
+      "Action": "s3:ListBucket",
+      "Resource": "arn:aws:s3:::example_bucket"
+    }
+}
+"""
+
+MOCK_POLICY_2 = """
+{
+  "Version": "2012-10-17",
+  "Id": "2",
+  "Statement":
+    {
+      "Effect": "Allow",
+      "Action": "s3:ListBucket",
+      "Resource": "arn:aws:s3:::example_bucket"
+    }
+}
+"""
+
+MOCK_POLICY_3 = """
+{
+  "Version": "2012-10-17",
+  "Id": "3",
+  "Statement":
+    {
+      "Effect": "Allow",
+      "Action": "s3:ListBucket",
+      "Resource": "arn:aws:s3:::example_bucket"
+    }
+}
+"""
+
 
 @mock_iam_deprecated()
 def test_get_all_server_certs():
@@ -243,12 +282,12 @@ def test_list_instance_profiles_for_role():
 def test_list_role_policies():
     conn = boto.connect_iam()
     conn.create_role("my-role")
-    conn.put_role_policy("my-role", "test policy", "my policy")
+    conn.put_role_policy("my-role", "test policy", MOCK_POLICY)
     role = conn.list_role_policies("my-role")
     role.policy_names.should.have.length_of(1)
     role.policy_names[0].should.equal("test policy")
 
-    conn.put_role_policy("my-role", "test policy 2", "another policy")
+    conn.put_role_policy("my-role", "test policy 2", MOCK_POLICY)
     role = conn.list_role_policies("my-role")
     role.policy_names.should.have.length_of(2)
 
@@ -266,7 +305,7 @@ def test_put_role_policy():
     conn = boto.connect_iam()
     conn.create_role(
         "my-role", assume_role_policy_document="some policy", path="my-path")
-    conn.put_role_policy("my-role", "test policy", "my policy")
+    conn.put_role_policy("my-role", "test policy", MOCK_POLICY)
     policy = conn.get_role_policy(
         "my-role", "test policy")['get_role_policy_response']['get_role_policy_result']['policy_name']
     policy.should.equal("test policy")
@@ -286,7 +325,7 @@ def test_create_policy():
     conn = boto3.client('iam', region_name='us-east-1')
     response = conn.create_policy(
         PolicyName="TestCreatePolicy",
-        PolicyDocument='{"some":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     response['Policy']['Arn'].should.equal("arn:aws:iam::123456789012:policy/TestCreatePolicy")
 
 
@@ -299,19 +338,19 @@ def test_create_policy_versions():
             PolicyDocument='{"some":"policy"}')
     conn.create_policy(
         PolicyName="TestCreatePolicyVersion",
-        PolicyDocument='{"some":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     version = conn.create_policy_version(
         PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
-        PolicyDocument='{"some":"policy"}',
+        PolicyDocument=MOCK_POLICY,
         SetAsDefault=True)
-    version.get('PolicyVersion').get('Document').should.equal({'some': 'policy'})
+    version.get('PolicyVersion').get('Document').should.equal(json.loads(MOCK_POLICY))
     version.get('PolicyVersion').get('VersionId').should.equal("v2")
     conn.delete_policy_version(
         PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
         VersionId="v1")
     version = conn.create_policy_version(
         PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
-        PolicyDocument='{"some":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     version.get('PolicyVersion').get('VersionId').should.equal("v3")
 
 
@@ -320,7 +359,7 @@ def test_get_policy():
     conn = boto3.client('iam', region_name='us-east-1')
     response = conn.create_policy(
         PolicyName="TestGetPolicy",
-        PolicyDocument='{"some":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     policy = conn.get_policy(
         PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicy")
     policy['Policy']['Arn'].should.equal("arn:aws:iam::123456789012:policy/TestGetPolicy")
@@ -342,10 +381,10 @@ def test_get_policy_version():
     conn = boto3.client('iam', region_name='us-east-1')
     conn.create_policy(
         PolicyName="TestGetPolicyVersion",
-        PolicyDocument='{"some":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     version = conn.create_policy_version(
         PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
-        PolicyDocument='{"some":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     with assert_raises(ClientError):
         conn.get_policy_version(
             PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
@@ -353,7 +392,7 @@ def test_get_policy_version():
     retrieved = conn.get_policy_version(
         PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
         VersionId=version.get('PolicyVersion').get('VersionId'))
-    retrieved.get('PolicyVersion').get('Document').should.equal({'some': 'policy'})
+    retrieved.get('PolicyVersion').get('Document').should.equal(json.loads(MOCK_POLICY))
 
 
 @mock_iam
@@ -396,22 +435,22 @@ def test_list_policy_versions():
             PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions")
     conn.create_policy(
         PolicyName="TestListPolicyVersions",
-        PolicyDocument='{"first":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     versions = conn.list_policy_versions(
         PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions")
     versions.get('Versions')[0].get('VersionId').should.equal('v1')
 
     conn.create_policy_version(
         PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions",
-        PolicyDocument='{"second":"policy"}')
+        PolicyDocument=MOCK_POLICY_2)
     conn.create_policy_version(
         PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions",
-        PolicyDocument='{"third":"policy"}')
+        PolicyDocument=MOCK_POLICY_3)
     versions = conn.list_policy_versions(
         PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions")
     print(versions.get('Versions'))
-    versions.get('Versions')[1].get('Document').should.equal({'second': 'policy'})
-    versions.get('Versions')[2].get('Document').should.equal({'third': 'policy'})
+    versions.get('Versions')[1].get('Document').should.equal(json.loads(MOCK_POLICY_2))
+    versions.get('Versions')[2].get('Document').should.equal(json.loads(MOCK_POLICY_3))
 
 
 @mock_iam
@@ -419,10 +458,10 @@ def test_delete_policy_version():
     conn = boto3.client('iam', region_name='us-east-1')
     conn.create_policy(
         PolicyName="TestDeletePolicyVersion",
-        PolicyDocument='{"first":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     conn.create_policy_version(
         PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
-        PolicyDocument='{"second":"policy"}')
+        PolicyDocument=MOCK_POLICY)
     with assert_raises(ClientError):
         conn.delete_policy_version(
             PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
@@ -489,22 +528,20 @@ def test_list_users():
 @mock_iam()
 def test_user_policies():
     policy_name = 'UserManagedPolicy'
-    policy_document = "{'mypolicy': 'test'}"
     user_name = 'my-user'
     conn = boto3.client('iam', region_name='us-east-1')
     conn.create_user(UserName=user_name)
     conn.put_user_policy(
         UserName=user_name,
         PolicyName=policy_name,
-        PolicyDocument=policy_document
+        PolicyDocument=MOCK_POLICY
     )
 
     policy_doc = conn.get_user_policy(
         UserName=user_name,
         PolicyName=policy_name
     )
-    test = policy_document in policy_doc['PolicyDocument']
-    test.should.equal(True)
+    policy_doc['PolicyDocument'].should.equal(json.loads(MOCK_POLICY))
 
     policies = conn.list_user_policies(UserName=user_name)
     len(policies['PolicyNames']).should.equal(1)
@@ -665,7 +702,7 @@ def test_managed_policy():
     conn = boto.connect_iam()
 
     conn.create_policy(policy_name='UserManagedPolicy',
-                       policy_document={'mypolicy': 'test'},
+                       policy_document=MOCK_POLICY,
                        path='/mypolicy/',
                        description='my user managed policy')
 
@@ -766,7 +803,7 @@ def test_attach_detach_user_policy():
 
     policy_name = 'UserAttachedPolicy'
     policy = iam.create_policy(PolicyName=policy_name,
-                               PolicyDocument='{"mypolicy": "test"}',
+                               PolicyDocument=MOCK_POLICY,
                                Path='/mypolicy/',
                                Description='my user attached policy')
 
@@ -822,7 +859,6 @@ def test_get_access_key_last_used():
 
 @mock_iam
 def test_get_account_authorization_details():
-    import json
     test_policy = json.dumps({
         "Version": "2012-10-17",
         "Statement": [
@@ -1254,7 +1290,6 @@ def test_update_role():
 
 @mock_iam()
 def test_list_entities_for_policy():
-    import json
     test_policy = json.dumps({
         "Version": "2012-10-17",
         "Statement": [
diff --git a/tests/test_iam/test_iam_groups.py b/tests/test_iam/test_iam_groups.py
--- a/tests/test_iam/test_iam_groups.py
+++ b/tests/test_iam/test_iam_groups.py
@@ -10,6 +10,18 @@
 from boto.exception import BotoServerError
 from moto import mock_iam, mock_iam_deprecated
 
+MOCK_POLICY = """
+{
+  "Version": "2012-10-17",
+  "Statement":
+    {
+      "Effect": "Allow",
+      "Action": "s3:ListBucket",
+      "Resource": "arn:aws:s3:::example_bucket"
+    }
+}
+"""
+
 
 @mock_iam_deprecated()
 def test_create_group():
@@ -101,7 +113,7 @@ def test_get_groups_for_user():
 def test_put_group_policy():
     conn = boto.connect_iam()
     conn.create_group('my-group')
-    conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
+    conn.put_group_policy('my-group', 'my-policy', MOCK_POLICY)
 
 
 @mock_iam
@@ -131,7 +143,7 @@ def test_get_group_policy():
     with assert_raises(BotoServerError):
         conn.get_group_policy('my-group', 'my-policy')
 
-    conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
+    conn.put_group_policy('my-group', 'my-policy', MOCK_POLICY)
     conn.get_group_policy('my-group', 'my-policy')
 
 
@@ -141,7 +153,7 @@ def test_get_all_group_policies():
     conn.create_group('my-group')
     policies = conn.get_all_group_policies('my-group')['list_group_policies_response']['list_group_policies_result']['policy_names']
     assert policies == []
-    conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
+    conn.put_group_policy('my-group', 'my-policy', MOCK_POLICY)
     policies = conn.get_all_group_policies('my-group')['list_group_policies_response']['list_group_policies_result']['policy_names']
     assert policies == ['my-policy']
 
@@ -151,5 +163,5 @@ def test_list_group_policies():
     conn = boto3.client('iam', region_name='us-east-1')
     conn.create_group(GroupName='my-group')
     conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.be.empty
-    conn.put_group_policy(GroupName='my-group', PolicyName='my-policy', PolicyDocument='{"some": "json"}')
+    conn.put_group_policy(GroupName='my-group', PolicyName='my-policy', PolicyDocument=MOCK_POLICY)
     conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.equal(['my-policy'])
diff --git a/tests/test_iam/test_iam_policies.py b/tests/test_iam/test_iam_policies.py
new file mode 100644
--- /dev/null
+++ b/tests/test_iam/test_iam_policies.py
@@ -0,0 +1,1861 @@
+import json
+
+import boto3
+from botocore.exceptions import ClientError
+from nose.tools import assert_raises
+
+from moto import mock_iam
+
+invalid_policy_document_test_cases = [
+    {
+        "document": "This is not a json document",
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Policy document must be version 2012-10-17 or greater.'
+    },
+    {
+        "document": {
+            "Version": "2008-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Policy document must be version 2012-10-17 or greater.'
+    },
+    {
+        "document": {
+            "Version": "2013-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17"
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": ["afd"]
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            },
+            "Extra field": "value"
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Extra field": "value"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Id": ["cd3a324d2343d942772346-34234234423404-4c2242343242349d1642ee"],
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Id": {},
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "invalid",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "invalid",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Actions/Conditions must be prefaced by a vendor, e.g., iam, sdb, ec2, etc.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "NotAction": "",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+        },
+        "error_message": 'Actions/Conditions must be prefaced by a vendor, e.g., iam, sdb, ec2, etc.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "a a:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+        },
+        "error_message": 'Vendor a a is not valid'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:List:Bucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+        },
+        "error_message": 'Actions/Condition can contain only one colon.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Effect": "Allow",
+                    "Action": "s3s:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                },
+                {
+                    "Effect": "Allow",
+                    "Action": "s:3s:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+            ]
+        },
+        "error_message": 'Actions/Condition can contain only one colon.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "invalid resource"
+            }
+        },
+        "error_message": 'Resource invalid resource must be in ARN format or "*".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Sid": "EnableDisableHongKong",
+                    "Effect": "Allow",
+                    "Action": [
+                        "account:EnableRegion",
+                        "account:DisableRegion"
+                    ],
+                    "Resource": "",
+                    "Condition": {
+                        "StringEquals": {"account:TargetRegion": "ap-east-1"}
+                    }
+                },
+                {
+                    "Sid": "ViewConsole",
+                    "Effect": "Allow",
+                    "Action": [
+                        "aws-portal:ViewAccount",
+                        "account:ListRegions"
+                    ],
+                    "Resource": ""
+                }
+            ]
+        },
+        "error_message": 'Resource  must be in ARN format or "*".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s:3:ListBucket",
+                    "Resource": "sdfsadf"
+                }
+        },
+        "error_message": 'Resource sdfsadf must be in ARN format or "*".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": ["adf"]
+            }
+        },
+        "error_message": 'Resource adf must be in ARN format or "*".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": ""
+            }
+        },
+        "error_message": 'Resource  must be in ARN format or "*".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "NotAction": "s3s:ListBucket",
+                    "Resource": "a:bsdfdsafsad"
+                }
+        },
+        "error_message": 'Partition "bsdfdsafsad" is not valid for resource "arn:bsdfdsafsad:*:*:*:*".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "NotAction": "s3s:ListBucket",
+                    "Resource": "a:b:cadfsdf"
+                }
+        },
+        "error_message": 'Partition "b" is not valid for resource "arn:b:cadfsdf:*:*:*".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "NotAction": "s3s:ListBucket",
+                    "Resource": "a:b:c:d:e:f:g:h"
+                }
+        },
+        "error_message": 'Partition "b" is not valid for resource "arn:b:c:d:e:f:g:h".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Partition "s3" is not valid for resource "arn:s3:::example_bucket:*".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": [
+                    "arn:error:s3:::example_bucket",
+                    "arn:error:s3::example_bucket"
+                ]
+            }
+        },
+        "error_message": 'Partition "error" is not valid for resource "arn:error:s3:::example_bucket".'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": []
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket"
+            }
+        },
+        "error_message": 'Policy statement must contain resources.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": []
+            }
+        },
+        "error_message": 'Policy statement must contain resources.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "invalid"
+            }
+        },
+        "error_message": 'Policy statement must contain resources.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Policy statement must contain actions.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow"
+            }
+        },
+        "error_message": 'Policy statement must contain actions.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": [],
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+        },
+        "error_message": 'Policy statement must contain actions.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Effect": "Deny"
+                },
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+            ]
+        },
+        "error_message": 'Policy statement must contain actions.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:iam:::example_bucket"
+                }
+        },
+        "error_message": 'IAM resource path must either be "*" or start with user/, federated-user/, role/, group/, instance-profile/, mfa/, server-certificate/, policy/, sms-mfa/, saml-provider/, oidc-provider/, report/, access-report/.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3::example_bucket"
+                }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Resource": "arn:aws:s3::example_bucket"
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws"
+                }
+        },
+        "error_message": 'Resource vendor must be fully qualified and cannot contain regexes.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": {
+                        "a": "arn:aws:s3:::example_bucket"
+                    }
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Deny",
+                "Action": "s3:ListBucket",
+                "Resource": ["adfdf", {}]
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "NotAction": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "NotResource": []
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Deny",
+                "Action": [[]],
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "NotAction": "s3s:ListBucket",
+                    "Action": [],
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": {},
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": []
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": "a"
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": {
+                        "a": "b"
+                    }
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": {
+                        "DateGreaterThan": "b"
+                    }
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": {
+                        "DateGreaterThan": []
+                    }
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": {
+                        "DateGreaterThan": {"a": {}}
+                    }
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": {
+                        "DateGreaterThan": {"a": {}}
+                    }
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "x": {
+                        "a": "1"
+                    }
+                }
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "ForAnyValue::StringEqualsIfExists": {
+                        "a": "asf"
+                    }
+                }
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": [
+                        {"ForAllValues:StringEquals": {"aws:TagKeys": "Department"}}
+                    ]
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:iam:us-east-1::example_bucket"
+                }
+        },
+        "error_message": 'IAM resource arn:aws:iam:us-east-1::example_bucket cannot contain region information.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:us-east-1::example_bucket"
+                }
+        },
+        "error_message": 'Resource arn:aws:s3:us-east-1::example_bucket can not contain region information.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Sid": {},
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Sid": [],
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Sid": "sdf",
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                },
+                {
+                    "Sid": "sdf",
+                    "Effect": "Allow"
+                }
+            ]
+        },
+        "error_message": 'Statement IDs (SID) in a single policy must be unique.'
+    },
+    {
+        "document": {
+            "Statement": [
+                {
+                    "Sid": "sdf",
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                },
+                {
+                    "Sid": "sdf",
+                    "Effect": "Allow"
+                }
+            ]
+        },
+        "error_message": 'Policy document must be version 2012-10-17 or greater.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "NotAction": "s3:ListBucket",
+                "Action": "iam:dsf",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "NotResource": "*"
+            }
+        },
+        "error_message": 'Syntax errors in policy.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "denY",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": {
+                        "DateGreaterThan": {"a": "sdfdsf"}
+                    }
+                }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket",
+                    "Condition": {
+                        "DateGreaterThan": {"a": "sdfdsf"}
+                    }
+                }
+        },
+        "error_message": 'Policy document must be version 2012-10-17 or greater.'
+    },
+    {
+        "document": {
+            "Statement": {
+                "Effect": "denY",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        },
+        "error_message": 'Policy document must be version 2012-10-17 or greater.'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "Condition": {
+                        "DateGreaterThan": {"a": "sdfdsf"}
+                    }
+                }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "Allow",
+                    "NotAction": "s3:ListBucket",
+                    "Resource": "arn:aws::::example_bucket"
+                }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement":
+                {
+                    "Effect": "allow",
+                    "Resource": "arn:aws:s3:us-east-1::example_bucket"
+                }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Sid": "sdf",
+                    "Effect": "aLLow",
+                    "Action": "s3:ListBucket",
+                    "Resource": "arn:aws:s3:::example_bucket"
+                },
+                {
+                    "Sid": "sdf",
+                    "Effect": "Allow"
+                }
+            ]
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "NotResource": "arn:aws:s3::example_bucket"
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "DateLessThanEquals": {
+                        "a": "234-13"
+                    }
+                }
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "DateLessThanEquals": {
+                        "a": "2016-12-13t2:00:00.593194+1"
+                    }
+                }
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "DateLessThanEquals": {
+                        "a": "2016-12-13t2:00:00.1999999999+10:59"
+                    }
+                }
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "DateLessThan": {
+                        "a": "9223372036854775808"
+                    }
+                }
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:error:s3:::example_bucket",
+                "Condition": {
+                    "DateGreaterThan": {
+                        "a": "sdfdsf"
+                    }
+                }
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    },
+    {
+        "document": {
+            "Version": "2012-10-17",
+            "Statement": {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws::fdsasf"
+            }
+        },
+        "error_message": 'The policy failed legacy parsing'
+    }
+]
+
+valid_policy_documents = [
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": [
+                "arn:aws:s3:::example_bucket"
+            ]
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "iam: asdf safdsf af ",
+            "Resource": "arn:aws:s3:::example_bucket"
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": [
+                "arn:aws:s3:::example_bucket",
+                "*"
+            ]
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "*",
+            "Resource": "arn:aws:s3:::example_bucket"
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "service-prefix:action-name",
+            "Resource": "*",
+            "Condition": {
+                "DateGreaterThan": {"aws:CurrentTime": "2017-07-01T00:00:00Z"},
+                "DateLessThan": {"aws:CurrentTime": "2017-12-31T23:59:59Z"}
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "fsx:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:iam:::user/example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s33:::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:fdsasf"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {}
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {"ForAllValues:StringEquals": {"aws:TagKeys": "Department"}}
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:cloudwatch:us-east-1::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:ec2:us-east-1::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:invalid-service:::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:invalid-service:us-east-1::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "DateGreaterThan": {"aws:CurrentTime": "2017-07-01T00:00:00Z"},
+                    "DateLessThan": {"aws:CurrentTime": "2017-12-31T23:59:59Z"}
+                }
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "DateGreaterThan": {}
+                }
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "DateGreaterThan": {"a": []}
+                }
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket",
+                "Condition": {
+                    "a": {}
+                }
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Sid": "dsfsdfsdfsdfsdfsadfsd",
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "ConsoleDisplay",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:GetRole",
+                    "iam:GetUser",
+                    "iam:ListRoles",
+                    "iam:ListRoleTags",
+                    "iam:ListUsers",
+                    "iam:ListUserTags"
+                ],
+                "Resource": "*"
+            },
+            {
+                "Sid": "AddTag",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:TagUser",
+                    "iam:TagRole"
+                ],
+                "Resource": "*",
+                "Condition": {
+                    "StringEquals": {
+                        "aws:RequestTag/CostCenter": [
+                            "A-123",
+                            "B-456"
+                        ]
+                    },
+                    "ForAllValues:StringEquals": {"aws:TagKeys": "CostCenter"}
+                }
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "NotAction": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket"
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Deny",
+            "Action": "s3:*",
+            "NotResource": [
+                "arn:aws:s3:::HRBucket/Payroll",
+                "arn:aws:s3:::HRBucket/Payroll/*"
+            ]
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Id": "sdfsdfsdf",
+        "Statement": {
+            "Effect": "Allow",
+            "NotAction": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket"
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "aaaaaadsfdsafsadfsadfaaaaa:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3-s:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "Action": "s3.s:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement":
+            {
+                "Effect": "Allow",
+                "NotAction": "s3:ListBucket",
+                "NotResource": "*"
+            }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "sdf",
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            },
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::example_bucket"
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket",
+            "Condition": {
+                "DateGreaterThan": {
+                    "a": "01T"
+                }
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket",
+            "Condition": {
+                "x": {
+                },
+                "y": {}
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket",
+            "Condition": {
+                "StringEqualsIfExists": {
+                    "a": "asf"
+                }
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket",
+            "Condition": {
+                "ForAnyValue:StringEqualsIfExists": {
+                    "a": "asf"
+                }
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket",
+            "Condition": {
+                "DateLessThanEquals": {
+                    "a": "2019-07-01T13:20:15Z"
+                }
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket",
+            "Condition": {
+                "DateLessThanEquals": {
+                    "a": "2016-12-13T21:20:37.593194+00:00"
+                }
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket",
+            "Condition": {
+                "DateLessThanEquals": {
+                    "a": "2016-12-13t2:00:00.593194+23"
+                }
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": {
+            "Effect": "Allow",
+            "Action": "s3:ListBucket",
+            "Resource": "arn:aws:s3:::example_bucket",
+            "Condition": {
+                "DateLessThan": {
+                    "a": "-292275054"
+                }
+            }
+        }
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "AllowViewAccountInfo",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:GetAccountPasswordPolicy",
+                    "iam:GetAccountSummary",
+                    "iam:ListVirtualMFADevices"
+                ],
+                "Resource": "*"
+            },
+            {
+                "Sid": "AllowManageOwnPasswords",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:ChangePassword",
+                    "iam:GetUser"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnAccessKeys",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:CreateAccessKey",
+                    "iam:DeleteAccessKey",
+                    "iam:ListAccessKeys",
+                    "iam:UpdateAccessKey"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnSigningCertificates",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:DeleteSigningCertificate",
+                    "iam:ListSigningCertificates",
+                    "iam:UpdateSigningCertificate",
+                    "iam:UploadSigningCertificate"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnSSHPublicKeys",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:DeleteSSHPublicKey",
+                    "iam:GetSSHPublicKey",
+                    "iam:ListSSHPublicKeys",
+                    "iam:UpdateSSHPublicKey",
+                    "iam:UploadSSHPublicKey"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnGitCredentials",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:CreateServiceSpecificCredential",
+                    "iam:DeleteServiceSpecificCredential",
+                    "iam:ListServiceSpecificCredentials",
+                    "iam:ResetServiceSpecificCredential",
+                    "iam:UpdateServiceSpecificCredential"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnVirtualMFADevice",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:CreateVirtualMFADevice",
+                    "iam:DeleteVirtualMFADevice"
+                ],
+                "Resource": "arn:aws:iam::*:mfa/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnUserMFA",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:DeactivateMFADevice",
+                    "iam:EnableMFADevice",
+                    "iam:ListMFADevices",
+                    "iam:ResyncMFADevice"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "DenyAllExceptListedIfNoMFA",
+                "Effect": "Deny",
+                "NotAction": [
+                    "iam:CreateVirtualMFADevice",
+                    "iam:EnableMFADevice",
+                    "iam:GetUser",
+                    "iam:ListMFADevices",
+                    "iam:ListVirtualMFADevices",
+                    "iam:ResyncMFADevice",
+                    "sts:GetSessionToken"
+                ],
+                "Resource": "*",
+                "Condition": {
+                    "BoolIfExists": {
+                        "aws:MultiFactorAuthPresent": "false"
+                    }
+                }
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "ListAndDescribe",
+                "Effect": "Allow",
+                "Action": [
+                    "dynamodb:List*",
+                    "dynamodb:DescribeReservedCapacity*",
+                    "dynamodb:DescribeLimits",
+                    "dynamodb:DescribeTimeToLive"
+                ],
+                "Resource": "*"
+            },
+            {
+                "Sid": "SpecificTable",
+                "Effect": "Allow",
+                "Action": [
+                    "dynamodb:BatchGet*",
+                    "dynamodb:DescribeStream",
+                    "dynamodb:DescribeTable",
+                    "dynamodb:Get*",
+                    "dynamodb:Query",
+                    "dynamodb:Scan",
+                    "dynamodb:BatchWrite*",
+                    "dynamodb:CreateTable",
+                    "dynamodb:Delete*",
+                    "dynamodb:Update*",
+                    "dynamodb:PutItem"
+                ],
+                "Resource": "arn:aws:dynamodb:*:*:table/MyTable"
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": [
+                    "ec2:AttachVolume",
+                    "ec2:DetachVolume"
+                ],
+                "Resource": [
+                    "arn:aws:ec2:*:*:volume/*",
+                    "arn:aws:ec2:*:*:instance/*"
+                ],
+                "Condition": {
+                    "ArnEquals": {"ec2:SourceInstanceARN": "arn:aws:ec2:*:*:instance/instance-id"}
+                }
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": [
+                    "ec2:AttachVolume",
+                    "ec2:DetachVolume"
+                ],
+                "Resource": "arn:aws:ec2:*:*:instance/*",
+                "Condition": {
+                    "StringEquals": {"ec2:ResourceTag/Department": "Development"}
+                }
+            },
+            {
+                "Effect": "Allow",
+                "Action": [
+                    "ec2:AttachVolume",
+                    "ec2:DetachVolume"
+                ],
+                "Resource": "arn:aws:ec2:*:*:volume/*",
+                "Condition": {
+                    "StringEquals": {"ec2:ResourceTag/VolumeUser": "${aws:username}"}
+                }
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "StartStopIfTags",
+                "Effect": "Allow",
+                "Action": [
+                    "ec2:StartInstances",
+                    "ec2:StopInstances",
+                    "ec2:DescribeTags"
+                ],
+                "Resource": "arn:aws:ec2:region:account-id:instance/*",
+                "Condition": {
+                    "StringEquals": {
+                        "ec2:ResourceTag/Project": "DataAnalytics",
+                        "aws:PrincipalTag/Department": "Data"
+                    }
+                }
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "ListYourObjects",
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": ["arn:aws:s3:::bucket-name"],
+                "Condition": {
+                    "StringLike": {
+                        "s3:prefix": ["cognito/application-name/${cognito-identity.amazonaws.com:sub}"]
+                    }
+                }
+            },
+            {
+                "Sid": "ReadWriteDeleteYourObjects",
+                "Effect": "Allow",
+                "Action": [
+                    "s3:GetObject",
+                    "s3:PutObject",
+                    "s3:DeleteObject"
+                ],
+                "Resource": [
+                    "arn:aws:s3:::bucket-name/cognito/application-name/${cognito-identity.amazonaws.com:sub}",
+                    "arn:aws:s3:::bucket-name/cognito/application-name/${cognito-identity.amazonaws.com:sub}/*"
+                ]
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": [
+                    "s3:ListAllMyBuckets",
+                    "s3:GetBucketLocation"
+                ],
+                "Resource": "*"
+            },
+            {
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": "arn:aws:s3:::bucket-name",
+                "Condition": {
+                    "StringLike": {
+                        "s3:prefix": [
+                            "",
+                            "home/",
+                            "home/${aws:userid}/*"
+                        ]
+                    }
+                }
+            },
+            {
+                "Effect": "Allow",
+                "Action": "s3:*",
+                "Resource": [
+                    "arn:aws:s3:::bucket-name/home/${aws:userid}",
+                    "arn:aws:s3:::bucket-name/home/${aws:userid}/*"
+                ]
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "ConsoleAccess",
+                "Effect": "Allow",
+                "Action": [
+                    "s3:GetAccountPublicAccessBlock",
+                    "s3:GetBucketAcl",
+                    "s3:GetBucketLocation",
+                    "s3:GetBucketPolicyStatus",
+                    "s3:GetBucketPublicAccessBlock",
+                    "s3:ListAllMyBuckets"
+                ],
+                "Resource": "*"
+            },
+            {
+                "Sid": "ListObjectsInBucket",
+                "Effect": "Allow",
+                "Action": "s3:ListBucket",
+                "Resource": ["arn:aws:s3:::bucket-name"]
+            },
+            {
+                "Sid": "AllObjectActions",
+                "Effect": "Allow",
+                "Action": "s3:*Object",
+                "Resource": ["arn:aws:s3:::bucket-name/*"]
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "AllowViewAccountInfo",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:GetAccountPasswordPolicy",
+                    "iam:GetAccountSummary"
+                ],
+                "Resource": "*"
+            },
+            {
+                "Sid": "AllowManageOwnPasswords",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:ChangePassword",
+                    "iam:GetUser"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnAccessKeys",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:CreateAccessKey",
+                    "iam:DeleteAccessKey",
+                    "iam:ListAccessKeys",
+                    "iam:UpdateAccessKey"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnSigningCertificates",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:DeleteSigningCertificate",
+                    "iam:ListSigningCertificates",
+                    "iam:UpdateSigningCertificate",
+                    "iam:UploadSigningCertificate"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnSSHPublicKeys",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:DeleteSSHPublicKey",
+                    "iam:GetSSHPublicKey",
+                    "iam:ListSSHPublicKeys",
+                    "iam:UpdateSSHPublicKey",
+                    "iam:UploadSSHPublicKey"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            },
+            {
+                "Sid": "AllowManageOwnGitCredentials",
+                "Effect": "Allow",
+                "Action": [
+                    "iam:CreateServiceSpecificCredential",
+                    "iam:DeleteServiceSpecificCredential",
+                    "iam:ListServiceSpecificCredentials",
+                    "iam:ResetServiceSpecificCredential",
+                    "iam:UpdateServiceSpecificCredential"
+                ],
+                "Resource": "arn:aws:iam::*:user/${aws:username}"
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Action": "ec2:*",
+                "Resource": "*",
+                "Effect": "Allow",
+                "Condition": {
+                    "StringEquals": {
+                        "ec2:Region": "region"
+                    }
+                }
+            }
+        ]
+    },
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": "rds:*",
+                "Resource": ["arn:aws:rds:region:*:*"]
+            },
+            {
+                "Effect": "Allow",
+                "Action": ["rds:Describe*"],
+                "Resource": ["*"]
+            }
+        ]
+    }
+]
+
+
+def test_create_policy_with_invalid_policy_documents():
+    for test_case in invalid_policy_document_test_cases:
+        yield check_create_policy_with_invalid_policy_document, test_case
+
+
+def test_create_policy_with_valid_policy_documents():
+    for valid_policy_document in valid_policy_documents:
+        yield check_create_policy_with_valid_policy_document, valid_policy_document
+
+
+@mock_iam
+def check_create_policy_with_invalid_policy_document(test_case):
+    conn = boto3.client('iam', region_name='us-east-1')
+    with assert_raises(ClientError) as ex:
+        conn.create_policy(
+            PolicyName="TestCreatePolicy",
+            PolicyDocument=json.dumps(test_case["document"]))
+    ex.exception.response['Error']['Code'].should.equal('MalformedPolicyDocument')
+    ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400)
+    ex.exception.response['Error']['Message'].should.equal(test_case["error_message"])
+
+
+@mock_iam
+def check_create_policy_with_valid_policy_document(valid_policy_document):
+    conn = boto3.client('iam', region_name='us-east-1')
+    conn.create_policy(
+        PolicyName="TestCreatePolicy",
+        PolicyDocument=json.dumps(valid_policy_document))