def test_add_single_rule_builds_correct_map(self): """Test that adding a single rule builds the correct map.""" rule_book = ire.IamRuleBook(test_rules.RULES1, self.fake_timestamp) actual_rules = rule_book.resource_rules_map # expected rule_bindings = [{ 'role': 'roles/*', 'members': ['user:*@company.com'] }] rule = scanner_rules.Rule( 'my rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='whitelist') expected_org_rules = ire.ResourceRules(self.org789, rules=set([rule]), applies_to='self_and_children') expected_proj1_rules = ire.ResourceRules(self.project1, rules=set([rule]), applies_to='self') expected_proj2_rules = ire.ResourceRules(self.project2, rules=set([rule]), applies_to='self') expected_rules = { (self.org789, 'self_and_children'): expected_org_rules, (self.project1, 'self'): expected_proj1_rules, (self.project2, 'self'): expected_proj2_rules } self.assertEqual(expected_rules, actual_rules)
def test_one_member_mismatch(self): """Test a policy where one member mismatches the whitelist. Setup: * Create a RulesEngine and add test_rules.RULES1. * Create the policy binding. * Create the Rule and rule bindings. * Create the resource association for the Rule. Expected results: One policy binding member missing from the whitelist. """ # actual rules_local_path = get_datafile_path(__file__, 'test_rules_1.yaml') rules_engine = ire.IamRulesEngine(rules_local_path) rules_engine.rule_book = ire.IamRuleBook(test_rules.RULES1, self.fake_timestamp) rules_engine.rule_book.org_res_rel_dao = mock.MagicMock() find_ancestor_mock = mock.MagicMock(side_effect=[[self.org789]]) rules_engine.rule_book.org_res_rel_dao.find_ancestors = \ find_ancestor_mock policy = { 'bindings': [{ 'role': 'roles/editor', 'members': ['user:[email protected]', 'user:[email protected]'] }] } actual_violations = set( rules_engine.find_policy_violations(self.project1, policy)) # expected rule_bindings = [{ 'role': 'roles/*', 'members': ['user:*@company.com'] }] rule = scanner_rules.Rule( 'my rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='whitelist') expected_outstanding = { 'roles/editor': [IamPolicyMember.create_from('user:[email protected]')] } expected_violations = set([ scanner_rules.RuleViolation( resource_type=self.project1.type, resource_id=self.project1.id, rule_name=rule.rule_name, rule_index=rule.rule_index, role='roles/editor', violation_type=scanner_rules.VIOLATION_TYPE.get(rule.mode), members=tuple(expected_outstanding['roles/editor'])) ]) self.assertEqual(expected_violations, actual_violations)
def test_policy_binding_matches_whitelist_rules(self): """Test that a policy binding matches the whitelist rules. Setup: * Create a test policy binding. * Create a test rule binding. * Create a whitelist rule with the test rules. Expected results: All policy binding members are in the whitelist. """ test_binding = { 'role': 'roles/owner', 'members': [ 'user:[email protected]', 'user:[email protected]', 'group:[email protected]', 'serviceAccount:[email protected]', ] } rule_bindings = [{ 'role': 'roles/owner', 'members': [ 'user:*@company.com', 'user:abc@*.somewhere.com', 'group:*@googlegroups.com', 'serviceAccount:*@*.gserviceaccount.com', ] }] rule = scanner_rules.Rule( 'test rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='whitelist') resource_rule = ire.ResourceRules(rules=[rule]) results = list( resource_rule.find_mismatches(self.project1, test_binding)) self.assertEqual(0, len(results))
def test_policy_binding_mismatches_required_rules(self): """Test that a required list of members mismatches policy binding. Setup: * Create a test policy binding. * Create a test rule binding. * Create a required rule with the test rules. Expected results: All required members are found in the policy. """ test_binding = { 'role': 'roles/owner', 'members': [ 'user:[email protected]', 'group:[email protected]', 'serviceAccount:[email protected]', ] } rule_bindings = [{ 'role': 'roles/owner', 'members': [ 'user:[email protected]', 'user:[email protected]', ] }] rule = scanner_rules.Rule( 'test rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='required') resource_rule = ire.ResourceRules(resource=self.project1) resource_rule.rules.add(rule) results = list( resource_rule.find_mismatches(self.project1, test_binding)) self.assertEqual(1, len(results))
def test_policy_binding_does_not_match_blacklist_rules(self): """Test that a policy binding does not match the blacklist. Setup: * Create a test policy binding. * Create a test rule binding. * Create a blacklist rule with the test rules. Expected results: No policy bindings found in the blacklist. """ test_binding = { 'role': 'roles/owner', 'members': [ 'user:[email protected]', ] } rule_bindings = [{ 'role': 'roles/owner', 'members': [ 'user:*@company.com', 'user:abc@*.somewhere.com', 'group:*@googlegroups.com', 'serviceAccount:*@*.gserviceaccount.com', ] }] rule = scanner_rules.Rule( 'test rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='blacklist') resource_rule = ire.ResourceRules(rules=[rule]) results = list( resource_rule.find_mismatches(self.project1, test_binding)) self.assertEqual(0, len(results))
def add_rule(self, rule_def, rule_index): """Add a rule to the rule book. The rule supplied to this method is the dictionary parsed from the rules definition file. For example, this rule... # rules yaml: rules: - name: a rule mode: whitelist resource: - type: project applies_to: self resource_ids: - my-project-123 inherit_from_parents: true bindings: - role: roles/editor members: - users:[email protected] ... gets parsed into: { 'name': 'a rule', 'mode': 'whitelist', 'resource': { 'type': 'project', 'applies_to': self, 'resource_ids': ['my-project-id'] }, 'inherit_from_parents': true, 'bindings': [ { 'role': 'roles/editor', 'members': ['users:[email protected]'] } ] } Args: rule_def (dict): Contains rule definition properties. rule_index (int): The index of the rule from the rule definitions. Assigned automatically when the rule book is built. """ self._rules_sema.acquire() try: resources = rule_def.get('resource') for resource in resources: resource_ids = resource.get('resource_ids') resource_type = None # TODO: collect these errors and output them in a log. # TODO: log the error and keep going try: resource_type = resource_mod.ResourceType.verify( resource.get('type')) except resource_errors.InvalidResourceTypeError: raise audit_errors.InvalidRulesSchemaError( 'Missing resource type in rule {}'.format(rule_index)) if not resource_ids or len(resource_ids) < 1: raise audit_errors.InvalidRulesSchemaError( 'Missing resource ids in rule {}'.format(rule_index)) # For each resource id associated with the rule, create a # mapping of resource => rules. for resource_id in resource_ids: gcp_resource = resource_util.create_resource( resource_id=resource_id, resource_type=resource_type) rule_bindings = [ iam_policy.IamPolicyBinding.create_from(b) for b in rule_def.get('bindings') ] rule = scanner_rules.Rule(rule_name=rule_def.get('name'), rule_index=rule_index, bindings=rule_bindings, mode=rule_def.get('mode')) rule_applies_to = resource.get('applies_to') rule_key = (gcp_resource, rule_applies_to) # See if we have a mapping of the resource and rule resource_rules = self.resource_rules_map.get(rule_key) # If no mapping exists, create it. if not resource_rules: resource_rules = ResourceRules( resource=gcp_resource, applies_to=rule_applies_to, inherit_from_parents=rule_def.get( 'inherit_from_parents', False)) self.resource_rules_map[rule_key] = resource_rules # If the rule isn't in the mapping, add it. if rule not in resource_rules.rules: resource_rules.rules.add(rule) finally: self._rules_sema.release()
def test_invalid_rule_mode_raises_when_create_rule(self): """Test that creating a Rule with invalid rule mode raises error.""" with self.assertRaises(InvalidRulesSchemaError): scanner_rules.Rule('exception', 0, [])