def test_admin_can_do_anything(self): graph = build_graph_with_one_admin() principal = graph.nodes[0] self.assertTrue(local_check_authorization(principal, 'iam:PutUserPolicy', '*', {})) self.assertTrue(local_check_authorization(principal, 'iam:PutUserPolicy', principal.arn, {})) self.assertTrue(local_check_authorization(principal, 'iam:CreateRole', '*', {})) self.assertTrue(local_check_authorization(principal, 'sts:AssumeRole', '*', {}))
def gen_os_lpe_finding(graph: Graph) -> List[Finding]: """Generates findings related to risk of SSM permissions being misconfigured (local priv-esc on the host)""" result = [] affected_roles = [] for node in graph.nodes: if ':role/' in node.arn and node.instance_profile is not None and len( node.instance_profile) > 0: # https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-setting-up-messageAPIs.html if query_interface.local_check_authorization( node, 'ssmmessages:*', '*', {}): if query_interface.local_check_authorization( node, 'ssm:SendCommand', '*', {}): affected_roles.append(node) elif query_interface.local_check_authorization( node, 'ssm:StartSession', '*', {}): affected_roles.append(node) if len(affected_roles) > 0: description_preamble = 'In AWS EC2, instances can be assigned instance profiles. An instance profile is tied ' \ 'to a single IAM Role. The instance profile can be used to access the AWS API with ' \ 'the permissions of the IAM Role. If the IAM Role has permission to call certain SSM ' \ 'actions such as `ssm:SendCommand` or `ssm:StartSession`, the instance profile ' \ 'can be used to invoke commands on other instances or itself.' \ '\n' \ '\n' \ 'Because the SSM Agent runs with the highest permissions on their hosts ' \ '(root or SYSTEM), this can be a way for attackers to pivot and compromise other ' \ 'instances, or escalate privileges on the local machine. The following IAM Roles ' \ 'are attached to at least one instance profile and have permissions with the ' \ 'aforementioned risk:' \ '\n' \ '\n' description_body = '' for node in affected_roles: description_body += '* {}\n'.format(node.searchable_name()) result.append( Finding( 'IAM Roles With Unsafe SSM Permissions' if len(affected_roles) > 1 else 'IAM Role With Unsafe SSM Permissions', 'Medium', 'If an attacker gains access to an instance with the unsafe permissions, they could escalate privileges ' 'on its current host or compromise other hosts.', description_preamble + description_body, 'Reduce the scope of permissions attached to the noted IAM Role(s).' )) return result
def test_permissions_boundary_no_resource_policy(self): """In the case of no resource policy, the effective permissions are the "intersection" of the caller's identity policies + the boundary policy. Both the user's identity policies + boundary policy must permit the API call. A matching deny statement in either set will deny the whole call. """ boundary = Policy( 'arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess', 'AmazonS3ReadOnlyAccess', { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:Get*", "s3:List*" ], "Resource": "*", "Effect": "Allow" } ] } ) iam_user_1 = _build_user_with_policy( { 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*' }] }, 'admin_policy', 'asdf1', '1' ) iam_user_1.permissions_boundary = boundary self.assertTrue( local_check_authorization(iam_user_1, 's3:GetObject', 'arn:aws:s3:::bucket/object', {}) ) self.assertFalse( local_check_authorization(iam_user_1, 's3:PutObject', 'arn:aws:s3:::bucket/object', {}) )
def return_edges(self, nodes: List[Node], output: io.StringIO = os.devnull, debug: bool = False) -> List[Edge]: """Fulfills expected method return_edges. If session object is None, runs checks in offline mode.""" result = [] for node_source in nodes: for node_destination in nodes: # skip self-access checks if node_source == node_destination: continue # check if source is an admin, if so it can access destination but this is not tracked via an Edge if node_source.is_admin: continue # check if destination is a role with an instance profile if ':role/' not in node_destination.arn or node_destination.instance_profile is None: continue # at this point, we make an assumption that some instance is operating with the given instance profile # we assume if the role can call ssmmessages:CreateControlChannel, anyone with ssm perms can access it if not query_interface.local_check_authorization( node_destination, 'ssmmessages:CreateControlChannel', '*', {}, False): continue # so if source can call ssm:SendCommand or ssm:StartSession, it's an edge cmd_auth_res, mfa_res_1 = query_interface.local_check_authorization_handling_mfa( node_source, 'ssm:SendCommand', '*', {}, False) if cmd_auth_res: reason = 'can call ssm:SendCommand to access an EC2 instance with access to' if mfa_res_1: reason = '(Requires MFA) ' + reason result.append(Edge(node_source, node_destination, reason)) sesh_auth_res, mfa_res_2 = query_interface.local_check_authorization_handling_mfa( node_source, 'ssm:StartSession', '*', {}, False) if sesh_auth_res: reason = 'can call ssm:StartSession to access an EC2 instance with access to' if mfa_res_2: reason = '(Requires MFA) ' + reason result.append(Edge(node_source, node_destination, reason)) for edge in result: output.write("Found new edge: {}\n".format(edge.describe_edge())) return result
def generate_edges_locally( nodes: List[Node], scps: Optional[List[List[dict]]] = None) -> List[Edge]: """Generates and returns Edge objects. It is possible to use this method if you are operating offline (infra-as-code). """ result = [] for node_destination in nodes: # check if destination is a role with an instance profile if ':role/' not in node_destination.arn or node_destination.instance_profile is None: continue # check if the destination can be assumed by EC2 sim_result = resource_policy_authorization( 'ec2.amazonaws.com', arns.get_account_id(node_destination.arn), node_destination.trust_policy, 'sts:AssumeRole', node_destination.arn, {}, ) if sim_result != ResourcePolicyEvalResult.SERVICE_MATCH: continue # EC2 wasn't auth'd to assume the role # at this point, we make an assumption that some instance is operating with the given instance profile # we assume if the role can call ssmmessages:CreateControlChannel, anyone with ssm perms can access it if not query_interface.local_check_authorization( node_destination, 'ssmmessages:CreateControlChannel', '*', {}): continue for node_source in nodes: # skip self-access checks if node_source == node_destination: continue # check if source is an admin, if so it can access destination but this is not tracked via an Edge if node_source.is_admin: continue # so if source can call ssm:SendCommand or ssm:StartSession, it's an edge cmd_auth_res, mfa_res_1 = query_interface.local_check_authorization_handling_mfa( node_source, 'ssm:SendCommand', '*', {}, ) if cmd_auth_res: reason = 'can call ssm:SendCommand to access an EC2 instance with access to' if mfa_res_1: reason = '(Requires MFA) ' + reason result.append( Edge(node_source, node_destination, reason, 'SSM')) sesh_auth_res, mfa_res_2 = query_interface.local_check_authorization_handling_mfa( node_source, 'ssm:StartSession', '*', {}, ) if sesh_auth_res: reason = 'can call ssm:StartSession to access an EC2 instance with access to' if mfa_res_2: reason = '(Requires MFA) ' + reason result.append( Edge(node_source, node_destination, reason, 'SSM')) return result
def test_arn_condition(self): """ Validate the following conditions are correctly handled: ArnEquals, ArnLike, ArnNotEquals, ArnNotLike. Note, ArnEquals and ArnLike have the same behavior, as well as ArnNotEquals and ArnNotLike Validated against the Simulator API TODO: Check on ForAnyValue and ForAllValues """ # ArnEquals (and ArnLike) testing: no wildcards test_arn_equals = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'ArnEquals': { 'aws:SourceArn': 'arn:aws:iam::000000000000:user/test1', } } }] }) self.assertTrue( local_check_authorization( test_arn_equals, 'iam:CreateUser', '*', {'aws:SourceArn': 'arn:aws:iam::000000000000:user/test1'})) self.assertFalse( local_check_authorization( test_arn_equals, 'iam:CreateUser', '*', {'aws:SourceArn': 'arn:aws:iam::000000000000:user/test2'})) # ArnEquals (and ArnLike) testing: wildcards test_arn_equals_wild = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'ArnEquals': { 'aws:SourceArn': 'arn:aws:iam::*:user/test1', } } }] }) self.assertTrue( local_check_authorization( test_arn_equals_wild, 'iam:CreateUser', '*', {'aws:SourceArn': 'arn:aws:iam::000000000000:user/test1'}, )) self.assertFalse( local_check_authorization( test_arn_equals_wild, 'iam:CreateUser', '*', {'aws:SourceArn': 'arn:aws:iam::000000000000:user/test2'}, )) # ArnNotEquals (and ArnNotLike) testing: wildcards test_arn_equals_wild = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'ArnNotLike': { 'aws:SourceArn': 'arn:aws:iam::*:user/test1', } } }] }) self.assertFalse( local_check_authorization( test_arn_equals_wild, 'iam:CreateUser', '*', {'aws:SourceArn': 'arn:aws:iam::000000000000:user/test1'}, )) self.assertTrue( local_check_authorization( test_arn_equals_wild, 'iam:CreateUser', '*', {'aws:SourceArn': 'arn:aws:iam::000000000000:user/test2'}, )) self.assertFalse( local_check_authorization( test_arn_equals_wild, 'iam:CreateUser', '*', {'aws:SourceArn': 'test2'}, ))
def test_local_mfa_handling(self): mfa_policy = Policy( arn='arn:aws:iam::000000000000:policy/mfa_policy', name='mfa_policy', policy_doc={ "Version": "2012-10-17", "Statement": [{ "Sid": "AllowManageOwnUserMFA", "Effect": "Allow", "Action": [ "iam:DeactivateMFADevice", "iam:EnableMFADevice", "iam:GetUser", "iam:ListMFADevices", "iam:ResyncMFADevice" ], "Resource": "arn:aws:iam::*:user/${aws:username}" }, { "Sid": "DenyAllExceptListedIfNoMFA", "Effect": "Deny", "NotAction": [ "iam:CreateVirtualMFADevice", "iam:EnableMFADevice", "iam:GetUser", "iam:ListMFADevices", "iam:ListVirtualMFADevices", "iam:ResyncMFADevice", "sts:GetSessionToken" ], "Resource": "*", "Condition": { "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" } } }] }) s3_policy = Policy( 'arn:aws:iam::000000000000:policy/s3access', 's3access', { "Version": "2012-10-17", "Statement": [{ "Sid": "AllowViewAccountInfo", "Effect": "Allow", "Action": "s3:ListAllMyBuckets", "Resource": "*" }] }) test_node = Node('arn:aws:iam::000000000000:user/uses_mfa', 'AIDA00000000000000000', [mfa_policy, s3_policy], [], None, None, 1, False, False, None, True, None) print(mfa_policy.to_dictionary()) print(s3_policy.to_dictionary()) print(test_node.to_dictionary()) # Test that lack of MFA conditions is not allowed (note the function call diff) # This matches policy sim feedback auth_result = local_check_authorization(test_node, 's3:ListAllMyBuckets', '*', {}) self.assertFalse(auth_result) # Test that MFA set to false is disallowed auth_result, mfa_result = local_check_authorization_handling_mfa( test_node, 's3:ListAllMyBuckets', '*', {'aws:MultiFactorAuthPresent': 'false'}) self.assertFalse(auth_result) self.assertFalse(mfa_result) # Test that testing both with and without MFA yields correct results auth_result, mfa_result = local_check_authorization_handling_mfa( test_node, 's3:ListAllMyBuckets', '*', {}) self.assertTrue(auth_result) self.assertTrue(mfa_result) # Test that iam:EnableMFADevice is allowed despite lack of MFA auth_result, mfa_result = local_check_authorization_handling_mfa( test_node, 'iam:EnableMFADevice', 'arn:aws:iam::000000000000:user/uses_mfa', {}) self.assertFalse(mfa_result) self.assertTrue(auth_result)
def test_documented_ddb_authorization_behavior(self): test_node = _build_user_with_policy({ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": "dynamodb:GetItem", "Resource": "arn:aws:dynamodb:*:*:table/Thread", "Condition": { "ForAllValues:StringEquals": { "dynamodb:Attributes": ["ID", "Message", "Tags"] } } }] }) self.assertTrue( local_check_authorization( test_node, 'dynamodb:GetItem', 'arn:aws:dynamodb:us-west-2:000000000000:table/Thread', {'dynamodb:attributes': ['ID', 'Message', 'Tags']})) self.assertTrue( local_check_authorization( test_node, 'dynamodb:GetItem', 'arn:aws:dynamodb:us-west-2:000000000000:table/Thread', {'dynamodb:attributes': ['ID', 'Message']})) self.assertTrue( local_check_authorization( test_node, 'dynamodb:GetItem', 'arn:aws:dynamodb:us-west-2:000000000000:table/Thread', {})) self.assertFalse( local_check_authorization( test_node, 'dynamodb:GetItem', 'arn:aws:dynamodb:us-west-2:000000000000:table/Thread', {'dynamodb:Attributes': ['ID', 'Message', 'Tags', 'Password'] })) test_node = _build_user_with_policy({ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": "dynamodb:GetItem", "Resource": "arn:aws:dynamodb:*:*:table/Thread", "Condition": { "ForAnyValue:StringEquals": { "dynamodb:Attributes": ["ID", "Message", "Tags"] } } }] }) self.assertTrue( local_check_authorization( test_node, 'dynamodb:GetItem', 'arn:aws:dynamodb:us-west-2:000000000000:table/Thread', {'dynamodb:Attributes': ['ID', 'Message', 'Tags']})) self.assertTrue( local_check_authorization( test_node, 'dynamodb:GetItem', 'arn:aws:dynamodb:us-west-2:000000000000:table/Thread', {'dynamodb:Attributes': ['Tags', 'Password']})) self.assertFalse( local_check_authorization( test_node, 'dynamodb:GetItem', 'arn:aws:dynamodb:us-west-2:000000000000:table/Thread', {'dynamodb:Attributes': ['Password']})) self.assertFalse( local_check_authorization( test_node, 'dynamodb:GetItem', 'arn:aws:dynamodb:us-west-2:000000000000:table/Thread', {}))
def test_bool_condition_handling(self): """ Validate the following conditions are handled: * Bool TODO: Check on ForAnyValue and ForAllValues """ # Bool: true test_node_true = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'Bool': { 'aws:SecureTransport': 'true' } } }] }) self.assertTrue( local_check_authorization(test_node_true, 'iam:CreateUser', '*', {'aws:SecureTransport': 'true'})) self.assertTrue( local_check_authorization(test_node_true, 'iam:CreateUser', '*', {'aws:SecureTransport': 'True'})) self.assertFalse( local_check_authorization(test_node_true, 'iam:CreateUser', '*', {'aws:SecureTransport': 'tru'})) self.assertFalse( local_check_authorization(test_node_true, 'iam:CreateUser', '*', {'aws:SecureTransport': ''})) self.assertFalse( local_check_authorization(test_node_true, 'iam:CreateUser', '*', {'aws:SecureTransport': 'false'})) # Bool: false test_node_false = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'Bool': { 'aws:SecureTransport': 'false' } } }] }) self.assertTrue( local_check_authorization(test_node_false, 'iam:CreateUser', '*', {'aws:SecureTransport': 'false'})) self.assertFalse( local_check_authorization(test_node_false, 'iam:CreateUser', '*', {'aws:SecureTransport': 'true'})) self.assertTrue( local_check_authorization( test_node_false, 'iam:CreateUser', '*', {'aws:SecureTransport': 'asdf'})) # policy sim behavior self.assertTrue( local_check_authorization(test_node_false, 'iam:CreateUser', '*', {'aws:SecureTransport': 't'}))
def test_ipaddress_condition_handling(self): """ Validate the following conditions are handled: * IpAddress * NotIpAddress TODO: Check on ForAnyValue and ForAllValues """ # IpAddress: single IP test_node_ipaddress = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'IpAddress': { 'aws:SourceIp': '10.0.0.1' } } }] }) self.assertTrue( local_check_authorization(test_node_ipaddress, 'iam:CreateUser', '*', {'aws:SourceIp': '10.0.0.1'})) self.assertFalse( local_check_authorization(test_node_ipaddress, 'iam:CreateUser', '*', {'aws:SourceIp': '10.0.0.2'})) # IpAddress: IP range test_node_ipaddress = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'IpAddress': { 'aws:SourceIp': '10.0.0.0/8' } } }] }) self.assertTrue( local_check_authorization(test_node_ipaddress, 'iam:CreateUser', '*', {'aws:SourceIp': '10.0.0.1'})) self.assertFalse( local_check_authorization(test_node_ipaddress, 'iam:CreateUser', '*', {'aws:SourceIp': '127.0.0.1'})) # IpAddress: IP ranges test_node_ipaddress = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'IpAddress': { 'aws:SourceIp': ['10.0.0.0/8', '127.0.0.0/8'] } } }] }) self.assertTrue( local_check_authorization(test_node_ipaddress, 'iam:CreateUser', '*', {'aws:SourceIp': '10.0.0.1'})) self.assertTrue( local_check_authorization(test_node_ipaddress, 'iam:CreateUser', '*', {'aws:SourceIp': '127.0.0.1'})) self.assertFalse( local_check_authorization(test_node_ipaddress, 'iam:CreateUser', '*', {'aws:SourceIp': '192.168.0.1'}))
def test_datetime_condition_handling(self): """ Validate the following conditions are correctly handled: DateEquals DateNotEquals DateLessThan DateLessThanEquals DateGreaterThan DateGreaterThanEquals TODO: Check on ForAnyValue and ForAllValues """ # DateEquals test_node_date_equals = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'DateEquals': { 'aws:CurrentTime': '2018-08-10T00:00:00Z' } } }] }) self.assertTrue( local_check_authorization( test_node_date_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:00Z'})) self.assertTrue( local_check_authorization(test_node_date_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '1533859200.0'})) self.assertTrue( local_check_authorization(test_node_date_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '1533859200'})) self.assertFalse( local_check_authorization(test_node_date_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '1533859201'})) self.assertFalse( local_check_authorization( test_node_date_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:01Z'})) # DateNotEquals test_node_date_not_equals = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'DateNotEquals': { 'aws:CurrentTime': '2018-08-10T00:00:00Z' } } }] }) self.assertFalse( local_check_authorization( test_node_date_not_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:00Z'})) self.assertTrue( local_check_authorization( test_node_date_not_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:01Z'})) # DateGreaterThan test_node_date_greater_than = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'DateGreaterThan': { 'aws:CurrentTime': '2018-08-10T00:00:00Z' } } }] }) self.assertFalse( local_check_authorization( test_node_date_greater_than, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:00Z'})) self.assertTrue( local_check_authorization( test_node_date_greater_than, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:01Z'})) # DateGreaterThanEquals test_node_date_greater_than_equals = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'DateGreaterThanEquals': { 'aws:CurrentTime': '2018-08-10T00:00:00Z' } } }] }) self.assertFalse( local_check_authorization( test_node_date_greater_than_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-09T23:59:59Z'})) self.assertTrue( local_check_authorization( test_node_date_greater_than_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:00Z'})) self.assertTrue( local_check_authorization( test_node_date_greater_than_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:01Z'})) # DateLessThan test_node_date_less_than = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'DateLessThan': { 'aws:CurrentTime': '2018-08-10T00:00:00Z' } } }] }) self.assertTrue( local_check_authorization( test_node_date_less_than, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-09T23:59:59Z'})) self.assertFalse( local_check_authorization( test_node_date_less_than, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:01Z'})) # DateLessThanEquals test_node_date_less_than_equals = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'DateLessThanEquals': { 'aws:CurrentTime': '2018-08-10T00:00:00Z' } } }] }) self.assertTrue( local_check_authorization( test_node_date_less_than_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-09T23:59:59Z'})) self.assertTrue( local_check_authorization( test_node_date_less_than_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:00Z'})) self.assertFalse( local_check_authorization( test_node_date_less_than_equals, 'iam:CreateUser', '*', {'aws:CurrentTime': '2018-08-10T00:00:01Z'}))
def test_null_condition_handling(self): """ Validate the following conditions are correctly handled: Null, ForAnyValue:Null, ForAllValues:Null Validated against the Simulator API """ # Basic use validation test_node_null = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'Null': { 'aws:username': '******', # aws:username MUST NOT be present 'aws:userid': 'false' # aws:userid MUST be present } } }] }) self.assertTrue( local_check_authorization(test_node_null, 'iam:CreateUser', '*', { 'aws:userid': 'asdf', 'aws:username': '' })) self.assertFalse( local_check_authorization(test_node_null, 'iam:CreateUser', '*', { 'aws:userid': '', 'aws:username': '' })) # Array use validation test_node_null_array = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'Null': { 'aws:username': ['true', 'false'], # doesn't matter if it's in or not } } }] }) self.assertTrue( local_check_authorization(test_node_null_array, 'iam:CreateUser', '*', {'aws:username': ''})) self.assertTrue( local_check_authorization(test_node_null_array, 'iam:CreateUser', '*', {'aws:username': '******'})) # ForAllValues: validation test_node_null_forallvalues_1 = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'ForAllValues:Null': { # For all valid context values... 'aws:username': '******', # aws:username MUST be present } } }] }) self.assertTrue( local_check_authorization(test_node_null_forallvalues_1, 'iam:CreateUser', '*', {'aws:username': '******'})) self.assertTrue( local_check_authorization(test_node_null_forallvalues_1, 'iam:CreateUser', '*', {'aws:username': ''})) test_node_null_forallvalues_2 = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'ForAllValues:Null': { # For all valid context values... 'aws:username': '******', # aws:username MUST NOT be present } } }] }) self.assertFalse( local_check_authorization(test_node_null_forallvalues_2, 'iam:CreateUser', '*', {'aws:username': '******'})) self.assertTrue( local_check_authorization(test_node_null_forallvalues_2, 'iam:CreateUser', '*', {'aws:username': ''})) # ForAnyValue: validation test_node_null_foranyvalue_1 = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'ForAnyValue:Null': { # Among the valid context values... 'aws:username': '******', # aws:username MUST NOT be present (cannot fulfill this) } } }] }) self.assertFalse( local_check_authorization(test_node_null_foranyvalue_1, 'iam:CreateUser', '*', {'aws:username': '******'})) self.assertFalse( local_check_authorization(test_node_null_foranyvalue_1, 'iam:CreateUser', '*', {'aws:username': ''})) test_node_null_foranyvalue_2 = _build_user_with_policy({ 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Action': '*', 'Resource': '*', 'Condition': { 'ForAnyValue:Null': { # Among the valid context values... 'aws:username': '******', # aws:username MUST be present } } }] }) self.assertTrue( local_check_authorization(test_node_null_foranyvalue_2, 'iam:CreateUser', '*', {'aws:username': '******'})) self.assertFalse( local_check_authorization(test_node_null_foranyvalue_2, 'iam:CreateUser', '*', {'aws:username': ''}))