prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  IAM ManagedPolicy PolicyDocument Character limit of 6144 should not include whitespaces
  *cfn-lint version: (`cfn-lint --version`)* 0.48.2
  
  *Description of issue.*
  There is a 6,144 character limit on IAM managed policies but this does/should not include whitespaces ([source](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html#reference_iam-limits-entity-length)). cfn-lint flagged a PolicyDocument of mine as too long, while Cloudformation successfully deploys it. This character limit check would be very helpful if it checked against the same 6,144 character limit that AWS does.
  
  The PolicyDocument is defined in CFN in JSON. I can copy-and-paste this JSON into the AWS Policy Editor and it tells me there are only 4,189 characters in it. I can successfully deploy the CFN stack with this same policy. Counting whitespace, there are 9,849 characters so it seems highly likely that cfn-lint is counting whitespace when it should not be. 
  
  cfn-lint complaint:
  ```E3502 Role trust policy JSON text cannot be longer than 6144 characters```
  
  Please provide as much information as possible:
  * Template linting issues:
    * Please provide a CloudFormation sample that generated the issue.
    * If present, please add links to the (official) documentation for clarification.
    * Validate if the issue still exists with the latest version of `cfn-lint` and/or the latest Spec files
  * Feature request:
    * Please provide argumentation about the missing feature. Context is key!
  
  
  Cfn-lint uses the [CloudFormation Resource Specifications](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-resource-specification.html) as the base to do validation. These files are included as part of the application version. Please update to the latest version of `cfn-lint` or update the spec files manually (`cfn-lint -u`)
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/cfnlint/rules/resources/properties/JsonSize.py`
  Content:
  ```
  1 """
  2 Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3 SPDX-License-Identifier: MIT-0
  4 """
  5 import datetime
  6 import json
  7 import re
  8 import six
  9 import cfnlint.helpers
  10 from cfnlint.rules import CloudFormationLintRule
  11 from cfnlint.rules import RuleMatch
  12 from cfnlint.helpers import RESOURCE_SPECS
  13 
  14 
  15 class JsonSize(CloudFormationLintRule):
  16     """Check if JSON Object Size is within the specified length"""
  17     id = 'E3502'
  18     shortdesc = 'Check if a JSON Object is within size limits'
  19     description = 'Validate properties that are JSON values so that their length is within the limits'
  20     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html'
  21     tags = ['resources', 'limits', 'json']
  22 
  23     def initialize(self, cfn):
  24         """Initialize the rule"""
  25         for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):
  26             self.resource_property_types.append(resource_type_spec)
  27         for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):
  28             self.resource_sub_property_types.append(property_type_spec)
  29 
  30     def _serialize_date(self, obj):
  31         if isinstance(obj, datetime.date):
  32             return obj.isoformat()
  33         raise TypeError('Object of type {} is not JSON serializable'.format(obj.__class__.__name__))
  34 
  35     def check_value(self, value, path, prop, cfn, specs):
  36         """Check Role.AssumeRolePolicyDocument is within limits"""
  37         matches = []
  38 
  39         def remove_functions(obj):
  40             """ Replaces intrinsic functions with string """
  41             if isinstance(obj, dict):
  42                 new_obj = {}
  43                 if len(obj) == 1:
  44                     for k, v in obj.items():
  45                         if k in cfnlint.helpers.FUNCTIONS:
  46                             if k == 'Fn::Sub':
  47                                 if isinstance(v, six.string_types):
  48                                     return re.sub(r'\${.*}', '', v)
  49                                 if isinstance(v, list):
  50                                     return re.sub(r'\${.*}', '', v[0])
  51                         else:
  52                             new_obj[k] = remove_functions(v)
  53                             return new_obj
  54                 else:
  55                     for k, v in obj.items():
  56                         new_obj[k] = remove_functions(v)
  57                     return new_obj
  58             elif isinstance(obj, list):
  59                 new_list = []
  60                 for v in obj:
  61                     new_list.append(remove_functions(v))
  62                 return new_list
  63 
  64             return obj
  65 
  66         scenarios = cfn.get_object_without_nested_conditions(value, path)
  67         json_max_size = specs.get('JsonMax')
  68         for scenario in scenarios:
  69             if len(json.dumps(remove_functions(scenario['Object'][prop]), separators=(',', ':'), default=self._serialize_date)) > json_max_size:
  70                 if scenario['Scenario']:
  71                     message = 'Role trust policy JSON text cannot be longer than {0} characters when {1}'
  72                     scenario_text = ' and '.join(['when condition "%s" is %s' % (
  73                         k, v) for (k, v) in scenario['Scenario'].items()])
  74                     matches.append(
  75                         RuleMatch(path + [prop], message.format(json_max_size, scenario_text)))
  76                 else:
  77                     message = 'Role trust policy JSON text cannot be longer than {0} characters'
  78                     matches.append(
  79                         RuleMatch(
  80                             path + [prop],
  81                             message.format(json_max_size),
  82                         )
  83                     )
  84 
  85         return matches
  86 
  87     def check(self, cfn, properties, specs, path):
  88         """Check itself"""
  89         matches = []
  90         for p_value, p_path in properties.items_safe(path[:]):
  91             for prop in p_value:
  92                 if prop in specs:
  93                     value = specs.get(prop).get('Value', {})
  94                     if value:
  95                         value_type = value.get('ValueType', '')
  96                         primitive_type = specs.get(prop).get('PrimitiveType')
  97                         if primitive_type == 'Json':
  98                             matches.extend(
  99                                 self.check_value(
  100                                     p_value, p_path, prop, cfn,
  101                                     RESOURCE_SPECS.get(cfn.regions[0]).get(
  102                                         'ValueTypes').get(value_type, {})
  103                                 )
  104                             )
  105         return matches
  106 
  107     def match_resource_sub_properties(self, properties, property_type, path, cfn):
  108         """Match for sub properties"""
  109         matches = list()
  110 
  111         specs = RESOURCE_SPECS.get(cfn.regions[0]).get(
  112             'PropertyTypes').get(property_type, {}).get('Properties', {})
  113         matches.extend(self.check(cfn, properties, specs, path))
  114 
  115         return matches
  116 
  117     def match_resource_properties(self, properties, resource_type, path, cfn):
  118         """Check CloudFormation Properties"""
  119         matches = list()
  120 
  121         specs = RESOURCE_SPECS.get(cfn.regions[0]).get(
  122             'ResourceTypes').get(resource_type, {}).get('Properties', {})
  123         matches.extend(self.check(cfn, properties, specs, path))
  124 
  125         return matches
  126
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/cfnlint/rules/resources/properties/JsonSize.py b/src/cfnlint/rules/resources/properties/JsonSize.py
  --- a/src/cfnlint/rules/resources/properties/JsonSize.py
  +++ b/src/cfnlint/rules/resources/properties/JsonSize.py
  @@ -66,19 +66,25 @@
           scenarios = cfn.get_object_without_nested_conditions(value, path)
           json_max_size = specs.get('JsonMax')
           for scenario in scenarios:
  -            if len(json.dumps(remove_functions(scenario['Object'][prop]), separators=(',', ':'), default=self._serialize_date)) > json_max_size:
  +            j = remove_functions(scenario['Object'][prop])
  +            if isinstance(j, six.string_types):
  +                try:
  +                    j = json.loads(j)
  +                except:  #pylint: disable=bare-except
  +                    continue
  +            if len(json.dumps(j, separators=(',', ':'), default=self._serialize_date)) > json_max_size:
                   if scenario['Scenario']:
  -                    message = 'Role trust policy JSON text cannot be longer than {0} characters when {1}'
  +                    message = '{0} JSON text cannot be longer than {1} characters when {2}'
                       scenario_text = ' and '.join(['when condition "%s" is %s' % (
                           k, v) for (k, v) in scenario['Scenario'].items()])
                       matches.append(
  -                        RuleMatch(path + [prop], message.format(json_max_size, scenario_text)))
  +                        RuleMatch(path + [prop], message.format(prop, json_max_size, scenario_text)))
                   else:
  -                    message = 'Role trust policy JSON text cannot be longer than {0} characters'
  +                    message = '{0} JSON text cannot be longer than {1} characters'
                       matches.append(
                           RuleMatch(
                               path + [prop],
  -                            message.format(json_max_size),
  +                            message.format(prop, json_max_size),
                           )
                       )
  
verification_info: {"golden_diff": "diff --git a/src/cfnlint/rules/resources/properties/JsonSize.py b/src/cfnlint/rules/resources/properties/JsonSize.py\n--- a/src/cfnlint/rules/resources/properties/JsonSize.py\n+++ b/src/cfnlint/rules/resources/properties/JsonSize.py\n@@ -66,19 +66,25 @@\n         scenarios = cfn.get_object_without_nested_conditions(value, path)\n         json_max_size = specs.get('JsonMax')\n         for scenario in scenarios:\n-            if len(json.dumps(remove_functions(scenario['Object'][prop]), separators=(',', ':'), default=self._serialize_date)) > json_max_size:\n+            j = remove_functions(scenario['Object'][prop])\n+            if isinstance(j, six.string_types):\n+                try:\n+                    j = json.loads(j)\n+                except:  #pylint: disable=bare-except\n+                    continue\n+            if len(json.dumps(j, separators=(',', ':'), default=self._serialize_date)) > json_max_size:\n                 if scenario['Scenario']:\n-                    message = 'Role trust policy JSON text cannot be longer than {0} characters when {1}'\n+                    message = '{0} JSON text cannot be longer than {1} characters when {2}'\n                     scenario_text = ' and '.join(['when condition \"%s\" is %s' % (\n                         k, v) for (k, v) in scenario['Scenario'].items()])\n                     matches.append(\n-                        RuleMatch(path + [prop], message.format(json_max_size, scenario_text)))\n+                        RuleMatch(path + [prop], message.format(prop, json_max_size, scenario_text)))\n                 else:\n-                    message = 'Role trust policy JSON text cannot be longer than {0} characters'\n+                    message = '{0} JSON text cannot be longer than {1} characters'\n                     matches.append(\n                         RuleMatch(\n                             path + [prop],\n-                            message.format(json_max_size),\n+                            message.format(prop, json_max_size),\n                         )\n                     )\n", "issue": "IAM ManagedPolicy PolicyDocument Character limit of 6144 should not include whitespaces\n*cfn-lint version: (`cfn-lint --version`)* 0.48.2\r\n\r\n*Description of issue.*\r\nThere is a 6,144 character limit on IAM managed policies but this does/should not include whitespaces ([source](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html#reference_iam-limits-entity-length)). cfn-lint flagged a PolicyDocument of mine as too long, while Cloudformation successfully deploys it. This character limit check would be very helpful if it checked against the same 6,144 character limit that AWS does.\r\n\r\nThe PolicyDocument is defined in CFN in JSON. I can copy-and-paste this JSON into the AWS Policy Editor and it tells me there are only 4,189 characters in it. I can successfully deploy the CFN stack with this same policy. Counting whitespace, there are 9,849 characters so it seems highly likely that cfn-lint is counting whitespace when it should not be. \r\n\r\ncfn-lint complaint:\r\n```E3502 Role trust policy JSON text cannot be longer than 6144 characters```\r\n\r\nPlease provide as much information as possible:\r\n* Template linting issues:\r\n  * Please provide a CloudFormation sample that generated the issue.\r\n  * If present, please add links to the (official) documentation for clarification.\r\n  * Validate if the issue still exists with the latest version of `cfn-lint` and/or the latest Spec files\r\n* Feature request:\r\n  * Please provide argumentation about the missing feature. Context is key!\r\n\r\n\r\nCfn-lint uses the [CloudFormation Resource Specifications](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-resource-specification.html) as the base to do validation. These files are included as part of the application version. Please update to the latest version of `cfn-lint` or update the spec files manually (`cfn-lint -u`)\r\n\n", "before_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport datetime\nimport json\nimport re\nimport six\nimport cfnlint.helpers\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\nfrom cfnlint.helpers import RESOURCE_SPECS\n\n\nclass JsonSize(CloudFormationLintRule):\n    \"\"\"Check if JSON Object Size is within the specified length\"\"\"\n    id = 'E3502'\n    shortdesc = 'Check if a JSON Object is within size limits'\n    description = 'Validate properties that are JSON values so that their length is within the limits'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html'\n    tags = ['resources', 'limits', 'json']\n\n    def initialize(self, cfn):\n        \"\"\"Initialize the rule\"\"\"\n        for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):\n            self.resource_property_types.append(resource_type_spec)\n        for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):\n            self.resource_sub_property_types.append(property_type_spec)\n\n    def _serialize_date(self, obj):\n        if isinstance(obj, datetime.date):\n            return obj.isoformat()\n        raise TypeError('Object of type {} is not JSON serializable'.format(obj.__class__.__name__))\n\n    def check_value(self, value, path, prop, cfn, specs):\n        \"\"\"Check Role.AssumeRolePolicyDocument is within limits\"\"\"\n        matches = []\n\n        def remove_functions(obj):\n            \"\"\" Replaces intrinsic functions with string \"\"\"\n            if isinstance(obj, dict):\n                new_obj = {}\n                if len(obj) == 1:\n                    for k, v in obj.items():\n                        if k in cfnlint.helpers.FUNCTIONS:\n                            if k == 'Fn::Sub':\n                                if isinstance(v, six.string_types):\n                                    return re.sub(r'\\${.*}', '', v)\n                                if isinstance(v, list):\n                                    return re.sub(r'\\${.*}', '', v[0])\n                        else:\n                            new_obj[k] = remove_functions(v)\n                            return new_obj\n                else:\n                    for k, v in obj.items():\n                        new_obj[k] = remove_functions(v)\n                    return new_obj\n            elif isinstance(obj, list):\n                new_list = []\n                for v in obj:\n                    new_list.append(remove_functions(v))\n                return new_list\n\n            return obj\n\n        scenarios = cfn.get_object_without_nested_conditions(value, path)\n        json_max_size = specs.get('JsonMax')\n        for scenario in scenarios:\n            if len(json.dumps(remove_functions(scenario['Object'][prop]), separators=(',', ':'), default=self._serialize_date)) > json_max_size:\n                if scenario['Scenario']:\n                    message = 'Role trust policy JSON text cannot be longer than {0} characters when {1}'\n                    scenario_text = ' and '.join(['when condition \"%s\" is %s' % (\n                        k, v) for (k, v) in scenario['Scenario'].items()])\n                    matches.append(\n                        RuleMatch(path + [prop], message.format(json_max_size, scenario_text)))\n                else:\n                    message = 'Role trust policy JSON text cannot be longer than {0} characters'\n                    matches.append(\n                        RuleMatch(\n                            path + [prop],\n                            message.format(json_max_size),\n                        )\n                    )\n\n        return matches\n\n    def check(self, cfn, properties, specs, path):\n        \"\"\"Check itself\"\"\"\n        matches = []\n        for p_value, p_path in properties.items_safe(path[:]):\n            for prop in p_value:\n                if prop in specs:\n                    value = specs.get(prop).get('Value', {})\n                    if value:\n                        value_type = value.get('ValueType', '')\n                        primitive_type = specs.get(prop).get('PrimitiveType')\n                        if primitive_type == 'Json':\n                            matches.extend(\n                                self.check_value(\n                                    p_value, p_path, prop, cfn,\n                                    RESOURCE_SPECS.get(cfn.regions[0]).get(\n                                        'ValueTypes').get(value_type, {})\n                                )\n                            )\n        return matches\n\n    def match_resource_sub_properties(self, properties, property_type, path, cfn):\n        \"\"\"Match for sub properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'PropertyTypes').get(property_type, {}).get('Properties', {})\n        matches.extend(self.check(cfn, properties, specs, path))\n\n        return matches\n\n    def match_resource_properties(self, properties, resource_type, path, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'ResourceTypes').get(resource_type, {}).get('Properties', {})\n        matches.extend(self.check(cfn, properties, specs, path))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/properties/JsonSize.py"}], "after_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport datetime\nimport json\nimport re\nimport six\nimport cfnlint.helpers\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\nfrom cfnlint.helpers import RESOURCE_SPECS\n\n\nclass JsonSize(CloudFormationLintRule):\n    \"\"\"Check if JSON Object Size is within the specified length\"\"\"\n    id = 'E3502'\n    shortdesc = 'Check if a JSON Object is within size limits'\n    description = 'Validate properties that are JSON values so that their length is within the limits'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html'\n    tags = ['resources', 'limits', 'json']\n\n    def initialize(self, cfn):\n        \"\"\"Initialize the rule\"\"\"\n        for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):\n            self.resource_property_types.append(resource_type_spec)\n        for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):\n            self.resource_sub_property_types.append(property_type_spec)\n\n    def _serialize_date(self, obj):\n        if isinstance(obj, datetime.date):\n            return obj.isoformat()\n        raise TypeError('Object of type {} is not JSON serializable'.format(obj.__class__.__name__))\n\n    def check_value(self, value, path, prop, cfn, specs):\n        \"\"\"Check Role.AssumeRolePolicyDocument is within limits\"\"\"\n        matches = []\n\n        def remove_functions(obj):\n            \"\"\" Replaces intrinsic functions with string \"\"\"\n            if isinstance(obj, dict):\n                new_obj = {}\n                if len(obj) == 1:\n                    for k, v in obj.items():\n                        if k in cfnlint.helpers.FUNCTIONS:\n                            if k == 'Fn::Sub':\n                                if isinstance(v, six.string_types):\n                                    return re.sub(r'\\${.*}', '', v)\n                                if isinstance(v, list):\n                                    return re.sub(r'\\${.*}', '', v[0])\n                        else:\n                            new_obj[k] = remove_functions(v)\n                            return new_obj\n                else:\n                    for k, v in obj.items():\n                        new_obj[k] = remove_functions(v)\n                    return new_obj\n            elif isinstance(obj, list):\n                new_list = []\n                for v in obj:\n                    new_list.append(remove_functions(v))\n                return new_list\n\n            return obj\n\n        scenarios = cfn.get_object_without_nested_conditions(value, path)\n        json_max_size = specs.get('JsonMax')\n        for scenario in scenarios:\n            j = remove_functions(scenario['Object'][prop])\n            if isinstance(j, six.string_types):\n                try:\n                    j = json.loads(j)\n                except:  #pylint: disable=bare-except\n                    continue\n            if len(json.dumps(j, separators=(',', ':'), default=self._serialize_date)) > json_max_size:\n                if scenario['Scenario']:\n                    message = '{0} JSON text cannot be longer than {1} characters when {2}'\n                    scenario_text = ' and '.join(['when condition \"%s\" is %s' % (\n                        k, v) for (k, v) in scenario['Scenario'].items()])\n                    matches.append(\n                        RuleMatch(path + [prop], message.format(prop, json_max_size, scenario_text)))\n                else:\n                    message = '{0} JSON text cannot be longer than {1} characters'\n                    matches.append(\n                        RuleMatch(\n                            path + [prop],\n                            message.format(prop, json_max_size),\n                        )\n                    )\n\n        return matches\n\n    def check(self, cfn, properties, specs, path):\n        \"\"\"Check itself\"\"\"\n        matches = []\n        for p_value, p_path in properties.items_safe(path[:]):\n            for prop in p_value:\n                if prop in specs:\n                    value = specs.get(prop).get('Value', {})\n                    if value:\n                        value_type = value.get('ValueType', '')\n                        primitive_type = specs.get(prop).get('PrimitiveType')\n                        if primitive_type == 'Json':\n                            matches.extend(\n                                self.check_value(\n                                    p_value, p_path, prop, cfn,\n                                    RESOURCE_SPECS.get(cfn.regions[0]).get(\n                                        'ValueTypes').get(value_type, {})\n                                )\n                            )\n        return matches\n\n    def match_resource_sub_properties(self, properties, property_type, path, cfn):\n        \"\"\"Match for sub properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'PropertyTypes').get(property_type, {}).get('Properties', {})\n        matches.extend(self.check(cfn, properties, specs, path))\n\n        return matches\n\n    def match_resource_properties(self, properties, resource_type, path, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'ResourceTypes').get(resource_type, {}).get('Properties', {})\n        matches.extend(self.check(cfn, properties, specs, path))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/properties/JsonSize.py"}]}