def test_failures_are_raised_for_bad_instances_and_bad_clusters( bad_template_clusters_with_bad_instances): rule = HardcodedRDSPasswordRule(None) result = rule.invoke(bad_template_clusters_with_bad_instances) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "Default RDS Cluster password parameter (readable in plain-text) for BadCluster99.", risk_value=RuleRisk.MEDIUM, rule="HardcodedRDSPasswordRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"BadCluster99"}, resource_types={"AWS::RDS::DBCluster"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "RDS Instance password parameter missing NoEcho for BadDb33.", risk_value=RuleRisk.MEDIUM, rule="HardcodedRDSPasswordRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"BadDb33"}, resource_types={"AWS::RDS::DBInstance"}, ), ], )
def test_failures_are_raised(bad_template): rule = S3BucketPolicyPrincipalRule(Config(aws_principals=["12345"])) result = rule.invoke(bad_template) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "S3 Bucket S3BucketPolicy policy has non-allowed principals 1234556", risk_value=RuleRisk.HIGH, rule="S3BucketPolicyPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"S3BucketPolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "S3 Bucket S3BucketPolicy policy has non-allowed principals 1234557", risk_value=RuleRisk.HIGH, rule="S3BucketPolicyPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"S3BucketPolicy"}, ), ], )
def test_s3_bucket_with_wildcards(s3_bucket_with_wildcards): rule = S3BucketPolicyWildcardActionRule(None) result = rule.invoke(s3_bucket_with_wildcards) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "The S3BucketPolicy S3BucketPolicy should not allow a `*` action", risk_value=RuleRisk.MEDIUM, rule="S3BucketPolicyWildcardActionRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"S3BucketPolicy"}, resource_types={"AWS::S3::BucketPolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "The S3BucketPolicy S3BucketPolicy2 should not allow a `*` action", risk_value=RuleRisk.MEDIUM, rule="S3BucketPolicyWildcardActionRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"S3BucketPolicy2"}, resource_types={"AWS::S3::BucketPolicy"}, ), ], )
def test_user_with_inline_policy_with_wildcard_resource_is_detected( user_with_wildcard_resource): rule = WildcardResourceRule(None) rule._config.stack_name = "not_allowed_stack" rule.all_cf_actions = set() result = rule.invoke(user_with_wildcard_resource) assert result.valid is False assert compare_lists_of_failures( result.failures, [ Failure( granularity="ACTION", reason= '"userWithInline" is using a wildcard resource in "somePolicy" for "s3:DeleteBucket"', risk_value="MEDIUM", rule="WildcardResourceRule", rule_mode="BLOCKING", actions={"s3:ListBucket", "s3:DeleteBucket"}, resource_ids={"userWithInline"}, ), Failure( granularity="ACTION", reason= '"userWithInline" is using a wildcard resource in "somePolicy" for "s3:ListBucket"', risk_value="MEDIUM", rule="WildcardResourceRule", rule_mode="BLOCKING", actions={"s3:ListBucket", "s3:DeleteBucket"}, resource_ids={"userWithInline"}, ), ], )
def test_remove_debug_rules(): original_failed_monitored_rules = [ Failure( rule="a", reason="something", rule_mode=RuleMode.MONITOR, granularity=RuleGranularity.STACK, risk_value=RuleRisk.HIGH, ), Failure( rule="b", reason="something", rule_mode=RuleMode.DEBUG, granularity=RuleGranularity.STACK, risk_value=RuleRisk.MEDIUM, ), Failure( rule="c", reason="something", rule_mode=RuleMode.MONITOR, granularity=RuleGranularity.STACK, risk_value=RuleRisk.LOW, ), ] list_with_no_debug_rules = [original_failed_monitored_rules[0], original_failed_monitored_rules[2]] processed_list = RuleProcessor.remove_debug_rules(rules=original_failed_monitored_rules) assert list_with_no_debug_rules == processed_list
def test_s3_read_plus_list(s3_read_plus_list): rule = S3BucketPublicReadAclAndListStatementRule(None) result = rule.invoke(s3_read_plus_list) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason="S3 Bucket S3BucketPolicy should not have a public read acl and list bucket statement", risk_value=RuleRisk.MEDIUM, rule="S3BucketPublicReadAclAndListStatementRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"S3BucketPolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason="S3 Bucket S3BucketPolicy2 should not have a public read acl and list bucket statement", risk_value=RuleRisk.MEDIUM, rule="S3BucketPublicReadAclAndListStatementRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"S3BucketPolicy2"}, ), ], )
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 expected_result_two_roles(): return [ Failure( rule="CrossAccountTrustRule", reason=( "RootRoleOne has forbidden cross-account trust relationship with " "arn:aws:iam::999999999:role/[email protected]" ), rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.MEDIUM, resource_ids={"RootRoleOne"}, actions=set(), granularity=RuleGranularity.RESOURCE, ), Failure( rule="CrossAccountTrustRule", reason=( "RootRoleTwo has forbidden cross-account trust relationship with " "arn:aws:iam::999999999:role/[email protected]" ), rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.MEDIUM, resource_ids={"RootRoleTwo"}, actions=set(), granularity=RuleGranularity.RESOURCE, ), ]
def test_failures_are_raised(bad_template): rule = GenericResourcePartialWildcardPrincipalRule(None) result = rule.invoke(bad_template) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "PolicyA should not allow wildcard in principals or account-wide principals (principal: 'arn:aws:iam::123445:12345*')", risk_value=RuleRisk.MEDIUM, rule="GenericResourcePartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"PolicyA"}, resource_types={"AWS::IAM::Policy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "PolicyA should not allow wildcard in principals or account-wide principals (principal: 'arn:aws:iam::123445:root')", risk_value=RuleRisk.MEDIUM, rule="GenericResourcePartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"PolicyA"}, resource_types={"AWS::IAM::Policy"}, ), ], )
def test_failures_for_correct_account_ids(intra_account_root_access): rule = GenericResourcePartialWildcardPrincipalRule( Config(aws_account_id="123456789012")) result = rule.invoke(intra_account_root_access) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "AccLoadBalancerAccessLogBucketPolicy should not allow wildcard in principals or account-wide principals (principal: 'arn:aws:iam::123456789012:root')", risk_value=RuleRisk.MEDIUM, rule="GenericResourcePartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"AccLoadBalancerAccessLogBucketPolicy"}, resource_types={"AWS::S3::BucketPolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "AccLoadBalancerAccessLogBucketPolicy should not allow wildcard in principals or account-wide principals (principal: '987654321012')", risk_value=RuleRisk.MEDIUM, rule="GenericResourcePartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"AccLoadBalancerAccessLogBucketPolicy"}, resource_types={"AWS::S3::BucketPolicy"}, ), ], )
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_failures_are_raised(bad_template): rule = EC2SecurityGroupIngressOpenToWorldRule(Config()) result = rule.invoke(bad_template) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "Port(s) 46 open to public IPs: (11.0.0.0/8) in security group 'securityGroupIngress1'", risk_value=RuleRisk.MEDIUM, rule="EC2SecurityGroupIngressOpenToWorldRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"securityGroupIngress1"}, resource_types={"AWS::EC2::SecurityGroupIngress"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "Port(s) 46 open to public IPs: (::/0) in security group 'securityGroupIngress2'", risk_value=RuleRisk.MEDIUM, rule="EC2SecurityGroupIngressOpenToWorldRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"securityGroupIngress2"}, resource_types={"AWS::EC2::SecurityGroupIngress"}, ), ], )
def test_failures_are_raised(bad_template): rule = GenericResourceWildcardPrincipalRule(None) result = rule.invoke(bad_template) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "PolicyA should not allow full wildcard '*', or wildcard in account ID like 'arn:aws:iam::*:12345' at 'somewhatrestricted:*'", risk_value=RuleRisk.MEDIUM, rule="GenericResourceWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"PolicyA"}, resource_types={"AWS::IAM::Policy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "PolicyA should not allow full wildcard '*', or wildcard in account ID like 'arn:aws:iam::*:12345' at 'arn:aws:iam::*:12345'", risk_value=RuleRisk.MEDIUM, rule="GenericResourceWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"PolicyA"}, resource_types={"AWS::IAM::Policy"}, ), ], )
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_sqs_policy_public(sqs_policy_public): rule = SQSQueuePolicyPublicRule(None) result = rule.invoke(sqs_policy_public) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "SQS Queue policy QueuePolicyPublic1 should not be public", risk_value=RuleRisk.HIGH, rule="SQSQueuePolicyPublicRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"QueuePolicyPublic1"}, resource_types={"AWS::SQS::QueuePolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "SQS Queue policy QueuePolicyPublic2 should not be public", risk_value=RuleRisk.HIGH, rule="SQSQueuePolicyPublicRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"QueuePolicyPublic2"}, resource_types={"AWS::SQS::QueuePolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "SQS Queue policy QueuePolicyPublic3 should not be public", risk_value=RuleRisk.HIGH, rule="SQSQueuePolicyPublicRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"QueuePolicyPublic3"}, resource_types={"AWS::SQS::QueuePolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "SQS Queue policy QueuePolicyPublic4 should not be public", risk_value=RuleRisk.HIGH, rule="SQSQueuePolicyPublicRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"QueuePolicyPublic4"}, resource_types={"AWS::SQS::QueuePolicy"}, ), ], )
def test_sqs_queue_with_wildcards(sqs_queue_with_wildcards): rule = SQSQueuePolicyWildcardActionRule(None) result = rule.invoke(sqs_queue_with_wildcards) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "The SQSQueuePolicy mysqspolicy1 should not allow a `*` action", risk_value=RuleRisk.MEDIUM, rule="SQSQueuePolicyWildcardActionRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"mysqspolicy1"}, resource_types={"AWS::SQS::QueuePolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "The SQSQueuePolicy mysqspolicy1b should not allow a `*` action", risk_value=RuleRisk.MEDIUM, rule="SQSQueuePolicyWildcardActionRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"mysqspolicy1b"}, resource_types={"AWS::SQS::QueuePolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "The SQSQueuePolicy mysqspolicy1c should not allow a `*` action", risk_value=RuleRisk.MEDIUM, rule="SQSQueuePolicyWildcardActionRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"mysqspolicy1c"}, resource_types={"AWS::SQS::QueuePolicy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "The SQSQueuePolicy mysqspolicy1d should not allow a `*` action", risk_value=RuleRisk.MEDIUM, rule="SQSQueuePolicyWildcardActionRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"mysqspolicy1d"}, resource_types={"AWS::SQS::QueuePolicy"}, ), ], )
def test_failures_are_raised(bad_template): rule = GenericResourcePartialWildcardPrincipalRule(None) result = rule.invoke(bad_template) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "PolicyA should not allow wildcard, account-wide or root in resource-id like 'arn:aws:iam::12345:root' at 'arn:aws:iam::123445:12345*'", risk_value=RuleRisk.MEDIUM, rule="GenericResourcePartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"PolicyA"}, resource_types={"AWS::IAM::Policy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "PolicyA should not allow wildcard, account-wide or root in resource-id like 'arn:aws:iam::12345:root' at 'arn:aws:iam::123445:root'", risk_value=RuleRisk.MEDIUM, rule="GenericResourcePartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"PolicyA"}, resource_types={"AWS::IAM::Policy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "PolicyA should not allow wildcard, account-wide or root in resource-id like 'arn:aws:iam::12345:root' at '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be'", risk_value=RuleRisk.MEDIUM, rule="GenericResourcePartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"PolicyA"}, resource_types={"AWS::IAM::Policy"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "PolicyA should not allow wildcard, account-wide or root in resource-id like 'arn:aws:iam::12345:root' at 'eb2fe74dc7e8125d8f8fcae89d90e6dfdecabf896e1a69d55e949b009fd95a97'", risk_value=RuleRisk.MEDIUM, rule="GenericResourcePartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"PolicyA"}, resource_types={"AWS::IAM::Policy"}, ), ], )
def test_can_whitelist_resource_from_any_stack_if_granularity_is_resource(): whitelist_for_all_stacks = { "S3CrossAccountTrustRule": { ".*": {"ProductionAccessTest"}, "otherstack": {"rolething"} } } config = Config(stack_name="abcd", rules=["S3CrossAccountTrustRule"], rule_to_resource_whitelist=whitelist_for_all_stacks) result = Result() failed_rules = [ Failure( rule="S3CrossAccountTrustRule", reason= "ProductionAccessTest has forbidden cross-account policy allow with 123456789 for an S3 bucket.", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"ProductionAccessTest"}, actions=None, granularity=RuleGranularity.RESOURCE, ), Failure( rule="S3CrossAccountTrustRule", reason= "This one isn't whitelisted because granularity is ACTION and not RESOURCE", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"ProductionAccessTest"}, actions=None, granularity=RuleGranularity.ACTION, ), ] result.failed_rules = failed_rules RuleProcessor.remove_failures_of_whitelisted_resources(config=config, result=result) assert result.failed_rules == [ Failure( rule="S3CrossAccountTrustRule", reason= "This one isn't whitelisted because granularity is ACTION and not RESOURCE", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"ProductionAccessTest"}, actions=None, granularity=RuleGranularity.ACTION, ) ]
def test_non_matching_filters_are_reported_normally(bad_template): mock_config = Config( rules=["EC2SecurityGroupIngressOpenToWorldRule"], aws_account_id="123456789", stack_name="mockstack", rules_filters=[ Filter( rule_mode=RuleMode.ALLOWED, eval={"eq": [{ "ref": "config.stack_name" }, "anotherstack"]}, rules={"EC2SecurityGroupIngressOpenToWorldRule"}, ) ], ) rules = [ DEFAULT_RULES.get(rule)(mock_config) for rule in mock_config.rules ] processor = RuleProcessor(*rules) result = processor.process_cf_template(bad_template, mock_config) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "Port(s) 46 open to public IPs: (11.0.0.0/8) in security group 'securityGroupIngress1'", risk_value=RuleRisk.MEDIUM, rule="EC2SecurityGroupIngressOpenToWorldRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"securityGroupIngress1"}, resource_types={"AWS::EC2::SecurityGroupIngress"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "Port(s) 46 open to public IPs: (::/0) in security group 'securityGroupIngress2'", risk_value=RuleRisk.MEDIUM, rule="EC2SecurityGroupIngressOpenToWorldRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"securityGroupIngress2"}, resource_types={"AWS::EC2::SecurityGroupIngress"}, ), ], )
def add_warning_to_result( self, result: Result, reason: str, granularity: Optional[RuleGranularity] = None, resource_ids: Optional[Set] = None, actions: Optional[Set] = None, risk_value: Optional[RuleRisk] = None, rule_mode: Optional[RuleMode] = None, context: Optional[Dict] = None, ): rule_mode = rule_mode or self.rule_mode risk_value = risk_value or self.risk_value for fltr in self.rule_config.filters: if fltr(**context): risk_value = fltr.risk_value or risk_value rule_mode = fltr.rule_mode or rule_mode if rule_mode not in (RuleMode.DISABLED, RuleMode.WHITELISTED): warning = Failure( rule=type(self).__name__, reason=reason, granularity=granularity or self.GRANULARITY, resource_ids=resource_ids, actions=actions, risk_value=risk_value, rule_mode=rule_mode, ) result.add_warning(warning)
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_policy_document_with_condition_is_ignored( iam_policy_with_wildcard_resource_and_wildcard_action_and_condition): rule = WildcardResourceRule(None) rule._config.stack_name = "stack3" rule.all_cf_actions = set() result = rule.invoke( iam_policy_with_wildcard_resource_and_wildcard_action_and_condition) assert result.valid assert result.failed_monitored_rules == [] assert compare_lists_of_failures( result.warnings, [ Failure( granularity="ACTION", reason= '"RolePolicy" is using a wildcard resource in "root" allowing all actions', risk_value="MEDIUM", rule="WildcardResourceRule", rule_mode="BLOCKING", actions={"*"}, resource_ids={"RolePolicy"}, ) ], )
def test_policy_document_with_wildcard_resource_and_wilcard_action_without_policy_name_is_detected( iam_policy_with_wildcard_resource_and_wilcard_action_without_policy_name, ): rule = WildcardResourceRule(None) rule._config.stack_name = "stack3" rule.all_cf_actions = set() result = rule.invoke( iam_policy_with_wildcard_resource_and_wilcard_action_without_policy_name ) assert result.valid is False assert compare_lists_of_failures( result.failures, [ Failure( granularity="ACTION", reason= '"RolePolicy" is using a wildcard resource allowing all actions', risk_value="MEDIUM", rule="WildcardResourceRule", rule_mode="BLOCKING", actions={"*"}, resource_ids={"RolePolicy"}, ) ], )
def test_generic_cross_account_for_opensearch_domain_different_principals( principal): rule = GenericCrossAccountTrustRule( Config(aws_account_id="123456789", aws_principals=["999999999"])) model = get_cfmodel_from( "rules/CrossAccountTrustRule/opensearch_domain_basic.yml").resolve( extra_params={"Principal": principal}) result = rule.invoke(model) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= f"TestDomain has forbidden cross-account with {principal}", risk_value=RuleRisk.MEDIUM, rule="GenericCrossAccountTrustRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"TestDomain"}, resource_types={"AWS::OpenSearchService::Domain"}, ) ], )
def test_remove_failures_from_whitelisted_actions_only_removes_action_granularity( mock_rule_to_action_whitelist): config = Config( stack_name="teststack", rules=["S3CrossAccountTrustRule"], rule_to_action_whitelist=mock_rule_to_action_whitelist, ) result = Result() failed_rules = [ Failure( rule="WildcardResourceRule", reason= "rolething is using a wildcard resource in BucketAccessPolicy", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"BucketAccessPolicy"}, actions={"s3:Get*"}, granularity=RuleGranularity.ACTION, ), Failure( rule="WildcardResourceRule", reason= "rolething is using a wildcard resource in BucketAccessPolicy", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids=set(), actions=set(), granularity=RuleGranularity.STACK, ), ] result.failed_rules = failed_rules RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result) assert result.failed_rules == [ Failure( rule="WildcardResourceRule", reason= "rolething is using a wildcard resource in BucketAccessPolicy", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids=set(), actions=set(), granularity=RuleGranularity.STACK, ) ]
def test_can_whitelist_action_from_any_stack_if_granularity_is_action(): whitelist_for_all_stacks = { "S3CrossAccountTrustRule": { ".*": {"s3:ListBucket"} } } config = Config(stack_name="abcd", rules=["S3CrossAccountTrustRule"], rule_to_action_whitelist=whitelist_for_all_stacks) result = Result() failed_rules = [ Failure( rule="S3CrossAccountTrustRule", reason= "ProductionAccessTest has forbidden cross-account policy allow with 123456789 for an S3 bucket.", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, actions={"s3:ListBucket"}, granularity=RuleGranularity.ACTION, ), Failure( rule="S3CrossAccountTrustRule", reason= "This one isn't whitelisted because granularity is STACK and not ACTION", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, actions=set(), granularity=RuleGranularity.STACK, ), ] result.failed_rules = failed_rules RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result) assert result.failed_rules == [ Failure( rule="S3CrossAccountTrustRule", reason= "This one isn't whitelisted because granularity is STACK and not ACTION", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, actions=set(), granularity=RuleGranularity.STACK, ) ]
def test_remove_failures_from_whitelisted_actions_uses_whitelist( mock_rule_to_action_whitelist): config = Config(stack_name="teststack", rules=["WildcardResourceRule"], rule_to_action_whitelist=mock_rule_to_action_whitelist) result = Result() result.failed_rules = [ Failure( rule="WildcardResourceRule", reason= "rolething is using a wildcard resource in BucketAccessPolicy", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"BucketAccessPolicy"}, actions={"s3:Get*"}, granularity=RuleGranularity.ACTION, ), Failure( rule="WildcardResourceRule", reason= "rolething is using a wildcard resource in DynamoAccessPolicy", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"DynamoAccessPolicy"}, actions={"dynamodb:Get"}, granularity=RuleGranularity.ACTION, ), ] RuleProcessor.remove_failures_of_whitelisted_actions(config=config, result=result) assert result.failed_rules == [ Failure( rule="WildcardResourceRule", reason= "rolething is using a wildcard resource in DynamoAccessPolicy", rule_mode=RuleMode.BLOCKING, risk_value=RuleRisk.HIGH, resource_ids={"DynamoAccessPolicy"}, actions={"dynamodb:Get"}, granularity=RuleGranularity.ACTION, ) ]
def test_result_addition(): failure1 = Failure( granularity=RuleGranularity.STACK, reason="reason1", risk_value=RuleRisk.HIGH, rule="rule1", rule_mode=RuleMode.BLOCKING, ) failure2 = Failure( granularity=RuleGranularity.STACK, reason="reason2", risk_value=RuleRisk.HIGH, rule="rule2", rule_mode=RuleMode.BLOCKING, ) result1 = Result(failures=[failure1]) result2 = Result(failures=[failure2]) assert result1 + result2 == Result(failures=[failure1, failure2])
def test_load_filters_work_with_several_rules(template_two_roles_dict, test_files_location): config = Config( rules=["CrossAccountTrustRule", "PartialWildcardPrincipalRule"], aws_account_id="123456789", stack_name="mockstack", ) config.load_rules_config_file( open( f"{test_files_location}/config/rules_config_CrossAccountTrustRule.py" )) config.add_filters_from_dir(f"{test_files_location}/filters") rules = [DEFAULT_RULES.get(rule)(config) for rule in config.rules] processor = RuleProcessor(*rules) result = processor.process_cf_template(template_two_roles_dict, config) assert not result.valid assert compare_lists_of_failures( result.failures, [ Failure( granularity=RuleGranularity.RESOURCE, reason= "RootRoleTwo has forbidden cross-account trust relationship with arn:aws:iam::999999999:role/[email protected]", risk_value=RuleRisk.MEDIUM, rule="CrossAccountTrustRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"RootRoleTwo"}, resource_types={"AWS::IAM::Role"}, ), Failure( granularity=RuleGranularity.RESOURCE, reason= "RootRoleTwo should not allow wildcard, account-wide or root in resource-id like 'arn:aws:iam::12345:root' at 'arn:aws:iam::123456789:root'", risk_value=RuleRisk.MEDIUM, rule="PartialWildcardPrincipalRule", rule_mode=RuleMode.BLOCKING, actions=None, resource_ids={"RootRoleTwo"}, resource_types={"AWS::IAM::Role"}, ), ], )
def test_result_addition(): failure1 = Failure( granularity=RuleGranularity.STACK, reason="reason1", risk_value="risk1", rule="rule1", rule_mode="mode1", ) failure2 = Failure( granularity=RuleGranularity.STACK, reason="reason2", risk_value="risk2", rule="rule2", rule_mode="mode2", ) monitored_failure1 = Failure( granularity=RuleGranularity.RESOURCE, reason="reason1", risk_value="risk1", rule="rule1", rule_mode="mode1", ) monitored_failure2 = Failure( granularity=RuleGranularity.RESOURCE, reason="reason2", risk_value="risk2", rule="rule2", rule_mode="mode2", ) result1 = Result(failed_rules=[failure1], failed_monitored_rules=[monitored_failure1]) result2 = Result(failed_rules=[failure2], failed_monitored_rules=[monitored_failure2]) assert result1 + result2 == Result( failed_rules=[failure1, failure2], failed_monitored_rules=[monitored_failure1, monitored_failure2])