cloudformation-guard icon indicating copy to clipboard operation
cloudformation-guard copied to clipboard

Trying to search an If statement for a value

Open Aaron-Garrett opened this issue 1 year ago • 2 comments

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

Trying to search the If statement of CloudFormation template to see if its contents match a regex expression.

Any examples Please supply:

  1. Example rules and template that you have tried
let aws_iam_entities_no_managed_policy = Resources.*[
  Type in [ /AWS::IAM::User/,
            /AWS::IAM::Role/,
            /AWS::IAM::Group/ ]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_ONLY_ALLOW_ManagedPolicies"
]

let INVALID_VALUES = [/(?i)policy\/AWS/, /(?i)\/VMImportExportRoleForAWSConnector/]

rule IAM_ONLY_ALLOW_ManagedPolicies when %aws_iam_entities_no_managed_policy !empty {
  when %aws_iam_entities_no_managed_policy.Properties.ManagedPolicyArns[*] !empty {
    let sub_queries = %aws_iam_entities_no_managed_policy.Properties.ManagedPolicyArns.*[ keys == "Fn::Sub" ]
    let if_queries = %aws_iam_entities_no_managed_policy.Properties.ManagedPolicyArns.*[ keys == "Fn::If" ]

    when %sub_queries EXISTS {
        %sub_queries not in %INVALID_VALUES
          <<
            Violation: AWS Managed Policies are not approved for use.
            Fix: Use EDB-Manged Policies instead
          >>
          %if_queries not in %INVALID_VALUES
          <<
            Violation: AWS Managed Policies are not approved for use.
            Fix: Use EDB-Manged Policies instead
          >>
    }
  }
}
let aws_iam_entities_no_managed_policy = Resources.*[
  Type in [ /AWS::IAM::User/,
            /AWS::IAM::Role/,
            /AWS::IAM::Group/ ]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_ONLY_ALLOW_ManagedPolicies"
]

let INVALID_VALUES = [/(?i)\/AWS/, /(?i)\/VMImportExportRoleForAWSConnector/]

rule IAM_ONLY_ALLOW_ManagedPolicies when %aws_iam_entities_no_managed_policy !empty {
  when %aws_iam_entities_no_managed_policy.Properties.ManagedPolicyArns[*] !empty {
    let sub_queries = %aws_iam_entities_no_managed_policy.Properties.ManagedPolicyArns.*[ keys == "Fn::Sub" ]
    let if_queries = %aws_iam_entities_no_managed_policy.Properties.ManagedPolicyArns.*[ keys == "Fn::If" ].*[ keys == "Fn::Sub" ]

    when %sub_queries EXISTS {
        %sub_queries not in %INVALID_VALUES
          <<
            Violation: AWS Managed Policies are not approved for use.
            Fix: Use EDB-Manged Policies instead
          >>
          %if_queries not in %INVALID_VALUES
          <<
            Violation: AWS Managed Policies are not approved for use.
            Fix: Use EDB-Manged Policies instead
          >>
    }
  }
}
  1. 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/pr-automation/lib/cfn-guard/rules/ 2>&1`
  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

Everything passed for the first one

The second one gave me errors saying things did not equal Fn::Sub, this was resolved when I removed the .*[ keys == "Fn::Sub" ] from the if statement query.

Operating System: Ubuntu

OS Version Latest

Additional Information Here is an example block of code:

rAwsEdbFPDBUDeveloperRole:
    Type: AWS::IAM::Role
    # Condition: CreateDevORQaRole
    Properties:
      RoleName: !Sub aws_${pBusinessUsecase}_${pEnv}
      PermissionsBoundary: !Sub 'arn:aws:iam::${AWS::AccountId}:policy/Boundary'
      MaxSessionDuration: 36000
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
      ManagedPolicyArns:
      **- !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-kms-s3-rw"**
      - !Sub "arn:aws:iam::${AWS::AccountId}:policy/kms-secrets-rw"
      - !If [
            CreateDevRole,
            **!Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-allowed-policies",**
            !Ref "AWS::NoValue",
          ]
      - !If [CreateDevRole, !Ref rFPDs3WritePolicy, !Ref "AWS::NoValue"] 
      - !Ref rFPDs3ReadPolicy
      - !Ref rSecretsPolicy
      - !If [CreateDevRole, !Ref rFPDRedshiftPolicy, !Ref "AWS::NoValue"]

I am wanting it to look at this chunk of code and flag anything !Sub that has aws after the "/" and anything !If that has aws after the "/", such as the bolded lines above. The !Sub search works well, but the !If statement keeps passing incorrectly even though I am searching only for \/aws.

Aaron-Garrett avatar Apr 16 '24 13:04 Aaron-Garrett

Hey @Aaron-Garrett I think i was able to come up with a rule to help you with this specific use case. I think one issue with the logic in your original rule was to do with the way if intrinsic functions is mapped to their json equivalent..

In this case your first if function maps to this in json format

{
    "Fn::If": [
        "CreateDevRole",
        {
            "Fn::Sub": "arn:aws:iam::${AWS::AccountId}:policy/aws-allowed-policies"
        },
        {
            "Ref": "AWS::NoValue"
        }
    ]
}

It's important to note here when evaluating this mapped if function that youre comparing a value (in this case a list where some elements are strings, some elements are maps/structs) to an array of regex. This will always pass in this case due to the contents of the list.

Heres the rule, i have added some comments to help clear things up

let aws_iam_entities_no_managed_policy = Resources.*[
  Type in [ /AWS::IAM::User/,
            /AWS::IAM::Role/,
            /AWS::IAM::Group/ ]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_ONLY_ALLOW_ManagedPolicies"
]

let INVALID_VALUES = [/(?i)\/AWS/, /(?i)\/VMImportExportRoleForAWSConnector/]

rule IAM_ONLY_ALLOW_ManagedPolicies when %aws_iam_entities_no_managed_policy !empty {
  let managed_policy_arns = %aws_iam_entities_no_managed_policy.Properties.ManagedPolicyArns

  when %managed_policy_arns !empty {
    check(%managed_policy_arns)
  }
}


rule check(curr) {
    # if is string, we can do direct comparison to the list of strings/regex
    when %curr is_string {
        %curr NOT IN %INVALID_VALUES
    }


    # if is_list or is_struct iterate and check each individual value
    when %curr is_list OR %curr is_struct{
        %curr.* {
            check(this)
        }
    }
}

I hope this was helpful to you, please let me know if you have anymore questions.

joshfried-aws avatar Apr 17 '24 16:04 joshfried-aws

Hey @Aaron-Garrett just checking in to see if the information I provided was enough to help you resolve your issue here.

Please let us know if you need anymore help on this issue

joshfried-aws avatar Apr 26 '24 15:04 joshfried-aws