def resource_invoke(self, resource: S3BucketPolicy, logical_id: str, extras: Optional[Dict] = None) -> Result: result = Result() for statement in resource.Properties.PolicyDocument._statement_as_list( ): for principal in statement.get_principal_list(): account_id = get_account_id_from_principal(principal) if not account_id: continue if account_id not in self.valid_principals: if statement.Condition and statement.Condition.dict(): # Ignoring condition checks since they will get reviewed in other rules and future improvements pass else: self.add_failure_to_result( result, self.REASON.format(logical_id, account_id), resource_ids={logical_id}, resource_types={resource.Type}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource, "statement": statement, "principal": principal, "account_id": account_id, }, ) return result
def test_correct_event(): event = {"stack_template_url": "https://asdfasdfasdf/bucket/key", "stack": {"name": "blooblah"}} mock_created_s3_adapter_object = Mock() mock_created_s3_adapter_object.download_template_to_dictionary.return_value = {"Resources": {}} mock_boto3_adapter = Mock(return_value=mock_created_s3_adapter_object) mock_created_boto3_client_object = Mock() mock_created_boto3_client_object.get_template.return_value = {"Resources": {}} mock_created_boto3_client_object.compare_outputs.return_value = {} mock_boto3_client = Mock(return_value=mock_created_boto3_client_object) mock_created_rule_processor_object = Mock(spec=RuleProcessor) mock_created_rule_processor_object.process_cf_template.return_value = Result() mock_rule_processor = Mock(return_value=mock_created_rule_processor_object) mock_rule_processor.remove_debug_rules.return_value = [] with patch("cfripper.main.Boto3Client", new=mock_boto3_adapter): with patch("cfripper.main.RuleProcessor", new=mock_rule_processor): with patch("cfripper.main.Boto3Client", new=mock_boto3_client): from cfripper.main import handler handler(event, None) cfmodel = pycfmodel.parse({"Resources": {}}).resolve() mock_created_s3_adapter_object.download_template_to_dictionary.assert_called_once_with( "https://asdfasdfasdf/bucket/key" ) mock_created_rule_processor_object.process_cf_template.assert_called_once_with(cfmodel, ANY)
def resource_invoke(self, resource: S3BucketPolicy, logical_id: str, extras: Optional[Dict] = None) -> Result: result = Result() for statement in resource.Properties.PolicyDocument._statement_as_list( ): for principal in statement.get_principal_list(): account_id = get_account_id_from_principal(principal) if not account_id: continue if account_id not in self.valid_principals: if statement.Condition and statement.Condition.dict(): logger.warning( f"Not adding {type(self).__name__} failure in {logical_id} " f"because there are conditions: {statement.Condition}" ) else: self.add_failure_to_result( result, self.REASON.format(logical_id, account_id), resource_ids={logical_id}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource, "statement": statement, "principal": principal, "account_id": account_id, }, ) return result
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, KMSKey): # Ignoring KMSKey because there's already a rule for it `KMSKeyWildcardPrincipalRule` continue if isinstance(resource, IAMRole): # Checking the `AssumeRolePolicyDocument` for IAM Roles self.check_for_wildcards( result=result, logical_id=logical_id, resource=resource.Properties.AssumeRolePolicyDocument, resource_type=resource.Type, extras=extras, ) for policy in resource.policy_documents: self.check_for_wildcards( result=result, logical_id=logical_id, resource=policy.policy_document, resource_type=resource.Type, extras=extras, ) return result
def test_only_whitelisted_resources_are_removed(mock_rule_to_resource_whitelist): config = Config( stack_name="otherstack", rules=["S3CrossAccountTrustRule"], rule_to_resource_whitelist=mock_rule_to_resource_whitelist, ) result = Result() failed_rules = [ Failure( rule="S3CrossAccountTrustRule", reason="Forbidden cross-account policy allow with 123456789 for an S3 bucket.", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"rolething", "thenotwhitelistedthing", "anotherone"}, actions=None, granularity=RuleGranularity.RESOURCE, ) ] result.failed_rules = failed_rules RuleProcessor.remove_failures_of_whitelisted_resources(config=config, result=result) assert result.failed_rules == [ Failure( rule="S3CrossAccountTrustRule", reason="Forbidden cross-account policy allow with 123456789 for an S3 bucket.", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"thenotwhitelistedthing", "anotherone"}, actions=None, granularity=RuleGranularity.RESOURCE, ) ]
def test_with_invalid_role_inline_policy_fn_if(): role_props = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "RootRole": { "Type": "AWS::IAM::Role", "Properties": { "Path": "/", "Policies": [{ "Fn::If": [ "IsSandbox", { "PolicyDocument": { "Statement": [{ "Action": "sts:AssumeRole", "Effect": "Allow", "Resource": "arn:aws:iam::325714046698:role/sandbox-secrets-access", }], "Version": "2012-10-17", }, "PolicyName": "SandboxSecretsAccessAssumerole", }, { "PolicyDocument": { "Statement": [{ "Action": ["ec2:DeleteVpc"], "Effect": "Allow", "Resource": ["*"] }], "Version": "2012-10-17", }, "PolicyName": "ProdCredentialStoreAccessPolicy", }, ] }], }, } }, } result = Result() rule = IAMRolesOverprivilegedRule(None, result) rule.check_managed_policies = Mock() resources = pycfmodel.parse(role_props).resources rule.invoke(resources, []) rule.check_managed_policies.assert_called() assert not result.valid assert ( result.failed_rules[0]["reason"] == 'Role "RootRole" contains an insecure permission "ec2:DeleteVpc" in policy "ProdCredentialStoreAccessPolicy"' ) assert result.failed_rules[0]["rule"] == "IAMRolesOverprivilegedRule"
def resource_invoke(self, resource: SQSQueuePolicy, logical_id: str, extras: Optional[Dict] = None) -> Result: result = Result() if resource.Properties.PolicyDocument.allowed_principals_with( REGEX_HAS_STAR_OR_STAR_AFTER_COLON): for statement in resource.Properties.PolicyDocument._statement_as_list( ): if statement.Effect == "Allow" and statement.principals_with( REGEX_HAS_STAR_OR_STAR_AFTER_COLON): if statement.Condition and statement.Condition.dict(): logger.warning( f"Not adding {type(self).__name__} failure in {logical_id} " f"because there are conditions: {statement.Condition}" ) else: self.add_failure_to_result( result, self.REASON.format(logical_id), resource_ids={logical_id}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource, "statement": statement, }, ) return result
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, S3BucketPolicy): for statement in resource.Properties.PolicyDocument._statement_as_list( ): for principal in statement.get_principal_list(): account_id = get_account_id_from_principal(principal) if not account_id: continue if account_id not in self.valid_principals: if statement.Condition and statement.Condition.dict( ): logger.warning( f"Not adding {type(self).__name__} failure in {logical_id} " f"because there are conditions: {statement.Condition}" ) else: self.add_failure_to_result( result, self.REASON.format(logical_id, account_id), resource_ids={logical_id}, ) return result
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() if self.AWS_RESOURCE is None: logger.warning( f"Not running {type(self).__name__} rule as AWS_RESOURCE is not defined." ) else: for logical_id, resource in cfmodel.Resources.items(): if isinstance( resource, self.AWS_RESOURCE ) and resource.Properties.PolicyDocument.allowed_actions_with( REGEX_HAS_STAR_OR_STAR_AFTER_COLON): self.add_failure_to_result( result, self.REASON.format(self.AWS_RESOURCE.__name__, logical_id), resource_ids={logical_id}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource, }, resource_types={resource.Type}, ) return result
def test_with_valid_role_inline_policy(): role_props = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "RootRole": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyName": "root", "PolicyDocument": { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["IAM:CREATEPOLICY"], "Resource": ["arn:aws:glue:eu-west-1:12345678:catalog"], }], }, "Roles": "some_role", }, } }, } resource = pycfmodel.parse(role_props).resources result = Result() rule = PrivilegeEscalationRule(None, result) rule.invoke(resource, []) assert not result.valid assert len(result.failed_rules) == 1
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, S3BucketPolicy) and resource.Properties.PolicyDocument.allowed_actions_with( re.compile(r"^s3:L.*$") ): bucket_name = resource.Properties.Bucket if not isinstance(bucket_name, str): logger.warning(f"Not adding {type(self).__name__} failure in {logical_id} – try resolving?") continue if "UNDEFINED_PARAM_" in bucket_name: bucket_name = bucket_name[len("UNDEFINED_PARAM_") :] bucket = cfmodel.Resources.get(bucket_name) if bucket and bucket.Properties.AccessControl == "PublicRead": self.add_failure_to_result( result, self.REASON.format(logical_id), resource_ids={logical_id}, resource_types={resource.Type}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource, "bucket_name": bucket_name, }, ) return result
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, IAMRole): # check AssumeRolePolicyDocument. if resource.Properties.AssumeRolePolicyDocument.allowed_actions_with(REGEX_WILDCARD_POLICY_ACTION): self.add_failure_to_result( result, self.REASON.format(logical_id, "AssumeRolePolicy"), resource_ids={logical_id}, ) # check other policies of the IAM role. if resource.Properties.Policies: for policy in resource.Properties.Policies: if policy.PolicyDocument.allowed_actions_with(REGEX_WILDCARD_POLICY_ACTION): self.add_failure_to_result( result, self.REASON.format(logical_id, f"{policy.PolicyName} policy"), resource_ids={logical_id}, ) # check AWS::IAM::ManagedPolicy. elif isinstance(resource, IAMManagedPolicy) and resource.Properties.PolicyDocument.allowed_actions_with( REGEX_WILDCARD_POLICY_ACTION ): self.add_failure_to_result( result, self.REASON.format(logical_id, "AWS::IAM::ManagedPolicy"), resource_ids={logical_id}, ) return result
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, IAMRole): self.check_managed_policies(result, logical_id, resource) self.check_inline_policies(result, logical_id, resource) return result
def test_with_templates(self): dir_path = os.path.dirname(os.path.realpath(__file__)) test_templates = glob.glob(f"{dir_path}/test_templates/*.*") for template in test_templates: with open(template) as cf_script: cf_template = convert_json_or_yaml_to_dict(cf_script.read()) config = Config( project_name=template, service_name=template, stack_name=template, rules=DEFAULT_RULES.keys() ) # Scan result result = Result() rules = [DEFAULT_RULES.get(rule)(config, result) for rule in config.rules] processor = RuleProcessor(*rules) processor.process_cf_template(cf_template, config, result) # Use this to print the stack if there's an error if len(result.exceptions): print(template) traceback.print_tb(result.exceptions[0].__traceback__) no_resource_templates = ["vulgar_bad_syntax.yml", "rubbish.json"] if template.split("/")[-1] in no_resource_templates: assert len(result.exceptions) == 1 else: assert len(result.exceptions) == 0
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): for policy in resource.policy_documents: self._check_policy_document(result, logical_id, policy.policy_document, policy.name, extras) if isinstance(resource, IAMRole): self._check_policy_document( result, logical_id, resource.Properties.AssumeRolePolicyDocument, None, extras) elif isinstance(resource, KMSKey): self._check_policy_document(result, logical_id, resource.Properties.KeyPolicy, None, extras) elif isinstance(resource, GenericResource): if hasattr(resource, "Properties"): policy_document = resource.Properties.get("PolicyDocument") if policy_document: self._check_policy_document( result, logical_id, PolicyDocument(**policy_document), None, extras) return result
def test_remove_failures_from_whitelisted_actions_failure_no_actions_is_removed( mock_logger, mock_rule_to_action_whitelist): config = Config( stack_name="teststack", rules=["S3CrossAccountTrustRule"], rule_to_action_whitelist=mock_rule_to_action_whitelist, ) result = Result() failure = Failure( rule="S3CrossAccountTrustRule", reason= "rolething has forbidden cross-account policy allow with 123456789 for an S3 bucket.", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, actions=set(), granularity=RuleGranularity.ACTION, ) result.failed_rules = [failure] RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result) assert result.failed_rules == [] mock_logger.assert_called_once_with( f"Failure with action granularity doesn't have actions: {failure}")
def test_with_invalid_role_managed_policy(): role_props = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "RootRole": { "Type": "AWS::IAM::Role", "Properties": { "Path": "/", "ManagedPolicyArns": ["arn:aws:iam::aws:policy/AdministratorAccess"] }, } }, } result = Result() rule = IAMRolesOverprivilegedRule(None, result) resources = pycfmodel.parse(role_props).resources rule.invoke(resources, []) assert not result.valid assert ( result.failed_rules[0]["reason"] == "Role RootRole has forbidden Managed Policy arn:aws:iam::aws:policy/AdministratorAccess" ) assert result.failed_rules[0]["rule"] == "IAMRolesOverprivilegedRule"
def test_report_format_is_the_one_expected(template_one_role): result = Result() rule = CrossAccountTrustRule(Config(aws_account_id="123456789"), result) rule.invoke(template_one_role) assert not result.valid assert result.failed_rules == [ Failure( rule="CrossAccountTrustRule", reason= "RootRole has forbidden cross-account trust relationship with arn:aws:iam::123456789:root", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.MEDIUM, resource_ids={"RootRole"}, actions=set(), granularity=RuleGranularity.RESOURCE, ), Failure( rule="CrossAccountTrustRule", reason= ("RootRole has forbidden cross-account trust relationship with arn:aws:iam::999999999:role/" "*****@*****.**"), rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.MEDIUM, resource_ids={"RootRole"}, actions=set(), granularity=RuleGranularity.RESOURCE, ), ]
def test_failures_are_raised(bad_template): result = Result() rule = PartialWildcardPrincipalRule(None, result) rule.invoke(bad_template) assert result.valid assert len(result.failed_rules) == 0 assert len(result.failed_monitored_rules) == 4 assert result.failed_monitored_rules[ 0].rule == "PartialWildcardPrincipalRule" assert result.failed_monitored_rules[ 0].reason == "PolicyA contains an unknown principal: 123445" assert result.failed_monitored_rules[ 1].rule == "PartialWildcardPrincipalRule" assert ( result.failed_monitored_rules[1].reason == "PolicyA should not allow wildcard in principals or account-wide principals " "(principal: 'arn:aws:iam::123445:12345*')") assert result.failed_monitored_rules[ 2].rule == "PartialWildcardPrincipalRule" assert result.failed_monitored_rules[ 2].reason == "PolicyA contains an unknown principal: 123445" assert result.failed_monitored_rules[ 3].rule == "PartialWildcardPrincipalRule" assert ( result.failed_monitored_rules[3].reason == "PolicyA should not allow wildcard in principals or account-wide principals " "(principal: 'arn:aws:iam::123445:root')")
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, KMSKey): for statement in resource.Properties.KeyPolicy._statement_as_list( ): if statement.Effect == "Allow" and statement.principals_with( self.CONTAINS_WILDCARD_PATTERN): for principal in statement.get_principal_list(): if self.CONTAINS_WILDCARD_PATTERN.match(principal): if statement.Condition and statement.Condition.dict( ): logger.warning( f"Not adding {type(self).__name__} failure in {logical_id} " f"because there are conditions: {statement.Condition}" ) else: self.add_failure_to_result( result, self.REASON.format(logical_id), resource_ids={logical_id}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource, "statement": statement, "principal": principal, }, ) return result
def resource_invoke(self, resource: SQSQueuePolicy, logical_id: str, extras: Optional[Dict] = None) -> Result: result = Result() if resource.Properties.PolicyDocument.allowed_principals_with( REGEX_HAS_STAR_OR_STAR_AFTER_COLON): for statement in resource.Properties.PolicyDocument.statement_as_list( ): if statement.Effect == "Allow" and statement.principals_with( REGEX_HAS_STAR_OR_STAR_AFTER_COLON): if statement.Condition and statement.Condition.dict(): # Ignoring condition checks since they will get reviewed in other rules and future improvements pass else: self.add_failure_to_result( result, self.REASON.format(logical_id), resource_ids={logical_id}, resource_types={resource.Type}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource, "statement": statement, }, ) return result
def test_remove_failures_from_whitelisted_resources_only_removes_resource_granularity(mock_rule_to_resource_whitelist): config = Config( stack_name="otherstack", rules=["S3CrossAccountTrustRule"], rule_to_resource_whitelist=mock_rule_to_resource_whitelist, ) result = Result() failed_rules = [ { "rule": "S3CrossAccountTrustRule", "reason": "rolething has forbidden cross-account policy allow with 123456789 for an S3 bucket.", "rule_mode": RuleMode.BLOCKING, "risk_value": RuleRisk.HIGH, "resource_ids": {"rolething"}, "actions": None, "granularity": RuleGranularity.ACTION, }, { "rule": "S3CrossAccountTrustRule", "reason": "anotherthing has forbidden cross-account policy allow with 123456789 for an S3 bucket.", "rule_mode": RuleMode.BLOCKING, "risk_value": RuleRisk.HIGH, "resource_ids": {"anotherthing"}, "actions": None, "granularity": RuleGranularity.RESOURCE, } ] result.failed_rules = failed_rules RuleProcessor.remove_failures_of_whitelisted_resources(config=config, result=result) assert result.failed_rules == failed_rules
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, (IAMManagedPolicy, IAMPolicy, S3BucketPolicy, SNSTopicPolicy, SQSQueuePolicy)): self.check_for_wildcards( result=result, logical_id=logical_id, resource=resource.Properties.PolicyDocument, resource_type=resource.Type, extras=extras, ) elif isinstance(resource, (IAMRole, IAMUser)): if isinstance(resource, IAMRole): self.check_for_wildcards( result=result, logical_id=logical_id, resource=resource.Properties.AssumeRolePolicyDocument, resource_type=resource.Type, extras=extras, ) if resource.Properties and resource.Properties.Policies: for policy in resource.Properties.Policies: self.check_for_wildcards( result=result, logical_id=logical_id, resource=policy.PolicyDocument, resource_type=resource.Type, extras=extras, ) return result
def test_invalid_security_group_range(self): role_props = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "RootRole": { "Type": "AWS::EC2::SecurityGroup", "Properties": { 'SecurityGroupIngress': [{ 'CidrIp': "0.0.0.0/0", 'FromPort': 0, 'ToPort': 100 }] } } } } result = Result() rule = SecurityGroupOpenToWorldRule(None, result) resources = parse(role_props).resources rule.invoke(resources) assert result.failed_rules[0][ 'reason'] == 'Ports 0 - 100 open in Security Group RootRole' assert result.failed_rules[0]['rule'] == 'SecurityGroupOpenToWorldRule'
def test_action_whitelist_keeps_non_whitelisted_actions(): whitelist_for_all_stacks = {"MockRule": {".*": {"s3:List"}}} config = Config(stack_name="abcd", rules=["MockRule"], rule_to_action_whitelist=whitelist_for_all_stacks) result = Result() failed_rules = [ Failure( rule="MockRule", reason="MockRule is invalid for some actions", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, actions={"s3:ListBucket", "s3:GetBucket"}, granularity=RuleGranularity.ACTION, ) ] result.failed_rules = failed_rules RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result) assert result.failed_rules == [ Failure( rule="MockRule", reason="MockRule is invalid for some actions", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, actions={"s3:GetBucket"}, granularity=RuleGranularity.ACTION, ) ]
def test_security_group_rules_as_refs(self): role_props = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "RootRole": { "Type": "AWS::EC2::SecurityGroup", "Properties": { 'SecurityGroupIngress': [{ 'CidrIp': { "Ref": "MyParam" }, 'FromPort': 22, 'ToPort': 22 }] } } } } result = Result() rule = SecurityGroupOpenToWorldRule(None, result) resources = parse(role_props).resources rule.invoke(resources) assert result.valid assert len(result.failed_rules) == 0
def process_cf_template(self, cfmodel: CFModel, config: Config, extras: Optional[Dict] = None) -> Result: result = Result() for rule in self.rules: try: result += rule.invoke(cfmodel, extras) except Exception as other_exception: result.add_exception(other_exception) logger.exception( "{} crashed with {} for project - {}, service - {}, stack - {}" .format( type(rule).__name__, type(other_exception).__name__, config.project_name, config.service_name, config.stack_name, )) continue self.remove_failures_of_whitelisted_actions(config=config, result=result) self.remove_failures_of_whitelisted_resources(config=config, result=result) return result
def test_security_group_type_slash0(self): role_props = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "RootRole": { "Type": "AWS::EC2::SecurityGroup", "Properties": { 'SecurityGroupIngress': [{ 'CidrIp': "0.0.0.0/0", 'FromPort': 22, 'ToPort': 22 }] } } } } result = Result() rule = SecurityGroupOpenToWorldRule(None, result) resources = parse(role_props).resources rule.invoke(resources) assert not result.valid assert result.failed_rules[0][ 'reason'] == 'Port 22 open to the world in security group "RootRole"' assert result.failed_rules[0]['rule'] == 'SecurityGroupOpenToWorldRule'
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() password_protected_cluster_ids = [] instances_to_check = [] for logical_id, resource in cfmodel.Resources.items(): # flag insecure RDS Clusters. if resource.Type == "AWS::RDS::DBCluster": failure_added = self._failure_added(result, logical_id, resource) if not failure_added: password_protected_cluster_ids.append(logical_id) # keep track of RDS instances so they can be examined in the code below. elif resource.Type == "AWS::RDS::DBInstance": instances_to_check.append((logical_id, resource)) # check each instance with the context of clusters. for logical_id, resource in instances_to_check: if resource.Properties.get("DBClusterIdentifier") and any( cluster_id in resource.Properties.get( "DBClusterIdentifier") for cluster_id in password_protected_cluster_ids): continue self._failure_added(result, logical_id, resource) return result
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, KMSKey): for statement in resource.Properties.KeyPolicy._statement_as_list(): filtered_principals = statement.principals_with(self.CONTAINS_WILDCARD_PATTERN) if statement.Effect == "Allow" and filtered_principals: for principal in filtered_principals: if statement.Condition and statement.Condition.dict(): # Ignoring condition checks since they will get reviewed in other # rules and future improvements pass else: self.add_failure_to_result( result, self.REASON.format(logical_id), resource_ids={logical_id}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource, "statement": statement, "principal": principal, }, ) return result