Пример #1
0
    def test_stack_whitelist_joins_all_whitelisted_matching_stack_names(self):
        mock_whitelist = {
            "stackname": [
                "S3CrossAccountTrustRule",
            ],
            "notstackname": [
                "IAMRolesOverprivilegedRule",
            ],
            "stackname_withmorethings": [
                "CrossAccountTrustRule",
                "ManagedPolicyOnUserRule",
            ]

        }

        config = Config(
            project_name="project_mock",
            service_name="service_mock",
            stack_name="stackname_withmorethings",
            stack_whitelist=mock_whitelist,
            rules=DEFAULT_RULES.keys(),
        )

        whitelisted_rules = config.get_whitelisted_rules()

        assert set(whitelisted_rules) == {
            "CrossAccountTrustRule",
            "ManagedPolicyOnUserRule",
            "S3CrossAccountTrustRule",
        }
Пример #2
0
def test_load_filters_file_invalid_file(test_files_location):
    mock_rules = [
        "RuleThatUsesResourceAllowlist", "SecurityGroupOpenToWorldRule"
    ]
    config = Config(stack_name="test_stack", rules=mock_rules)

    with pytest.raises(ValidationError):
        config.add_filters_from_dir(f"{test_files_location}/invalid_filters")
Пример #3
0
def test_load_rules_config_file_no_file(test_files_location):
    mock_rules = [
        "RuleThatUsesResourceAllowlist", "SecurityGroupOpenToWorldRule"
    ]
    config = Config(stack_name="test_stack", rules=mock_rules)

    with pytest.raises(FileNotFoundError):
        config.load_rules_config_file(
            open(f"{test_files_location}/config/non_existing_file.py"))
Пример #4
0
def test_load_rules_config_file_invalid_file(test_files_location):
    mock_rules = [
        "RuleThatUsesResourceAllowlist", "SecurityGroupOpenToWorldRule"
    ]
    config = Config(stack_name="test_stack", rules=mock_rules)

    with pytest.raises(ValidationError):
        config.load_rules_config_file(
            open(f"{test_files_location}/config/rules_config_invalid.py"))
Пример #5
0
    def test_stack_to_resource_whitelist_rule_not_in_whitelist(self, mock_rule_to_resource_whitelist):
        mock_rules = ["RuleThatUsesResourceWhitelists", "SecurityGroupOpenToWorldRule"]
        config = Config(
            stack_name="test_stack",
            rules=mock_rules,
            stack_whitelist={},
            rule_to_resource_whitelist=mock_rule_to_resource_whitelist

        )
        assert config.get_whitelisted_resources("SecurityGroupOpenToWorldRule") == []
Пример #6
0
def test_stack_to_action_whitelist_stack_without_resources(
        mock_rule_to_action_whitelist):
    mock_rules = [
        "RuleThatUsesResourceWhitelists", "SecurityGroupOpenToWorldRule"
    ]
    config = Config(
        stack_name="stack_without_whitelisted_resources",
        rules=mock_rules,
        stack_whitelist={},
        rule_to_action_whitelist=mock_rule_to_action_whitelist,
    )
    assert config.get_whitelisted_actions("SecurityGroupOpenToWorldRule") == []
Пример #7
0
    def test_stack_to_action_whitelist_normal_behavior(self, mock_rule_to_action_whitelist):
        mock_rules = ["RuleThatUsesResourceWhitelists", "SecurityGroupOpenToWorldRule"]
        config = Config(
            stack_name="stack_2",
            rules=mock_rules,
            stack_whitelist={},
            rule_to_action_whitelist=mock_rule_to_action_whitelist

        )
        assert config.get_whitelisted_actions("RuleThatUsesActionWhitelists") == [
            "s3:GetItem",
            "kms:*",
            "dynamodb:CreateTable",
        ]
Пример #8
0
    def test_stack_to_resource_whitelist_normal_behavior(self, mock_rule_to_resource_whitelist):
        mock_rules = ["RuleThatUsesResourceWhitelists", "SecurityGroupOpenToWorldRule"]
        config = Config(
            stack_name="test_stack",
            rules=mock_rules,
            stack_whitelist={},
            rule_to_resource_whitelist=mock_rule_to_resource_whitelist

        )
        assert config.get_whitelisted_resources("RuleThatUsesResourceWhitelists") == [
            "resource_5",
            "resource_1",
            "another_resource",
        ]
def test_no_failures_are_raised(good_template):
    rule = EBSVolumeHasSSERule(Config(aws_account_id="123456789"))
    result = rule.invoke(good_template)

    assert result.valid
    assert len(result.failed_rules) == 0
    assert len(result.failed_monitored_rules) == 0
def test_generic_cross_account_rule_for_resources_with_set_principals(
        template, is_valid, failures):
    rule = GenericCrossAccountTrustRule(
        Config(aws_account_id="123456789", aws_principals=["999999999"]))
    result = rule.invoke(template)
    assert result.valid == is_valid
    assert compare_lists_of_failures(result.failures, failures)
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_s3_bucket_cross_account_from_aws_service_with_generic(
        s3_bucket_cross_account_from_aws_service):
    rule = GenericCrossAccountTrustRule(Config(aws_account_id="123456789"))
    result = rule.invoke(s3_bucket_cross_account_from_aws_service)

    assert result.valid
    assert compare_lists_of_failures(result.failures, [])
def test_sts_valid(template_valid_with_sts):
    rule = KMSKeyCrossAccountTrustRule(Config(aws_account_id="123456789", aws_principals=["999999999"]))
    result = rule.invoke(template_valid_with_sts)

    assert result.valid
    assert len(result.failed_rules) == 0
    assert len(result.failed_monitored_rules) == 0
Пример #14
0
def test_canonical_id_is_not_blocked(template_valid_with_canonical_id):
    rule = CrossAccountTrustRule(Config())
    result = rule.invoke(template_valid_with_canonical_id)

    assert result.valid
    assert len(result.failed_rules) == 0
    assert len(result.failed_monitored_rules) == 0
def test_filter_do_not_report_anything(single_security_group_one_cidr_ingress):
    mock_config = Config(
        rules=["EC2SecurityGroupMissingEgressRule"],
        aws_account_id="123456789",
        stack_name="mockstack",
        rules_config={
            "EC2SecurityGroupMissingEgressRule":
            RuleConfig(filters=[
                Filter(
                    rule_mode=RuleMode.WHITELISTED,
                    eval={"eq": [{
                        "ref": "config.stack_name"
                    }, "mockstack"]},
                )
            ], )
        },
    )
    rules = [
        DEFAULT_RULES.get(rule)(mock_config) for rule in mock_config.rules
    ]
    processor = RuleProcessor(*rules)
    result = processor.process_cf_template(
        single_security_group_one_cidr_ingress, mock_config)

    assert result.valid
Пример #16
0
    def remove_failures_of_whitelisted_resources(config: Config, result: Result):

        if not result.failed_rules:
            return

        clean_failures = []

        for failure in result.failed_rules:
            if failure.granularity != RuleGranularity.RESOURCE:
                clean_failures.append(failure)
                continue

            if not failure.resource_ids:
                logger.warning(f"Failure with resource granularity doesn't have resources: {failure}")
                continue

            whitelisted_resources = {
                resource
                for resource in failure.resource_ids
                if any(
                    [
                        re.match(whitelisted_resource_regex, resource)
                        for whitelisted_resource_regex in config.get_whitelisted_resources(failure.rule)
                    ]
                )
            }
            failure.resource_ids = failure.resource_ids - whitelisted_resources
            if failure.resource_ids:
                clean_failures.append(failure)

        result.failed_rules = clean_failures
Пример #17
0
def default_allow_all_config():
    return Config(
        rules=DEFAULT_RULES,
        aws_account_id="123456789012",
        stack_name="mockstack",
        rules_filters=[
            Filter(
                rule_mode=RuleMode.ALLOWED,
                eval={
                    "and": [
                        {
                            "exists": {
                                "ref": "config.stack_name"
                            }
                        },
                        {
                            "eq": [{
                                "ref": "config.stack_name"
                            }, "mockstack"]
                        },
                    ]
                },
                rules=set(DEFAULT_RULES.keys()),
            ),
        ],
    )
Пример #18
0
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_s3_bucket_cross_account_from_aws_service(s3_bucket_cross_account_from_aws_service):
    rule = S3CrossAccountTrustRule(Config(aws_account_id="123456789"))
    result = rule.invoke(s3_bucket_cross_account_from_aws_service)

    assert result.valid
    assert len(result.failed_rules) == 0
    assert len(result.failed_monitored_rules) == 0
Пример #20
0
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
Пример #21
0
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,
        ),
    ]
Пример #22
0
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"},
            ),
        ],
    )
Пример #23
0
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"},
            ),
        ],
    )
Пример #24
0
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_non_matching_filters_are_reported_normally(
        single_security_group_one_cidr_ingress):
    mock_config = Config(
        rules=["EC2SecurityGroupMissingEgressRule"],
        aws_account_id="123456789",
        stack_name="mockstack",
        rules_config={
            "EC2SecurityGroupMissingEgressRule":
            RuleConfig(filters=[
                Filter(rule_mode=RuleMode.WHITELISTED,
                       eval={
                           "eq": [{
                               "ref": "config.stack_name"
                           }, "anotherstack"]
                       })
            ], )
        },
    )
    rules = [
        DEFAULT_RULES.get(rule)(mock_config) for rule in mock_config.rules
    ]
    processor = RuleProcessor(*rules)
    result = processor.process_cf_template(
        single_security_group_one_cidr_ingress, mock_config)

    assert result.valid
    assert len(result.failed_rules) == 0
    assert len(result.failed_monitored_rules) == 1
    assert result.failed_monitored_rules[
        0].rule == "EC2SecurityGroupMissingEgressRule"
    assert (
        result.failed_monitored_rules[0].reason ==
        "Missing egress rule in sg means all traffic is allowed outbound. Make this explicit if it is desired configuration"
    )
def test_s3_bucket_cross_account_for_current_account_with_generic(
        s3_bucket_cross_account):
    rule = GenericCrossAccountTrustRule(Config(aws_account_id="987654321"))
    result = rule.invoke(s3_bucket_cross_account)

    assert result.valid
    assert compare_lists_of_failures(result.failures, [])
Пример #27
0
def test_kms_cross_account_success(principal):
    rule = KMSKeyCrossAccountTrustRule(
        Config(aws_account_id="123456789", aws_principals=["999999999"]))
    model = get_cfmodel_from("rules/CrossAccountTrustRule/kms_basic.yml"
                             ).resolve(extra_params={"Principal": principal})
    result = rule.invoke(model)
    assert result.valid
def test_filter_works_as_expected(template_two_roles_dict, expected_result_two_roles):
    config = Config(
        rules=["CrossAccountTrustRule"],
        aws_account_id="123456789",
        stack_name="mockstack",
        rules_config={
            "CrossAccountTrustRule": RuleConfig(
                filters=[
                    Filter(
                        rule_mode=RuleMode.WHITELISTED,
                        eval={
                            "and": [
                                {"eq": [{"ref": "config.stack_name"}, "mockstack"]},
                                {"eq": [{"ref": "logical_id"}, "RootRoleOne"]},
                            ]
                        },
                    )
                ],
            )
        },
    )
    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 result.failed_rules[0] == expected_result_two_roles[-1]
Пример #29
0
    def remove_failures_of_whitelisted_actions(config: Config, result: Result):

        if not result.failed_rules:
            return

        clean_failures = []

        for failure in result.failed_rules:
            if failure.granularity != RuleGranularity.ACTION:
                clean_failures.append(failure)
                continue

            if not failure.actions:
                logger.warning(f"Failure with action granularity doesn't have actions: {failure}")
                continue

            whitelisted_actions = {
                action
                for action in failure.actions
                if any(
                    [
                        re.match(whitelisted_action_regex, action)
                        for whitelisted_action_regex in config.get_whitelisted_actions(failure.rule)
                    ]
                )
            }
            failure.actions = failure.actions - whitelisted_actions
            if failure.actions:
                clean_failures.append(failure)

        result.failed_rules = clean_failures
Пример #30
0
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,
        )
    ]