def audit(policy): allowed_actions = policy.get_allowed_actions() try: config_resources = policy.config["SENSITIVE_ACCESS"]["resources"] except KeyError: config_resources = {} sensitive_resources = defaultdict(list) for item in config_resources: action = list(item.keys())[0] expanded_action = _expand_action(action) resources = list(item.values())[0] sensitive_resources[expanded_action].extend(resources) action_resources = {} for action in allowed_actions: expanded_action = _expand_action(action) service, operation = expanded_action.split(":") action_resources[expanded_action] = policy.get_allowed_resources( service, operation) for action in action_resources: for action_resource in action_resources[action]: for sensitive_resource in sensitive_resources[action]: if is_arn_match("object", action_resource, sensitive_resource): policy.add_finding( "SENSITIVE_ACCESS", location={ "resource": action_resource, "actions": action }, )
def get_privilege_statements(policy_doc, privilege_matches, resource_arn, principal, policy_identifier): policy = parliament.policy.Policy(policy_doc) policy.analyze() policy_privilege_matches = [] for privilege_match in privilege_matches: references = policy.get_references(privilege_match["privilege_prefix"], privilege_match["privilege_name"]) statements_for_resource = [] for reference in references: expanded_reference = replace_principal_variables( reference, principal) # TODO I need to do something for NotResource and NotAction if parliament.is_arn_match(privilege_match["resource_type"], expanded_reference, resource_arn): # We now have a bunch of statements that match the privileges and resource of interest. # Now we need to check if the statement is allowed by its conditions. # We'll append the allowed statements to a second list, and then swap that list in. stmts = references[reference] condition_allowed_stmts = [] for stmt in stmts: stmt.set_policy_identifier(policy_identifier) allowed_by_conditions = True for condition_function in stmt.stmt.get("Condition", {}): condition_values = stmt.stmt["Condition"][ condition_function] condition_result = get_condition_result( condition_function, condition_values, resource_arn, principal, ) # TODO Need to do something different for Deny, to avoid false negatives if condition_result is not None: if condition_result == False: allowed_by_conditions = False break if allowed_by_conditions: condition_allowed_stmts.append(stmt) references[reference] = condition_allowed_stmts statements_for_resource.extend(references[reference]) if len(statements_for_resource) == 0: continue policy_privilege_matches.append({ "privilege": privilege_match, "matching_statements": statements_for_resource, }) return policy_privilege_matches
def audit(policy): action_resources = {} for action in expand_action("s3:*"): # Iterates through a list of containing elements such as # {'service': 's3', 'action': 'GetObject'} action_name = "{}:{}".format(action["service"], action["action"]) action_resources[action_name] = policy.get_allowed_resources( action["service"], action["action"]) for action_name in action_resources: resources = action_resources[action_name] for r in resources: if is_arn_match( "object", "arn:aws:s3:::secretbucket*", r) or is_arn_match( "object", "arn:aws:s3:::othersecretbucket*", r): policy.add_finding( "SENSITIVE_BUCKET_ACCESS", location={ "action": action_name, "resource": r }, )
def test_arn_match(self): assert_true(is_arn_match("object", "arn:*:s3:::*/*", "arn:*:s3:::*/*")) assert_true(is_arn_match("object", "*", "arn:*:s3:::*/*")) assert_true(is_arn_match("object", "arn:*:s3:::*/*", "*")) assert_true( is_arn_match("object", "arn:*:s3:::*/*", "arn:aws:s3:::*personalize*") ) assert_true( is_arn_match("bucket", "arn:*:s3:::mybucket", "arn:*:s3:::mybucket") ) assert_false( is_arn_match("bucket", "arn:*:s3:::mybucket", "arn:*:s3:::mybucket/*"), "Bucket and object types should not match", ) assert_false( is_arn_match("object", "arn:*:s3:::*/*", "arn:aws:s3:::examplebucket"), "Object and bucket types should not match", ) assert_true( is_arn_match("bucket", "arn:*:s3:::mybucket*", "arn:*:s3:::mybucket2") ) assert_true(is_arn_match("bucket", "arn:*:s3:::*", "arn:*:s3:::mybucket2")) assert_false( is_arn_match( "object", "arn:*:s3:::*/*", "arn:aws:logs:*:*:/aws/cloudfront/*" ) ) assert_false( is_arn_match( "object", "arn:aws:s3:::*/*", "arn:aws:logs:*:*:/aws/cloudfront/*" ) ) assert_true( is_arn_match( "cloudfront", "arn:aws:logs:*:*:/aws/cloudfront/*", "arn:aws:logs:us-east-1:000000000000:/aws/cloudfront/test", ) )
def test_arn_match_s3_withregion(self): assert_false( is_arn_match("object", "arn:*:s3:::*/*", "arn:aws:s3:us-east-1::bucket1/*"))
def test_arn_match_cloudtrail_emptysegments(self): assert_false( is_arn_match("cloudtrail", "arn:*:cloudtrail:*:*:trail/*", "arn:::::trail/my-trail"))
def test_arn_match_apigw_withaccount(self): assert_false( is_arn_match( "apigateway", "arn:*:apigateway:*::*", "arn:aws:apigateway:us-east-1:123412341234:/restapis/a123456789/*" ))
def test_arn_match_apigw_emptysegments(self): assert_false( is_arn_match("apigateway", "arn:*:apigateway:*::*", "arn:aws:apigateway:::/restapis/a123456789/*"))
def test_arn_match_iam_withregion(self): assert_false( is_arn_match("role", "arn:*:iam::*:role/*", "arn:aws:iam:us-east-1::role/my-role"))
def test_arn_match_iam_emptysegments(self): assert_false( is_arn_match("role", "arn:*:iam::*:role/*", "arn:aws:iam:::role/my-role"))
def test_arn_match_s3_withaccount(self): assert_false( is_arn_match("object", "arn:*:s3:::*/*", "arn:aws:s3::123412341234:bucket1/*"))