aws-cloudformation / cloudformation-guard

Guard offers a policy-as-code domain-specific language (DSL) to write rules and validate JSON- and YAML-formatted data such as CloudFormation Templates, K8s configurations, and Terraform JSON plans/configurations against those rules. Take this survey to provide feedback about cfn-guard: https://amazonmr.au1.qualtrics.com/jfe/form/SV_bpyzpfoYGGuuUl0
Apache License 2.0
1.29k stars 180 forks source link

[BUG] #567

Open Aaron-Garrett opened 1 month ago

Aaron-Garrett commented 1 month ago

Describe the bug A clear and concise description of what the bug is.

The rule I wrote checking for S3 permissions on wildcard Resource is running and failing on IAM roles without any S3 permissions.

To Reproduce Please supply:

Example rules and template that results in the error Template:

rImmutaRdsLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: immunment
      PermissionsBoundary: !Sub "arn:aws:iam::${AWS::AccountId}:policy/L-Boundary"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "rds.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "RdsInvokeLambdaPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "lambda:InvokeFunction"
                Resource: !GetAtt rIImmuta.Arn

CloudFormation Guard Rule:

I KNOW THIS SEEMS TOTALLY UNRELATED TO THE CODE ABOVE BUT THIS IS THE CHECK THAT KEEPS FAILING

# R5.0  Automatically inspect the code for * in S3 folder (prefix) access and present a warning statement to the developer if found

let aws_iam_policies_no_full_access_for_s3 = Resources.*[
  Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_POLICY_NO_FULL_ACCESS_FOR_S3"
]

let aws_iam_roles_no_full_access_for_s3 = Resources.*[
  Type in [/AWS::IAM::Role/]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_ROLE_NO_FULL_ACCESS_FOR_S3"
]

# Checks there is no wildcard in the bucket name in an IAM Role

rule IAM_ROLE_FULL_BUCKET_NAME_USED when %aws_iam_roles_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks the full folder path is used with no wildcards before the second "/" in an IAM Role

rule IAM_ROLE_FULL_FOLDER_PATH_USED when %aws_iam_roles_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks there is no wildcard in the bucket name in an IAM Policy

rule IAM_POLICY_FULL_BUCKET_NAME_USED when %aws_iam_policies_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks the full folder path is used with no wildcards before the second "/" in an IAM Policy

rule IAM_POLICY_FULL_FOLDER_PATH_USED when %aws_iam_policies_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

let s3_no_full_permission = Resources.*[ Type == 'AWS::S3::BucketPolicy'
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "S3_NO_FULL_PERMISSION"
]

# Checks there is no wildcard in the bucket name in an IAM Policy

rule BUCKET_POLICY_FULL_BUCKET_NAME_USED when %s3_no_full_permission !empty {
  let violationsString = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks the full folder path is used with no wildcards before the second "/" in an IAM Policy

rule BUCKET_POLICY_FULL_FOLDER_PATH_USED when %s3_no_full_permission !empty {
  let violationsString = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

The commands you used to invoke the tool
        OUTPUT=RUST_BACKTRACE=1 ./cfn-guard-v3-ubuntu-latest/cfn-guard validate --show-summary pass,fail --data "$TEMPLATE_FILE" --rules .github/folderlib/cfn-guard/rules/ 2>&1 

The output of a -v log level if it's not related to cfn-guard-lambda, or the relevant CloudWatch log messages if it is related to the cfn-guard-lambda
s3_no_full_access.guard/IAM_ROLE_NO_FULL_ACCESS_FOR_S3      FAIL
---
Evaluating data template.yml against rules s3_no_full_access.guard
Number of non-compliant resources 1
Resource = rImmutaRdsLambdaRole {
  Type      = AWS::IAM::Role
  Rule = IAM_ROLE_NO_FULL_ACCESS_FOR_S3 {
    ALL {
      Check =  %violationsSub EMPTY   {
        ComparisonError {
          Message          {
            Violation: * in S3 folder (prefix) access.
            Fix: Avoid wildcard matching * in S3 folder (prefix) access
          }
          Error            = Check was not compliant as property [/Resources/rImmutaRdsLambdaRole[L:1549,C:4]] was not empty.
          PropertyPath    = /Resources/rImmutaRdsLambdaRole[L:1549,C:4]
          Operator        = EMPTY
          Code:
             1547.      Principal: secretsmanager.amazonaws.com
             1548.
             1549.  rImmutaRdsLambdaRole:
             1550.    Type: "AWS::IAM::Role"
             1551.    Properties:
             1552.      RoleName: !Sub immuta-rds-invoke-lambda-role-

        }
      }
    }
  }
}

Expected behavior A clear and concise description of what you expected to happen.

The test should skip

Operating System: [eg, MacOS, Windows, Ubuntu, etc]

Linux

OS Version [eg Catalina, 10, 18.04, etc]

Ubuntu latest

Aaron-Garrett commented 1 month ago

This is a duplicate of a previous issue, but I got too busy and forgot to check on it. The comment on that was that the rules did not appear to be in the Issue, but they were, hence my bold heading highlighting that as silly as it seems, the rules above are the ones that were failing.

joshfried-aws commented 1 month ago

Hi @Aaron-Garrett as mentioned in your previous issue, the rule from your provided output that fails is IAM_ROLE_NO_FULL_ACCESS_FOR_S3 the definition for this rule is not included in your snippet above. Without the definition of this rule, we aren't able to help you. I can see you're passing a directory (--rules .github/folderlib/cfn-guard/rules/), and not a single rule file, and it's not clear if your snippet includes all the rules in this directory or not (since we're missing the definition for the previously mentioned rule, I'm assuming not).

Again, this rule must be defined somewhere, so please provide it's definition so we can reproduce and help you.

For future reference, please re-open the previous issue instead of creating a new one.

Thanks,

joshfried-aws commented 1 month ago

In addition to my above comment, to help you find it the output indicates the file this rule is defined in is s3_no_full_access.guard

Aaron-Garrett commented 1 month ago

Alright. I could not figure out how to reopen the issue. It is the correct rule, I have just updated the names since the error occurred in an attempt to bring more clarity to the output.

joshfried-aws commented 1 month ago

Hi @Aaron-Garrett this above snippet still doesn't include the definition for the rule IAM_ROLE_NO_FULL_ACCESS_FOR_S3 I am unable to assist you without this rules definition.

joshfried-aws commented 1 month ago

Hi @Aaron-Garrett I see you have edited your comment since my last reply. I want you to know I am unable to reproduce the issue (using cfn-guard v3.1.1) with everything you have provided.

> cfn-guard validate -r 567.guard -d 567.yaml -S all
/test-files/567.yaml Status = SKIP
SKIP rules
567.guard/IAM_ROLE_FULL_BUCKET_NAME_USED         SKIP
567.guard/IAM_ROLE_FULL_FOLDER_PATH_USED         SKIP
567.guard/IAM_POLICY_FULL_BUCKET_NAME_USED       SKIP
567.guard/IAM_POLICY_FULL_FOLDER_PATH_USED       SKIP
567.guard/BUCKET_POLICY_FULL_BUCKET_NAME_USED    SKIP
567.guard/BUCKET_POLICY_FULL_FOLDER_PATH_USED    SKIP
---
> cfn-guard --version
cfn-guard 3.1.1
Aaron-Garrett commented 3 weeks ago

Here is another one that just failed today. Here is the code in the CloudFormation template:

    rDMSPhysicianPolicyReadWrite:
      Type: "AWS::IAM::ManagedPolicy"
      Properties:
        ManagedPolicyName: "dms-physicianresearch-rw"
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            -
              Effect: Allow
              Action:
                - 'dms:ReloadTables'
                - 'dms:StopReplicationTask'
                - 'dms:StartReplicationTask'
                - 'dms:ModifyReplicationTask'
                - 'dms:ModifyEndpoint'
                - 'dms:TestConnection'
              Resource: 
                # - !Ref rLglPhysicianResearchReplicationTaskInfotrac
                # - !Ref rLglPhysicianResearchReplicationTaskTlac
                # - !Ref rLglPhysicianResearchReplicationTaskTlps
                # - !Ref rLglPhysicianResearchReplicationTaskUsol
                - !Ref rLglPhysicianResearchTargetEndpointInfotrac
                - !Ref rLglPhysicianResearchTargetEndpointTlac
                - !Ref rLglPhysicianResearchTargetEndpointTlps
                - !Ref rLglPhysicianResearchTargetEndpointUsol
                - !Ref rLglPhysicianResearchsourcepointInfotrac
                - !Ref rLglPhysicianResearchsourcepointTlac
                - !Ref rLglPhysicianResearchsourcepointTlps
                - !Ref rLglPhysicianResearchsourcepointUsol

        Roles:                                                            
          -  !Sub ${pBusinessUsecase}_data_processing_role
          -  !If [ CreateDevResources,!Sub "aws_${pBusinessUsecase}_developers", !Ref "AWS::NoValue" ]
          #- !Sub arn:aws:iam::${AWS::AccountId}:role/aws_${pBusinessUsecase}_developers

Here is the error from CFN-Guard:

Evaluating data TEMPLATE.yml against rules s3_full_path_used.guard
Number of non-compliant resources 2
Resource = rDMSPhysicianPolicyReadWrite {
  Type      = AWS::IAM::ManagedPolicy
  Rule = IAM_POLICY_FULL_BUCKET_NAME_USED {
    ALL {
      Check =  %violationsSub EMPTY   {
        ComparisonError {
          Message          {
            Violation: * in S3 folder (prefix) access.
            Fix: Avoid wildcard matching * in S3 folder (prefix) access
          }
          Error            = Check was not compliant as property [/Resources/rDMSPhysicianPolicyReadWrite[L:734,C:6]] was not empty.
          PropertyPath    = /Resources/rDMSPhysicianPolicyReadWrite[L:734,C:6]
          Operator        = EMPTY
          Code:
              732.              - '"'
              733.              - '}' 
              734.    rDMSPhysicianPolicyReadWrite:
              735.      Type: "AWS::IAM::ManagedPolicy"
              736.      Properties:
              737.        ManagedPolicyName: "dms-physicianresearch-rw"

        }
      }
    }
  }
  Rule = IAM_POLICY_FULL_FOLDER_PATH_USED {
    ALL {
      Check =  %violationsSub EMPTY   {
        ComparisonError {
          Message          {
            Violation: * in S3 folder (prefix) access.
            Fix: Avoid wildcard matching * in S3 folder (prefix) access
          }
          Error            = Check was not compliant as property [/Resources/rDMSPhysicianPolicyReadWrite[L:734,C:6]] was not empty.
          PropertyPath    = /Resources/rDMSPhysicianPolicyReadWrite[L:734,C:6]
          Operator        = EMPTY
          Code:
              732.              - '"'
              733.              - '}' 
              734.    rDMSPhysicianPolicyReadWrite:
              735.      Type: "AWS::IAM::ManagedPolicy"
              736.      Properties:
              737.        ManagedPolicyName: "dms-physicianresearch-rw"

        }
      }
    }
  }
}

And here is the CloudFormation guard script:

# R5.0  Automatically inspect the code for * in S3 folder (prefix) access and present a warning statement to the developer if found

let aws_iam_policies_no_full_access_for_s3 = Resources.*[
  Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_POLICY_NO_FULL_ACCESS_FOR_S3"
]

let aws_iam_roles_no_full_access_for_s3 = Resources.*[
  Type in [/AWS::IAM::Role/]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_ROLE_NO_FULL_ACCESS_FOR_S3"
]

# Checks there is no wildcard in the bucket name in an IAM Role

rule IAM_ROLE_FULL_BUCKET_NAME_USED when %aws_iam_roles_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks the full folder path is used with no wildcards before the second "/" in an IAM Role

rule IAM_ROLE_FULL_FOLDER_PATH_USED when %aws_iam_roles_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::lly-(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::lly-(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::lly-(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::lly-(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks there is no wildcard in the bucket name in an IAM Policy

rule IAM_POLICY_FULL_BUCKET_NAME_USED when %aws_iam_policies_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks the full folder path is used with no wildcards before the second "/" in an IAM Policy

rule IAM_POLICY_FULL_FOLDER_PATH_USED when %aws_iam_policies_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::lly-(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::lly-(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::lly-(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::lly-(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

let s3_no_full_permission = Resources.*[ Type == 'AWS::S3::BucketPolicy'
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "S3_NO_FULL_PERMISSION"
]

# Checks there is no wildcard in the bucket name in an IAM Policy

rule BUCKET_POLICY_FULL_BUCKET_NAME_USED when %s3_no_full_permission !empty {
  let violationsString = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks the full folder path is used with no wildcards before the second "/" in an IAM Policy

rule BUCKET_POLICY_FULL_FOLDER_PATH_USED when %s3_no_full_permission !empty {
  let violationsString = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::lly-(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::lly-(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::lly-(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::lly-(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

It seems to keep happening on DMS permissions. No idea why.