Repository: aws-cloudformation/cfn-lint

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`)



Discussion:
Thanks for reporting this.  I'll take a look.
@tcdachille do you have an example policy I can look at? We are removing white space when doing the analytics?
https://github.com/aws-cloudformation/cfn-python-lint/blob/853522ff0e4c977331ac677a47059cf96b6f3189/src/cfnlint/rules/resources/properties/JsonSize.py#L69

I built one that I'm measuring at 6155 characters and checked it on the policy analyzer which also said it was to large.  I feel like we are comparing correctly here but I may be missing something.  Do you have any CloudFormation functions inside your policy?
An example that's not any more complicated than what I'm doing would be (mostly the first few AWS managed policies strung together) the following- 
cfn-lint prints the same `E3502 Role trust policy JSON text cannot be longer than 6144 characters` error for this, but pasted into AWS Policy editor, it says it's 6,118 characters.
I don't know for sure that whitespace is the issue, but obviously there is a discrepancy in how it's being counted

```
---
AWSTemplateFormatVersion: 2010-09-09

Resources:
  ExamplePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: 'example-policy'
      PolicyDocument: |
           {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Action": [
                            "ec2:DescribeAddresses",
                            "ec2:DescribeByoipCidrs",
                            "ec2:DescribeVpcEndpoints",
                            "ec2:DescribeVpcs",
                            "iam:GetRole",
                            "iam:ListRoles",
                            "kms:DescribeKey",
                            "kms:GetKeyPolicy",
                            "kms:ListGrants",
                            "kms:ListKeyPolicies",
                            "kms:ListKeys",
                            "lambda:GetLayerVersionPolicy",
                            "lambda:GetPolicy",
                            "lambda:ListAliases",
                            "lambda:ListFunctions",
                            "lambda:ListLayers",
                            "lambda:ListLayerVersions",
                            "lambda:ListVersionsByFunction",
                            "organizations:DescribeAccount",
                            "organizations:DescribeOrganization",
                            "organizations:DescribeOrganizationalUnit",
                            "organizations:ListAccounts",
                            "organizations:ListAccountsForParent",
                            "organizations:ListAWSServiceAccessForOrganization",
                            "organizations:ListChildren",
                            "organizations:ListDelegatedAdministrators",
                            "organizations:ListOrganizationalUnitsForParent",
                            "organizations:ListParents",
                            "organizations:ListRoots",
                            "s3:GetAccessPoint",
                            "s3:GetAccessPointPolicy",
                            "s3:GetAccessPointPolicyStatus",
                            "s3:GetAccountPublicAccessBlock",
                            "s3:GetBucketAcl",
                            "s3:GetBucketLocation",
                            "s3:GetBucketPolicyStatus",
                            "s3:GetBucketPolicy",
                            "s3:GetBucketPublicAccessBlock",
                            "s3:ListAccessPoints",
                            "s3:ListAllMyBuckets",
                            "sns:GetTopicAttributes",
                            "sns:ListTopics",
                            "secretsmanager:DescribeSecret",
                            "secretsmanager:GetResourcePolicy",
                            "secretsmanager:ListSecrets",
                            "sqs:GetQueueAttributes",
                            "sqs:ListQueues"
                        ],
                        "Resource": [
                            "arn:aws:iam::123456789123:user/example_user1",
                            "arn:aws:iam::123456789123:user/example_user2",
                            "arn:aws:iam::123456789123:user/example_user3",
                            "arn:aws:iam::123456789123:user/example_user4",
                            "arn:aws:iam::123456789123:user/example_user5",
                            "arn:aws:iam::123456789123:user/example_user6",
                            "arn:aws:iam::123456789123:user/example_user7",
                            "arn:aws:iam::123456789123:user/example_user8"
                        ]
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "ssm:DescribeAssociation",
                            "ssm:GetDeployablePatchSnapshotForInstance",
                            "ssm:GetDocument",
                            "ssm:DescribeDocument",
                            "ssm:GetManifest",
                            "ssm:GetParameters",
                            "ssm:ListAssociations",
                            "ssm:ListInstanceAssociations",
                            "ssm:PutInventory",
                            "ssm:PutComplianceItems",
                            "ssm:PutConfigurePackageResult",
                            "ssm:UpdateAssociationStatus",
                            "ssm:UpdateInstanceAssociationStatus",
                            "ssm:UpdateInstanceInformation"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user1"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "ssmmessages:CreateControlChannel",
                            "ssmmessages:CreateDataChannel",
                            "ssmmessages:OpenControlChannel",
                            "ssmmessages:OpenDataChannel"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user2"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "ec2messages:AcknowledgeMessage",
                            "ec2messages:DeleteMessage",
                            "ec2messages:FailMessage",
                            "ec2messages:GetEndpoint",
                            "ec2messages:GetMessages",
                            "ec2messages:SendReply"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user3"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "cloudwatch:PutMetricData"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user4"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "ec2:DescribeInstanceStatus"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user5"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "ds:CreateComputer",
                            "ds:DescribeDirectories"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user6"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "logs:CreateLogGroup",
                            "logs:CreateLogStream",
                            "logs:DescribeLogGroups",
                            "logs:DescribeLogStreams",
                            "logs:PutLogEvents"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user7"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "s3:GetBucketLocation",
                            "s3:PutObject",
                            "s3:GetObject",
                            "s3:GetEncryptionConfiguration",
                            "s3:AbortMultipartUpload",
                            "s3:ListMultipartUploadParts",
                            "s3:ListBucket",
                            "s3:ListBucketMultipartUploads"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user8"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "a4b:RegisterDevice",
                            "a4b:CompleteRegistration",
                            "a4b:SearchDevices",
                            "a4b:SearchNetworkProfiles",
                            "a4b:GetNetworkProfile",
                            "a4b:PutDeviceSetupEvents"
                        ],
                        "Resource": "arn:aws:iam::123456789123:user/example_user9"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "chime:CreateMeeting",
                            "chime:CreateMeetingWithAttendees",
                            "chime:DeleteMeeting",
                            "chime:GetMeeting",
                            "chime:ListMeetings",
                            "chime:CreateAttendee",
                            "chime:BatchCreateAttendee",
                            "chime:DeleteAttendee",
                            "chime:GetAttendee",
                            "chime:ListAttendees",
                            "chime:ListAttendeeTags",
                            "chime:ListMeetingTags",
                            "chime:ListTagsForResource",
                            "chime:TagAttendee",
                            "chime:TagMeeting",
                            "chime:TagResource",
                            "chime:UntagAttendee",
                            "chime:UntagMeeting",
                            "chime:UntagResource"
                        ],
                        "Resource": [
                            "arn:aws:iam::123456789123:user/example_user1",
                            "arn:aws:iam::123456789123:user/example_user2",
                            "arn:aws:iam::123456789123:user/example_user3",
                            "arn:aws:iam::123456789123:user/example_user4",
                            "arn:aws:iam::123456789123:user/example_user5",
                            "arn:aws:iam::123456789123:user/example_user6",
                            "arn:aws:iam::123456789123:user/example_user7",
                            "arn:aws:iam::123456789123:user/example_user8",
                            "arn:aws:iam::123456789123:policy/example_policy1",
                            "arn:aws:iam::123456789123:policy/example_policy2",
                            "arn:aws:iam::123456789123:policy/example_policy3"
                        ]
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "cognito-identity:*",
                            "cognito-idp:*",
                            "cognito-sync:*",
                            "iam:ListRoles",
                            "iam:ListOpenIdConnectProviders",
                            "sns:ListPlatformApplications",
                            "iam:GetRole",
                            "iam:ListOpenIDConnectProviders",
                            "iam:ListRoles",
                            "iam:ListSAMLProviders",
                            "iam:GetSAMLProvider",
                            "kinesis:ListStreams",
                            "lambda:GetPolicy",
                            "lambda:ListFunctions",
                            "sns:ListPlatformApplications",
                            "ses:ListIdentities",
                            "ses:GetIdentityVerificationAttributes",
                            "mobiletargeting:GetApps",
                            "acm:ListCertificates"
                        ],
                        "Resource": [
                            "arn:aws:iam::123456789123:user/example_user1",
                            "arn:aws:iam::123456789123:user/example_user2"
                        ]
                    },
                    {
                        "Effect": "Allow",
                        "Action": "iam:CreateServiceLinkedRole",
                        "Resource": "*",
                        "Condition": {
                            "StringEquals": {
                                "iam:AWSServiceName": [
                                    "cognito-idp.amazonaws.com",
                                    "email.cognito-idp.amazonaws.com"
                                ]
                            }
                        }
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "iam:DeleteServiceLinkedRole",
                            "iam:GetServiceLinkedRoleDeletionStatus"
                        ],
                        "Resource": [
                            "arn:aws:iam::*:role/aws-service-role/cognito-idp.amazonaws.com/AWSServiceRoleForAmazonCognitoIdp*",
                            "arn:aws:iam::*:role/aws-service-role/email.cognito-idp.amazonaws.com/AWSServiceRoleForAmazonCognitoIdpEmail*"
                        ]
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "connect:Get*",
                            "connect:Describe*",
                            "connect:List*",
                            "ds:DescribeDirectories"
                        ],
                        "Resource": [
                            "arn:aws:iam::123456789123:user/example_user1",
                            "arn:aws:iam::123456789123:user/example_user4"
                        ]
                    },
                    {
                        "Effect": "Deny",
                        "Action": "connect:GetFederationTokens",
                        "Resource": "*"
                    }
                ]
            }   
...
```
Wondering if it has anything to do with the multiline YAML character at the beginning..

**Can also workaround with [resource-level ignores](https://github.com/aws-cloudformation/cfn-python-lint#resource-based-metadata) until this is fixed:**

```yaml
Resources:
  ExamplePolicy:
    Type: AWS::IAM::ManagedPolicy
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
            - E3502
```
okay I know what the problem is now.  Should have it fixed soon.

Resolving patch:
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 @@ def remove_functions(obj):
         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),
                         )
                     )
 


Test patch: