Skip to content

util.arns

util.arns

Functions to use for parsing ARNs, matching ARN types, and getting the right fragment/component from an ARN string,

arn_has_colons(arn)

Given an ARN, determine if the ARN has colons in it. Just useful for the hacky methods for parsing ARN namespaces. See http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more details on ARN namespacing.

Source code in policy_sentry/util/arns.py
 98
 99
100
101
102
103
104
105
def arn_has_colons(arn):
    """Given an ARN, determine if the ARN has colons in it. Just useful for the hacky methods for
    parsing ARN namespaces. See
    http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more details on ARN namespacing."""
    if arn.count(":") > 0:
        return True
    else:
        return False

arn_has_slash(arn)

Given an ARN, determine if the ARN has a stash in it. Just useful for the hacky methods for parsing ARN namespaces. See http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more details on ARN namespacing.

Source code in policy_sentry/util/arns.py
87
88
89
90
91
92
93
94
def arn_has_slash(arn):
    """Given an ARN, determine if the ARN has a stash in it. Just useful for the hacky methods for
    parsing ARN namespaces. See
    http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more details on ARN namespacing."""
    if arn.count("/") > 0:
        return True
    else:
        return False

does_arn_match(arn_to_test, arn_in_database)

Given two ARNs, determine if they have the same resource type.

Parameters:

Name Type Description Default
arn_to_test

ARN provided by user

required
arn_in_database

Raw ARN that exists in the policy sentry database

required

Returns:

Type Description
Boolean

result of whether or not the ARNs match

Source code in policy_sentry/util/arns.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
def does_arn_match(arn_to_test, arn_in_database):
    """
    Given two ARNs, determine if they have the same resource type.

    Arguments:
        arn_to_test: ARN provided by user
        arn_in_database: Raw ARN that exists in the policy sentry database

    Returns:
        Boolean: result of whether or not the ARNs match
    """
    score = 0
    # arn_in_database = 'arn:aws:ssm:${Region}:${Account}:parameter/${FullyQualifiedParameterName}'
    # arn_to_test = 'arn:aws:ssm:us-east-1:123456789012:parameter/test'
    exclusion_list = ["${ObjectName}"]
    if arn_in_database == "*":
        return False
    else:
        resource_string_arn_in_database = get_resource_string(arn_in_database)
        resource_string_arn_to_test = get_resource_string(arn_to_test)
        resource_type_arn_in_database = parse_arn_for_resource_type(arn_in_database)
        resource_type_arn_to_test = parse_arn_for_resource_type(arn_to_test)
        if get_service_from_arn(arn_in_database) != get_service_from_arn(arn_to_test):
            score += 10
            return False
        if resource_type_arn_to_test in (resource_type_arn_in_database, '*'):
            return True
        else:
            score += 1
        if (
            resource_string_arn_in_database.count("/") > 0
            and resource_string_arn_to_test.count("/") > 0
        ):
            # Example: SSM `parameter/`
            if get_resource_from_arn(arn_in_database) != get_resource_from_arn(
                arn_to_test
            ):
                # Some exclusions, like ObjectId for S3 buckets
                if get_resource_path_from_arn(arn_in_database) in exclusion_list:
                    return True
                else:
                    return False
    return score < 1

get_account_from_arn(arn)

Given an ARN, return the account ID in the ARN, if it is available. In certain cases like S3 it is not

Source code in policy_sentry/util/arns.py
61
62
63
64
65
66
67
68
69
def get_account_from_arn(arn):
    """Given an ARN, return the account ID in the ARN, if it is available. In certain cases like S3 it is not"""
    result = parse_arn(arn)
    # Support S3 buckets with no values under account
    if result["account"] is None:
        result = ""
    else:
        result = result["account"]
    return result

get_partition_from_arn(arn)

Given an ARN string, return the partition string. This is usually aws unless you are in C2S or AWS GovCloud.

Source code in policy_sentry/util/arns.py
37
38
39
40
41
def get_partition_from_arn(arn):
    """Given an ARN string, return the partition string. This is usually `aws` unless you are in C2S or
    AWS GovCloud."""
    result = parse_arn(arn)
    return result["partition"]

get_region_from_arn(arn)

Given an ARN, return the region in the ARN, if it is available. In certain cases like S3 it is not

Source code in policy_sentry/util/arns.py
50
51
52
53
54
55
56
57
58
def get_region_from_arn(arn):
    """Given an ARN, return the region in the ARN, if it is available. In certain cases like S3 it is not"""
    result = parse_arn(arn)
    # Support S3 buckets with no values under region
    if result["region"] is None:
        result = ""
    else:
        result = result["region"]
    return result

get_resource_from_arn(arn)

Given an ARN, parse it according to ARN namespacing and return the resource. See http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more details on ARN namespacing.

Source code in policy_sentry/util/arns.py
72
73
74
75
76
def get_resource_from_arn(arn):
    """Given an ARN, parse it according to ARN namespacing and return the resource. See
    http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more details on ARN namespacing."""
    result = parse_arn(arn)
    return result["resource"]

get_resource_path_from_arn(arn)

Given an ARN, parse it according to ARN namespacing and return the resource path. See http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more details on ARN namespacing.

Source code in policy_sentry/util/arns.py
79
80
81
82
83
def get_resource_path_from_arn(arn):
    """Given an ARN, parse it according to ARN namespacing and return the resource path. See
    http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more details on ARN namespacing."""
    result = parse_arn(arn)
    return result["resource_path"]

get_resource_string(arn)

Given an ARN, return the string after the account ID, no matter the ARN format.

Parameters:

Name Type Description Default
arn

An ARN, like arn:partition:service:region:account-id:resourcetype/resource

required

Returns:

Type Description
String

The resource string, like resourcetype/resource

Source code in policy_sentry/util/arns.py
108
109
110
111
112
113
114
115
116
117
118
119
def get_resource_string(arn):
    """
    Given an ARN, return the string after the account ID, no matter the ARN format.

    Arguments:
        arn: An ARN, like `arn:partition:service:region:account-id:resourcetype/resource`
    Return:
        String: The resource string, like `resourcetype/resource`
    """
    split_arn = arn.split(":")
    resource_string = ":".join(split_arn[5:])
    return resource_string

get_service_from_arn(arn)

Given an ARN string, return the service

Source code in policy_sentry/util/arns.py
44
45
46
47
def get_service_from_arn(arn):
    """Given an ARN string, return the service """
    result = parse_arn(arn)
    return result["service"]

parse_arn(arn)

Given an ARN, split up the ARN into the ARN namespacing schema dictated by the AWS docs.

Source code in policy_sentry/util/arns.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def parse_arn(arn):
    """
    Given an ARN, split up the ARN into the ARN namespacing schema dictated by the AWS docs.
    """
    elements = arn.split(":", 5)
    result = {
        "arn": elements[0],
        "partition": elements[1],
        "service": elements[2],
        "region": elements[3],
        "account": elements[4],
        "resource": elements[5],
        "resource_path": None,
    }
    if "/" in result["resource"]:
        result["resource"], result["resource_path"] = result["resource"].split("/", 1)
    elif ":" in result["resource"]:
        result["resource"], result["resource_path"] = result["resource"].split(":", 1)
    return result

parse_arn_for_resource_type(arn)

Parses the resource string (resourcetype/resource and other variants) and grab the resource type.

Parameters:

Name Type Description Default
arn

The resource string to parse, like resourcetype/resource

required

Returns:

Type Description
String

The resource type, like bucket or object

Source code in policy_sentry/util/arns.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def parse_arn_for_resource_type(arn):
    """
    Parses the resource string (resourcetype/resource and other variants) and grab the resource type.

    Arguments:
        arn: The resource string to parse, like `resourcetype/resource`
    Return:
        String: The resource type, like `bucket` or `object`
    """
    split_arn = arn.split(":")
    # Resource string will equal:
    #     Case 1: resource
    #     Case 2: resourcetype/resource
    #     Case 3: resourcetype/resource/qualifier
    #     Case 4: resourcetype/resource:qualifier
    #     Case 5: resourcetype:resource
    #     Case 6: resourcetype:resource:qualifier
    resource_string = ":".join(split_arn[5:])
    split_resource = re.split("/|:", resource_string)
    if len(split_resource) == 1:
        # logger.debug(f"split_resource length is 1: {str(split_resource)}")
        pass
    elif len(split_resource) > 1:
        return split_resource[0]