Ejemplo n.º 1
0
    def test_does_arn_match_rds(self):
        arns_to_test = [
            "arn:${Partition}:rds:${Region}:${Account}:cluster:${DbClusterInstanceName}",
            "arn:${Partition}:rds:${Region}:${Account}:cluster-endpoint:${DbClusterEndpoint}",
            "arn:${Partition}:rds:${Region}:${Account}:cluster-pg:${ClusterParameterGroupName}",
            "arn:${Partition}:rds:${Region}:${Account}:cluster-snapshot:${ClusterSnapshotName}",
            "arn:${Partition}:rds:${Region}:${Account}:es:${SubscriptionName}",
            "arn:${Partition}:rds:${Account}:global-cluster:${GlobalCluster}",
            "arn:${Partition}:rds:${Region}:${Account}:og:${OptionGroupName}",
            "arn:${Partition}:rds:${Region}:${Account}:pg:${ParameterGroupName}",
            "arn:${Partition}:rds:${Region}:${Account}:db-proxy:${DbProxyId}",
            "arn:${Partition}:rds:${Region}:${Account}:ri:${ReservedDbInstanceName}",
            "arn:${Partition}:rds:${Region}:${Account}:secgrp:${SecurityGroupName}",
            "arn:${Partition}:rds:${Region}:${Account}:snapshot:${SnapshotName}",
            "arn:${Partition}:rds:${Region}:${Account}:subgrp:${SubnetGroupName}",
            "arn:${Partition}:rds:${Region}:${Account}:target:${TargetId}",
            "arn:${Partition}:rds:${Region}:${Account}:target-group:${TargetGroupId}"
        ]
        arn_in_database = "arn:${Partition}:rds:${Region}:${Account}:db:${DbInstanceName}"
        for arn in arns_to_test:
            decision = does_arn_match(arn, arn_in_database)
            self.assertFalse(decision)

        arn_to_test = "arn:${Partition}:rds:${Region}:${Account}:cluster:${DbClusterInstanceName}"
        decision = does_arn_match(arn_to_test, arn_in_database)
        self.assertFalse(decision)

        arn_to_test = "arn:${Partition}:rds:${Region}:${Account}:db:${DbInstanceName}"
        arn_in_database = "arn:${Partition}:rds:${Region}:${Account}:db:${DbInstanceName}"
        decision = does_arn_match(arn_to_test, arn_in_database)
        self.assertTrue(decision)
Ejemplo n.º 2
0
    def test_dynamodb_arn_matching_gh_215(self):
        """test_dynamodb_arn_matching_gh_215: Validate fix for DynamoDB arn mismatch in GitHub issue #215"""
        index = "arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}/index/${IndexName}"
        stream = "arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}/stream/${StreamLabel}"
        table = "arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}"
        backup = "arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}/backup/${BackupName}"
        global_table = "arn:${Partition}:dynamodb::${Account}:global-table/${GlobalTableName}"

        this_arn = ARN("arn:aws:dynamodb:us-east-1:123456789123:table/mytable")
        self.assertTrue(this_arn.same_resource_type(table))
        result = this_arn.same_resource_type(index)
        self.assertFalse(result)
        result = this_arn.same_resource_type(stream)
        self.assertFalse(result)
        result = this_arn.same_resource_type(backup)
        self.assertFalse(result)
        result = this_arn.same_resource_type(global_table)
        self.assertFalse(result)

        this_arn = "arn:aws:dynamodb:us-east-1:123456789123:table/mytable"
        self.assertTrue(does_arn_match(this_arn, table))
        self.assertFalse(does_arn_match(this_arn, index))
        self.assertFalse(does_arn_match(this_arn, stream))
        self.assertFalse(does_arn_match(this_arn, backup))
        self.assertFalse(does_arn_match(this_arn, global_table))
Ejemplo n.º 3
0
    def test_does_arn_match_resource_wildcard(self):
        arn_to_test = "arn:${Partition}:rds:${Region}:${Account}:*:*"
        arn_in_database = "arn:${Partition}:rds:${Region}:${Account}:db:${DbInstanceName}"
        decision = does_arn_match(arn_to_test, arn_in_database)
        self.assertTrue(decision)

        # Make sure wrong service yields False
        arn_to_test = "arn:${Partition}:s3:${Region}:${Account}:*:*"
        arn_in_database = "arn:${Partition}:rds:${Region}:${Account}:db:${DbInstanceName}"
        decision = does_arn_match(arn_to_test, arn_in_database)
        self.assertFalse(decision)
Ejemplo n.º 4
0
    def add_by_arn_and_access_level(self,
                                    arn_list,
                                    access_level,
                                    conditions_block=None):
        """
        This adds the user-supplied ARN(s), service prefixes, access levels, and condition keys (if applicable) given
        by the user. It derives the list of IAM actions based on the user's requested ARNs and access levels.

        Arguments:
            arn_list: Just a list of resource ARNs.
            access_level: "Read", "List", "Tagging", "Write", or "Permissions management"
            conditions_block: Optionally, a condition block with one or more conditions
        """
        for arn in arn_list:
            service_prefix = get_service_from_arn(arn)
            service_action_data = get_action_data(service_prefix, "*")
            for service_prefix in service_action_data:
                for row in service_action_data[service_prefix]:
                    if (does_arn_match(arn, row["resource_arn_format"])
                            and row["access_level"] == access_level):
                        raw_arn_format = row["resource_arn_format"]
                        resource_type_name = get_resource_type_name_with_raw_arn(
                            raw_arn_format)
                        sid_namespace = create_policy_sid_namespace(
                            service_prefix, access_level, resource_type_name)
                        actions = get_actions_with_arn_type_and_access_level(
                            service_prefix, resource_type_name, access_level)
                        # Make supplied actions lowercase
                        # supplied_actions = [x.lower() for x in actions]
                        supplied_actions = actions.copy()
                        dependent_actions = get_dependent_actions(
                            supplied_actions)
                        # List comprehension to get all dependent actions that are not in the supplied actions.
                        dependent_actions = [
                            x for x in dependent_actions
                            if x not in supplied_actions
                        ]
                        if len(dependent_actions) > 0:
                            for dep_action in dependent_actions:
                                self.add_action_without_resource_constraint(
                                    dep_action)
                                # self.add_action_without_resource_constraint(
                                #     str.lower(dep_action)
                                # )

                        temp_sid_dict = {
                            "arn": [arn],
                            "service": service_prefix,
                            "access_level": access_level,
                            "arn_format": raw_arn_format,
                            "actions": actions,
                            "conditions": [],  # TODO: Add conditions
                        }
                        if sid_namespace in self.sids.keys():
                            # If the ARN already exists there, skip it.
                            if arn not in self.sids[sid_namespace]["arn"]:
                                self.sids[sid_namespace]["arn"].append(arn)
                        # If it did not exist before at all, create it.
                        else:
                            self.sids[sid_namespace] = temp_sid_dict
Ejemplo n.º 5
0
 def test_does_arn_match_case_6(self):
     # Case 6: arn:partition:service:region:account-id:resourcetype:resource:qualifier
     arn_to_test = (
         "arn:aws:states:region:account-id:execution:stateMachineName:executionName"
     )
     arn_in_database = "arn:${Partition}:states:${Region}:${Account}:execution:${StateMachineName}:${ExecutionId}"
     self.assertTrue(does_arn_match(arn_to_test, arn_in_database))
Ejemplo n.º 6
0
 def test_does_arn_match_case_1(self):
     # Case 1: arn:partition:service:region:account-id:resource
     arn_to_test = "arn:aws:codecommit:us-east-1:123456789012:MyDemoRepo"
     arn_in_database = (
         "arn:${Partition}:codecommit:${Region}:${Account}:${RepositoryName}"
     )
     self.assertTrue(does_arn_match(arn_to_test, arn_in_database))
Ejemplo n.º 7
0
    def add(self, db_session, arn_list_from_user, access_level):
        """
        This just adds the ARN, Service, and Access Level. ARN Format and Actions are not filled out.
        Example data can be found in the class ArnActionGroupTestCase in the testing folder.

        :param db_session: SQLAlchemy database session
        :param arn_list_from_user: Just a list of resource ARNs.
        :param access_level: "Read", "List", "Tagging", "Write", or "Permissions management"
        """
        for arn_from_user in arn_list_from_user:
            service = get_service_from_arn(arn_from_user)
            for row in db_session.query(ActionTable).filter(
                    ActionTable.service.like(service)):
                if does_arn_match(arn_from_user, row.resource_arn_format):
                    if row.access_level == access_level:
                        # If it's not a key in the dictionary, add it as a key
                        # and then add the item in the list
                        raw_arn_format = row.resource_arn_format
                        temp_arn_dict = {
                            'arn': arn_from_user,
                            'service': service,
                            'access_level': access_level,
                            'arn_format': raw_arn_format,
                            'actions': []
                        }

                        # If there is already an entry, skip it to avoid duplicates
                        # Otherwise, add it
                        if temp_arn_dict in self.arns:
                            continue
                        self.arns.append(copy.deepcopy(temp_arn_dict))
Ejemplo n.º 8
0
    def add_sts_actions(self, sts_actions):
        """
        To add STS actions to the output from special YAML section
        """
        if sts_actions:
            # Hard coded for this special case
            service_prefix = "sts"
            access_level = "Write"

            for action, arns in sts_actions.items():
                clean_action = action.replace(
                    '-', ''
                )  # Convention to follow adding dashes instead of CamelCase
                service_action_data = get_action_data(service_prefix,
                                                      clean_action)

                # Schema validation takes care of this, but just in case. No data returned for the action
                if not service_action_data:
                    raise Exception(
                        f"Could not find service action data for {service_prefix} - {clean_action}"
                    )

                for row in service_action_data[service_prefix]:
                    for arn in arns:
                        if not arn:  # skip the - '' situation
                            continue
                        if (does_arn_match(arn, row["resource_arn_format"])
                                and row["access_level"] == access_level):
                            raw_arn_format = row["resource_arn_format"]

                            # Each action will get its own namespace sts:AssumeRole -> AssumeRole
                            # -1 index is a neat trick if the colon ever goes away we won't get an index error.
                            sid_namespace = row["action"].split(':')[-1]

                            temp_sid_dict = {
                                "arn": [arn],
                                "service": service_prefix,
                                "access_level": access_level,
                                "arn_format": raw_arn_format,
                                "actions": [row["action"]],
                                "conditions": [],  # TODO: Add conditions
                            }

                        # Using a custom namespace and not gathering actions so no need to find
                        # dependent actions either, though we could do it here

                        if sid_namespace in self.sids.keys():
                            # If the ARN already exists there, skip it.
                            if arn not in self.sids[sid_namespace]["arn"]:
                                self.sids[sid_namespace]["arn"].append(arn)
                        else:
                            self.sids[sid_namespace] = temp_sid_dict
Ejemplo n.º 9
0
def get_matching_raw_arn(arn):
    """
    Given a user-supplied ARN, return the raw_arn since that is used as a unique identifier throughout this library

    :param arn: The user-supplied arn, like arn:aws:s3:::mybucket
    :return: The raw ARN stored in the database, like 'arn:${Partition}:s3:::${BucketName}'
    """
    result = None
    service_in_scope = get_service_from_arn(arn)
    # Determine which resource it applies to
    all_raw_arns_for_service = get_raw_arns_for_service(service_in_scope)
    # Get the raw ARN specific to the provided one
    for raw_arn in all_raw_arns_for_service:
        if does_arn_match(arn, raw_arn):
            result = raw_arn
    return result
Ejemplo n.º 10
0
def get_matching_raw_arns(arn):
    """
    Given a user-supplied ARN, return the list of raw_arns since that is used as a unique identifier throughout this library

    Arguments:
        arn: The user-supplied arn, like arn:aws:s3:::mybucket
    Returns:
        list(str): The list of raw ARNs stored in the database, like 'arn:${Partition}:s3:::${BucketName}'
    """
    result = []
    service_in_scope = get_service_from_arn(arn)
    # Determine which resource it applies to
    all_raw_arns_for_service = get_raw_arns_for_service(service_in_scope)
    # Get the raw ARN specific to the provided one
    for raw_arn in all_raw_arns_for_service:
        if does_arn_match(arn, raw_arn) and raw_arn not in result:
            result.append(raw_arn)
    return result
Ejemplo n.º 11
0
 def test_does_arn_match_case_5(self):
     # Case 5: arn:partition:service:region:account-id:resourcetype:resource
     arn_to_test = "arn:aws:states:region:account-id:stateMachine:stateMachineName"
     arn_in_database = "arn:${Partition}:states:${Region}:${Account}:stateMachine:${StateMachineName}"
     self.assertTrue(does_arn_match(arn_to_test, arn_in_database))
Ejemplo n.º 12
0
 def test_does_arn_match_case_4(self):
     # Case 4: arn:partition:service:region:account-id:resourcetype/resource:qualifier
     arn_to_test = "arn:aws:batch:region:account-id:job-definition/job-name:revision"
     arn_in_database = "arn:${Partition}:batch:${Region}:${Account}:job-definition/${JobDefinitionName}:${Revision}"
     self.assertTrue(does_arn_match(arn_to_test, arn_in_database))
Ejemplo n.º 13
0
 def test_does_arn_match_case_2(self):
     # Case 2: arn:partition:service:region:account-id:resourcetype/resource
     arn_to_test = "arn:aws:ssm:us-east-1:123456789012:parameter/test"
     arn_in_database = "arn:${Partition}:ssm:${Region}:${Account}:parameter/${FullyQualifiedParameterName}"
     self.assertTrue(does_arn_match(arn_to_test, arn_in_database))
Ejemplo n.º 14
0
 def test_does_arn_match_case_greengrass(self):
     # Undocumented case: AWS Greengrass: arn:aws:greengrass:${Region}:${Account}:/greengrass/definition/devices/${DeviceDefinitionId}/versions/${VersionId}
     arn_to_test = "arn:aws:greengrass:${Region}:${Account}:/greengrass/definition/devices/1234567/versions/1"
     arn_in_database = "arn:aws:greengrass:${Region}:${Account}:/greengrass/definition/devices/${DeviceDefinitionId}/versions/${VersionId}"
     self.assertTrue(does_arn_match(arn_to_test, arn_in_database))
Ejemplo n.º 15
0
 def test_does_arn_match_case_bucket(self):
     # Case 1: arn:partition:service:region:account-id:resource
     arn_to_test = "arn:aws:s3:::bucket_name"
     arn_in_database = "arn:${Partition}:s3:::${BucketName}"
     self.assertTrue(does_arn_match(arn_to_test, arn_in_database))
Ejemplo n.º 16
0
    def test_dynamo_db_non_paths(self):
        backup_arn = "arn:aws:dynamodb:us-east-1:123456789123:table/mytable/backup/mybackup"
        backup_raw_arn = "arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}/backup/${BackupName}"

        table_arn = "arn:aws:dynamodb:us-east-1:123456789123:table/mytable"
        table_raw_arn = "arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}"

        parameter_arn_with_path = "arn:aws:ssm:::parameter/dev/foo/bar*"
        parameter_arn_without_path = "arn:aws:ssm:::parameter/dev"
        parameter_raw_arn = "arn:${Partition}:ssm:${Region}:${Account}:parameter/${FullyQualifiedParameterName}"

        s3_object_with_path = "arn:aws:s3:::foo/bar/baz"
        s3_object_without_path = "arn:aws:s3:::foo/bar"

        s3_object_raw_arn = "arn:${Partition}:s3:::${BucketName}/${ObjectName}"
        s3_bucket_raw_arn = "arn:${Partition}:s3:::${BucketName}"

        ecr_raw_arn = "arn:${Partition}:ecr:${Region}:${Account}:repository/${RepositoryName}"
        ecr_arn_with_path = "arn:aws:ecr:*:*:repository/foo/bar"
        ecr_arn_without_path = "arn:aws:ecr:*:*:repository/foo"

        self.assertTrue(does_arn_match(backup_arn, backup_raw_arn))
        self.assertTrue(does_arn_match(table_arn, table_raw_arn))
        self.assertFalse(does_arn_match(table_arn, backup_raw_arn))
        self.assertFalse(does_arn_match(backup_arn, table_raw_arn))

        self.assertTrue(
            does_arn_match(parameter_arn_with_path, parameter_raw_arn))
        self.assertTrue(
            does_arn_match(parameter_arn_without_path, parameter_raw_arn))

        self.assertTrue(
            does_arn_match(s3_object_without_path, s3_object_raw_arn))
        self.assertTrue(does_arn_match(s3_object_with_path, s3_object_raw_arn))
        self.assertFalse(does_arn_match(s3_object_with_path,
                                        s3_bucket_raw_arn))
        self.assertFalse(
            does_arn_match(s3_object_without_path, s3_bucket_raw_arn))

        self.assertTrue(does_arn_match(ecr_arn_with_path, ecr_raw_arn))
        self.assertTrue(does_arn_match(ecr_arn_without_path, ecr_raw_arn))
Ejemplo n.º 17
0
#!/usr/bin/env python
from policy_sentry.util.arns import does_arn_match

if __name__ == '__main__':
    print(
        does_arn_match("arn:aws:s3:::bucket_name",
                       "arn:${Partition}:s3:::${BucketName}"))
    print(
        does_arn_match(
            "arn:aws:codecommit:us-east-1:123456789012:MyDemoRepo",
            "arn:${Partition}:codecommit:${Region}:${Account}:${RepositoryName}"
        ))
    print(
        does_arn_match(
            "arn:aws:ssm:us-east-1:123456789012:parameter/test",
            "arn:${Partition}:ssm:${Region}:${Account}:parameter/${FullyQualifiedParameterName}"
        ))
    print(
        does_arn_match(
            "arn:aws:batch:region:account-id:job-definition/job-name:revision",
            "arn:${Partition}:batch:${Region}:${Account}:job-definition/${JobDefinitionName}:${Revision}"
        ))
    print(
        does_arn_match(
            "arn:aws:states:region:account-id:stateMachine:stateMachineName",
            "arn:${Partition}:states:${Region}:${Account}:stateMachine:${StateMachineName}"
        ))
    print(
        does_arn_match(
            "arn:aws:states:region:account-id:execution:stateMachineName:executionName",
            "arn:${Partition}:states:${Region}:${Account}:execution:${StateMachineName}:${ExecutionId}"