def test_find_changes_batch(self): """ This will test the entry point via the find_changes() method vs. the find_changes_batch() method. This will also use the IAMRole watcher, since that already has batching support. :return: """ from security_monkey.watchers.iam.iam_role import IAMRole self.setup_batch_db() watcher = IAMRole(accounts=[self.account.name]) watcher.current_account = (self.account, 0) watcher.technology = self.technology items = [] for x in range(0, 5): mod_conf = dict(ACTIVE_CONF) mod_conf["name"] = "SomeRole{}".format(x) mod_conf["Arn"] = "arn:aws:iam::012345678910:role/SomeRole{}".format(x) items.append(SomeTestItem().from_slurp(mod_conf, account_name=self.account.name)) assert len(watcher.find_changes(items)) == 5 # Try again -- audit_items should be 0 since nothing was changed: assert len(watcher.find_changes(items)) == 0
def test_empty_slurp_list(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) watcher.list_method = lambda **kwargs: [] _, exceptions = watcher.slurp_list() assert len(exceptions) == 0 assert len(watcher.total_list) == 0 assert watcher.done_slurping
def test_slurp_list(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) _, exceptions = watcher.slurp_list() assert len(exceptions) == 0 assert len(watcher.total_list) == self.total_roles assert not watcher.done_slurping
def test_slurp_list(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) _, exceptions = watcher.slurp_list() assert len(exceptions) == 0 assert len(watcher.total_list) == self.total_roles assert not watcher.done_slurping mock_sts().stop()
def test_empty_slurp_list(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) watcher.list_method = lambda **kwargs: [] _, exceptions = watcher.slurp_list() assert len(exceptions) == 0 assert len(watcher.total_list) == 0 assert watcher.done_slurping mock_sts().stop()
def test_slurp_list_exceptions(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) def raise_exception(): raise Exception("LOL, HAY!") watcher.list_method = lambda **kwargs: raise_exception() _, exceptions = watcher.slurp_list() assert len(exceptions) == 1 assert len(ExceptionLogs.query.all()) == 1
def test_slurp_list_exceptions(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) def raise_exception(): raise Exception("LOL, HAY!") watcher.list_method = lambda **kwargs: raise_exception() _, exceptions = watcher.slurp_list() assert len(exceptions) == 1 assert len(ExceptionLogs.query.all()) == 1 mock_sts().stop()
def test_slurp_list_exceptions(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) def raise_exception(): raise Exception("LOL, HAY!") import security_monkey.watchers.iam.iam_role security_monkey.watchers.iam.iam_role.list_roles = lambda **kwargs: raise_exception() exceptions = watcher.slurp_list() assert len(exceptions) == 1 assert len(ExceptionLogs.query.all()) == 1 mock_sts().stop()
def test_celery_beat(self, mock_expired_exceptions, mock_account_tech, mock_purge, mock_setup): from security_monkey.task_scheduler.beat import setup_the_tasks from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.auditors.iam.iam_role import IAMRoleAuditor # Set up the monitor: test_account = Account.query.filter( Account.name == "TEST_ACCOUNT1").one() watcher = IAMRole(accounts=[test_account.name]) batched_monitor = Monitor(IAMRole, test_account) batched_monitor.watcher = watcher batched_monitor.auditors = [ IAMRoleAuditor(accounts=[test_account.name]) ] import security_monkey.task_scheduler.tasks old_get_monitors = security_monkey.task_scheduler.tasks.get_monitors security_monkey.task_scheduler.tasks.get_monitors = lambda x, y, z: [ batched_monitor ] setup_the_tasks(mock.Mock()) assert mock_setup.called assert mock_purge.called # "apply_async" where the immediately scheduled tasks assert mock_account_tech.apply_async.called # The ".s" are the scheduled tasks. Too lazy to grab the intervals out. assert mock_account_tech.s.called assert mock_expired_exceptions.s.called #assert mock_task_audit.s.called # Build the expected mock results: scheduled_tech_result_list = [] async_result_list = [] # audit_result_list = [] import security_monkey.watcher import security_monkey.auditor for account in Account.query.filter( Account.third_party == False).filter( Account.active == True).all(): # noqa for w in security_monkey.watcher.watcher_registry.iterkeys(): scheduled_tech_result_list.append(((account.name, w), )) async_result_list.append((((account.name, w), ), )) # It's just policy for IAM: # audit_result_list.append(((account.name, "policy"),)) assert mock_account_tech.s.call_args_list == scheduled_tech_result_list assert async_result_list == mock_account_tech.apply_async.call_args_list # assert audit_result_list == mock_task_audit.s.call_args_list security_monkey.task_scheduler.tasks.get_monitors = old_get_monitors
def test_slurp_items_with_exceptions(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) # Or else this will take forever: watcher.batched_size = 10 watcher.slurp_list() def raise_exception(): raise Exception("LOL, HAY!") import security_monkey.watchers.iam.iam_role security_monkey.watchers.iam.iam_role.get_role = lambda **kwargs: raise_exception() items, exceptions = watcher.slurp() assert len(exceptions) == self.total_roles assert len(items) == 0 mock_sts().stop()
def test_find_changes_batch(self): """ This will test the entry point via the find_changes() method vs. the find_changes_batch() method. This will also use the IAMRole watcher, since that already has batching support. :return: """ from security_monkey.watchers.iam.iam_role import IAMRole self.setup_batch_db() watcher = IAMRole(accounts=[self.account.name]) watcher.current_account = (self.account, 0) watcher.technology = self.technology items = [] for x in range(0, 5): mod_conf = dict(ACTIVE_CONF) mod_conf["name"] = "SomeRole{}".format(x) mod_conf[ "Arn"] = "arn:aws:iam::012345678910:role/SomeRole{}".format(x) items.append(SomeTestItem().from_slurp( mod_conf, account_name=self.account.name)) assert len(watcher.find_changes(items)) == 5 # Try again -- audit_items should be 0 since nothing was changed: assert len(watcher.find_changes(items)) == 0
def test_slurp_items_with_skipped(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) watcher.batched_size = 5 watcher.slurp_list() watcher.ignore_list = [ IgnoreListEntry(prefix="roleNumber0"), IgnoreListEntry(prefix="roleNumber1"), IgnoreListEntry(prefix="roleNumber6") ] first_batch, exceptions = watcher.slurp() item_sum = len(first_batch) assert len(exceptions) == 0 assert watcher.batch_counter == 1 batch_lookup = {} # Ensure we aren't processing duplicates for r in first_batch: assert r.name not in watcher.ignore_list # Ensure we properly ignore the things batch_lookup[r.name] = True # Slurp again: second_batch, exceptions = watcher.slurp() item_sum += len(second_batch) assert len(exceptions) == 0 assert watcher.batch_counter == 2 for r in second_batch: assert r.name not in watcher.ignore_list assert not batch_lookup.get(r.name) batch_lookup[r.name] = True # Sum of items should be total items - length of the ignored items: assert self.total_roles - len( watcher.ignore_list) == item_sum == len(batch_lookup) mock_sts().stop()
def test_slurp_items_with_exceptions(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) # Or else this will take forever: watcher.batched_size = 10 watcher.slurp_list() def raise_exception(): raise Exception("LOL, HAY!") watcher.get_method = lambda *args, **kwargs: raise_exception() items, exceptions = watcher.slurp() assert len(exceptions) == self.total_roles assert len(items) == 0 mock_sts().stop()
def test_slurp_items(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) # Or else this will take forever: watcher.batched_size = 10 watcher.slurp_list() items, exceptions = watcher.slurp() assert len(exceptions) == 0 assert self.total_roles > len(items) == watcher.batched_size assert watcher.batch_counter == 1 # Slurp again: items, exceptions = watcher.slurp() assert len(exceptions) == 0 assert self.total_roles > len(items) == watcher.batched_size assert watcher.batch_counter == 2
def test_slurp_items_with_skipped(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) watcher.batched_size = 5 watcher.slurp_list() watcher.ignore_list = [ IgnoreListEntry(prefix="roleNumber0"), IgnoreListEntry(prefix="roleNumber1"), IgnoreListEntry(prefix="roleNumber6") ] first_batch, exceptions = watcher.slurp() item_sum = len(first_batch) assert len(exceptions) == 0 assert watcher.batch_counter == 1 batch_lookup = {} # Ensure we aren't processing duplicates for r in first_batch: assert r.name not in watcher.ignore_list # Ensure we properly ignore the things batch_lookup[r.name] = True # Slurp again: second_batch, exceptions = watcher.slurp() item_sum += len(second_batch) assert len(exceptions) == 0 assert watcher.batch_counter == 2 for r in second_batch: assert r.name not in watcher.ignore_list assert not batch_lookup.get(r.name) batch_lookup[r.name] = True # Sum of items should be total items - length of the ignored items: assert self.total_roles - len(watcher.ignore_list) == item_sum == len(batch_lookup) mock_sts().stop()
def test_slurp_items_with_exceptions(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) # Or else this will take forever: watcher.batched_size = 10 watcher.slurp_list() def raise_exception(): raise Exception("LOL, HAY!") watcher.get_method = lambda *args, **kwargs: raise_exception() items, exceptions = watcher.slurp() assert len(exceptions) == watcher.batched_size assert len(items) == 0 assert watcher.batch_counter == 1 mock_sts().stop()
def test_slurp_items(self): mock_sts().start() watcher = IAMRole(accounts=[self.account.name]) # Or else this will take forever: watcher.batched_size = 10 watcher.slurp_list() items, exceptions = watcher.slurp() assert len(exceptions) == 0 assert self.total_roles > len(items) == watcher.batched_size assert watcher.batch_counter == 1 # Slurp again: items, exceptions = watcher.slurp() assert len(exceptions) == 0 assert self.total_roles > len(items) == watcher.batched_size assert watcher.batch_counter == 2 mock_sts().stop()
def test_find_deleted_batch(self): """ This will use the IAMRole watcher, since that already has batching support. :return: """ from security_monkey.watchers.iam.iam_role import IAMRole self.setup_batch_db() # Set everything up: watcher = IAMRole(accounts=[self.account.name]) watcher.current_account = (self.account, 0) watcher.technology = self.technology items = [] for x in range(0, 5): mod_conf = dict(ACTIVE_CONF) mod_conf["name"] = "SomeRole{}".format(x) mod_conf["Arn"] = ARN_PREFIX + ":iam::012345678910:role/SomeRole{}".format(x) items.append(SomeTestItem().from_slurp(mod_conf, account_name=self.account.name)) mod_aspd = dict(ASPD) mod_aspd["Arn"] = ARN_PREFIX + ":iam::012345678910:role/SomeRole{}".format(x) mod_aspd["RoleName"] = "SomeRole{}".format(x) watcher.total_list.append(mod_aspd) watcher.find_changes(items) # Check for deleted items: watcher.find_deleted_batch({}) assert len(watcher.deleted_items) == 0 # Check that nothing was deleted: for x in range(0, 5): item_revision = ItemRevision.query.join((Item, ItemRevision.id == Item.latest_revision_id)).filter( Item.arn == ARN_PREFIX + ":iam::012345678910:role/SomeRole{}".format(x), ).one() assert item_revision.active # Create some issues for testing purposes: db.session.add(ItemAudit(score=10, issue="IAM Role has full admin permissions.", notes=json.dumps(item_revision.config), item_id=item_revision.item_id)) db.session.add(ItemAudit(score=9001, issue="Some test issue", notes="{}", item_id=item_revision.item_id)) db.session.commit() assert len(ItemAudit.query.all()) == len(items) * 2 # Remove the last two items: removed_arns = [] removed_arns.append(watcher.total_list.pop()["Arn"]) removed_arns.append(watcher.total_list.pop()["Arn"]) # Check for deleted items again: watcher.find_deleted_batch({}) assert len(watcher.deleted_items) == 2 # Check that the last two items were deleted: for arn in removed_arns: item_revision = ItemRevision.query.join((Item, ItemRevision.id == Item.latest_revision_id)).filter( Item.arn == arn, ).one() assert not item_revision.active # Check that the current ones weren't deleted: for current_item in watcher.total_list: item_revision = ItemRevision.query.join((Item, ItemRevision.id == Item.latest_revision_id)).filter( Item.arn == current_item["Arn"], ).one() assert item_revision.active assert len(ItemAudit.query.filter(ItemAudit.item_id == item_revision.item_id).all()) == 2
def test_find_batch_changes(self): """ Runs through a full find job via the IAMRole watcher, as that supports batching. However, this is mostly testing the logic through each function call -- this is not going to do any boto work and that will instead be mocked out. :return: """ from security_monkey.scheduler import find_changes from security_monkey.monitors import Monitor from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.auditors.iam.iam_role import IAMRoleAuditor test_account = Account(name="TEST_ACCOUNT1") watcher = IAMRole(accounts=[test_account.name]) technology = Technology(name="iamrole") db.session.add(technology) db.session.commit() watcher.batched_size = 3 # should loop 4 times self.add_roles() # Set up the monitor: batched_monitor = Monitor(IAMRole, test_account) batched_monitor.watcher = watcher batched_monitor.auditors = [IAMRoleAuditor(accounts=[test_account.name])] import security_monkey.scheduler security_monkey.scheduler.get_monitors = lambda x, y, z: [batched_monitor] # Moto screws up the IAM Role ARN -- so we need to fix it: original_slurp_list = watcher.slurp_list original_slurp = watcher.slurp def mock_slurp_list(): exception_map = original_slurp_list() for item in watcher.total_list: item["Arn"] = "arn:aws:iam::012345678910:role/{}".format(item["RoleName"]) return exception_map def mock_slurp(): batched_items, exception_map = original_slurp() for item in batched_items: item.arn = "arn:aws:iam::012345678910:role/{}".format(item.name) item.config["Arn"] = item.arn item.config["RoleId"] = item.name # Need this to stay the same return batched_items, exception_map watcher.slurp_list = mock_slurp_list watcher.slurp = mock_slurp find_changes([test_account.name], test_account.name) # Check that all items were added to the DB: assert len(Item.query.all()) == 11 # Check that we have exactly 11 item revisions: assert len(ItemRevision.query.all()) == 11 # Check that there are audit issues for all 11 items: assert len(ItemAudit.query.all()) == 11 # Delete one of the items: # Moto lacks implementation for "delete_role" (and I'm too lazy to submit a PR :D) -- so need to create again... mock_iam().stop() mock_sts().stop() self.add_roles(initial=False) # Run the it again: watcher.current_account = None # Need to reset the watcher find_changes([test_account.name], test_account.name) # Check that nothing new was added: assert len(Item.query.all()) == 11 # There should be 2 less issues and 2 more revisions: assert len(ItemAudit.query.all()) == 9 assert len(ItemRevision.query.all()) == 13 # Check that the deleted roles show as being inactive: ir = ItemRevision.query.join((Item, ItemRevision.id == Item.latest_revision_id)) \ .filter(Item.arn.in_( ["arn:aws:iam::012345678910:role/roleNumber9", "arn:aws:iam::012345678910:role/roleNumber10"])).all() assert len(ir) == 2 assert not ir[0].active assert not ir[1].active # Finally -- test with a slurp list exception (just checking that things don't blow up): def mock_slurp_list_with_exception(): import security_monkey.watchers.iam.iam_role security_monkey.watchers.iam.iam_role.list_roles = lambda **kwargs: 1 / 0 exception_map = original_slurp_list() assert len(exception_map) > 0 return exception_map watcher.slurp_list = mock_slurp_list_with_exception watcher.current_account = None # Need to reset the watcher find_changes([test_account.name], test_account.name) mock_iam().stop() mock_sts().stop()
def test_find_batch_changes(self, mock_fix_orphaned): """ Runs through a full find job via the IAMRole watcher, as that supports batching. However, this is mostly testing the logic through each function call -- this is not going to do any boto work and that will instead be mocked out. :return: """ from security_monkey.task_scheduler.tasks import manual_run_change_finder from security_monkey.monitors import Monitor from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.auditors.iam.iam_role import IAMRoleAuditor test_account = Account(name="TEST_ACCOUNT1") watcher = IAMRole(accounts=[test_account.name]) technology = Technology(name="iamrole") db.session.add(technology) db.session.commit() watcher.batched_size = 3 # should loop 4 times self.add_roles() # Set up the monitor: batched_monitor = Monitor(IAMRole, test_account) batched_monitor.watcher = watcher batched_monitor.auditors = [ IAMRoleAuditor(accounts=[test_account.name]) ] import security_monkey.task_scheduler.tasks old_get_monitors = security_monkey.task_scheduler.tasks.get_monitors security_monkey.task_scheduler.tasks.get_monitors = lambda x, y, z: [ batched_monitor ] # Moto screws up the IAM Role ARN -- so we need to fix it: original_slurp_list = watcher.slurp_list original_slurp = watcher.slurp def mock_slurp_list(): items, exception_map = original_slurp_list() for item in watcher.total_list: item["Arn"] = ARN_PREFIX + ":iam::012345678910:role/{}".format( item["RoleName"]) return items, exception_map def mock_slurp(): batched_items, exception_map = original_slurp() for item in batched_items: item.arn = ARN_PREFIX + ":iam::012345678910:role/{}".format( item.name) item.config["Arn"] = item.arn item.config["RoleId"] = item.name # Need this to stay the same return batched_items, exception_map watcher.slurp_list = mock_slurp_list watcher.slurp = mock_slurp manual_run_change_finder([test_account.name], [watcher.index]) assert mock_fix_orphaned.called # Check that all items were added to the DB: assert len(Item.query.all()) == 11 # Check that we have exactly 11 item revisions: assert len(ItemRevision.query.all()) == 11 # Check that there are audit issues for all 11 items: assert len(ItemAudit.query.all()) == 11 # Delete one of the items: # Moto lacks implementation for "delete_role" (and I'm too lazy to submit a PR :D) -- so need to create again... mock_iam().stop() mock_sts().stop() self.add_roles(initial=False) # Run the it again: watcher.current_account = None # Need to reset the watcher manual_run_change_finder([test_account.name], [watcher.index]) # Check that nothing new was added: assert len(Item.query.all()) == 11 # There should be the same number of issues and 2 more revisions: assert len(ItemAudit.query.all()) == 11 assert len(ItemRevision.query.all()) == 13 # Check that the deleted roles show as being inactive: ir = ItemRevision.query.join((Item, ItemRevision.id == Item.latest_revision_id)) \ .filter(Item.arn.in_( [ARN_PREFIX + ":iam::012345678910:role/roleNumber9", ARN_PREFIX + ":iam::012345678910:role/roleNumber10"])).all() assert len(ir) == 2 assert not ir[0].active assert not ir[1].active # Finally -- test with a slurp list exception (just checking that things don't blow up): import security_monkey.watchers.iam.iam_role old_list_roles = security_monkey.watchers.iam.iam_role.list_roles def mock_slurp_list_with_exception(): security_monkey.watchers.iam.iam_role.list_roles = lambda **kwargs: 1 / 0 items, exception_map = original_slurp_list() assert len(exception_map) > 0 return items, exception_map watcher.slurp_list = mock_slurp_list_with_exception watcher.current_account = None # Need to reset the watcher manual_run_change_finder([test_account.name], [watcher.index]) security_monkey.task_scheduler.tasks.get_monitors = old_get_monitors security_monkey.watchers.iam.iam_role.list_roles = old_list_roles mock_iam().stop() mock_sts().stop()
def test_ensure_item_has_latest_revision_id(self): """ Test that items always have a proper current revision set. Otherwise, the item needs to be deleted. :return: """ from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.watcher import ensure_item_has_latest_revision_id from security_monkey.datastore import Datastore # Stop the watcher registry from stepping on everyone's toes: import security_monkey.watcher old_watcher_registry = security_monkey.watcher.watcher_registry security_monkey.watcher.watcher_registry = {IAMRole.index: IAMRole} # Set everything up: self.setup_batch_db() watcher = IAMRole(accounts=[self.account.name]) watcher.current_account = (self.account, 0) watcher.technology = self.technology # Test case #1: Create an item in the DB that has no current revision ID: no_revision_item = Item(region="us-east-1", name="NOREVISION", account_id=self.account.id, tech_id=self.technology.id) db.session.add(no_revision_item) db.session.commit() assert db.session.query(Item).filter(Item.name == no_revision_item.name).one() # Should delete the item from the DB: result = ensure_item_has_latest_revision_id(no_revision_item) assert not result assert not db.session.query(Item).filter(Item.name == no_revision_item.name).first() # Test case #2: Create two item revisions for the given item, but don't attach them to the item. # After the fixer runs, it should return the item with proper hashes and a proper # link to the latest version. ds = Datastore() no_revision_item = Item(region="us-east-1", name="NOREVISION", account_id=self.account.id, tech_id=self.technology.id) db.session.add(no_revision_item) db.session.commit() ir_one = ItemRevision(config=ACTIVE_CONF, date_created=datetime.datetime.utcnow(), item_id=no_revision_item.id) ir_two = ItemRevision(config=ACTIVE_CONF, date_created=(datetime.datetime.utcnow() - timedelta(days=1)), item_id=no_revision_item.id) db.session.add(ir_one) db.session.add(ir_two) db.session.commit() assert len(db.session.query(ItemRevision).filter(ItemRevision.item_id == no_revision_item.id).all()) == 2 result = ensure_item_has_latest_revision_id(no_revision_item) assert result assert result.latest_revision_id == ir_one.id assert ds.hash_config(ACTIVE_CONF) == no_revision_item.latest_revision_complete_hash assert ds.durable_hash(ACTIVE_CONF, watcher.ephemeral_paths) == no_revision_item.latest_revision_durable_hash # Undo the mock: security_monkey.watcher.watcher_registry = old_watcher_registry
def test_report_batch_changes(self, mock_fix_orphaned): from security_monkey.task_scheduler.tasks import manual_run_change_reporter from security_monkey.datastore import Item, ItemRevision, ItemAudit from security_monkey.monitors import Monitor from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.auditors.iam.iam_role import IAMRoleAuditor test_account = Account.query.filter( Account.name == "TEST_ACCOUNT1").one() watcher = IAMRole(accounts=[test_account.name]) watcher.batched_size = 3 # should loop 4 times self.add_roles() # Set up the monitor: batched_monitor = Monitor(IAMRole, test_account) batched_monitor.watcher = watcher batched_monitor.auditors = [ IAMRoleAuditor(accounts=[test_account.name]) ] # Set up the Reporter: import security_monkey.reporter old_all_monitors = security_monkey.reporter.all_monitors security_monkey.reporter.all_monitors = lambda x, y: [batched_monitor] import security_monkey.task_scheduler.tasks old_get_monitors = security_monkey.task_scheduler.tasks.get_monitors security_monkey.task_scheduler.tasks.get_monitors = lambda x, y, z: [ batched_monitor ] # Moto screws up the IAM Role ARN -- so we need to fix it: original_slurp_list = watcher.slurp_list original_slurp = watcher.slurp def mock_slurp_list(): items, exception_map = original_slurp_list() for item in watcher.total_list: item["Arn"] = ARN_PREFIX + ":iam::012345678910:role/{}".format( item["RoleName"]) return items, exception_map def mock_slurp(): batched_items, exception_map = original_slurp() for item in batched_items: item.arn = ARN_PREFIX + ":iam::012345678910:role/{}".format( item.name) item.config["Arn"] = item.arn item.config["RoleId"] = item.name # Need this to stay the same return batched_items, exception_map watcher.slurp_list = mock_slurp_list watcher.slurp = mock_slurp manual_run_change_reporter([test_account.name]) assert mock_fix_orphaned.called # Check that all items were added to the DB: assert len(Item.query.all()) == 11 # Check that we have exactly 11 item revisions: assert len(ItemRevision.query.all()) == 11 # Check that there are audit issues for all 11 items: assert len(ItemAudit.query.all()) == 11 mock_iam().stop() mock_sts().stop() security_monkey.reporter.all_monitors = old_all_monitors security_monkey.task_scheduler.tasks.get_monitors = old_get_monitors
def test_ensure_item_has_latest_revision_id(self): """ Test that items always have a proper current revision set. Otherwise, the item needs to be deleted. :return: """ from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.watcher import ensure_item_has_latest_revision_id from security_monkey.datastore import Datastore # Stop the watcher registry from stepping on everyone's toes: import security_monkey.watcher old_watcher_registry = security_monkey.watcher.watcher_registry security_monkey.watcher.watcher_registry = {IAMRole.index: IAMRole} # Set everything up: self.setup_batch_db() watcher = IAMRole(accounts=[self.account.name]) watcher.current_account = (self.account, 0) watcher.technology = self.technology # Test case #1: Create an item in the DB that has no current revision ID: no_revision_item = Item(region="us-east-1", name="NOREVISION", account_id=self.account.id, tech_id=self.technology.id) db.session.add(no_revision_item) db.session.commit() assert db.session.query(Item).filter( Item.name == no_revision_item.name).one() # Should delete the item from the DB: result = ensure_item_has_latest_revision_id(no_revision_item) assert not result assert not db.session.query(Item).filter( Item.name == no_revision_item.name).first() # Test case #2: Create two item revisions for the given item, but don't attach them to the item. # After the fixer runs, it should return the item with proper hashes and a proper # link to the latest version. ds = Datastore() no_revision_item = Item(region="us-east-1", name="NOREVISION", account_id=self.account.id, tech_id=self.technology.id) db.session.add(no_revision_item) db.session.commit() ir_one = ItemRevision(config=ACTIVE_CONF, date_created=datetime.datetime.utcnow(), item_id=no_revision_item.id) ir_two = ItemRevision(config=ACTIVE_CONF, date_created=(datetime.datetime.utcnow() - timedelta(days=1)), item_id=no_revision_item.id) db.session.add(ir_one) db.session.add(ir_two) db.session.commit() assert len( db.session.query(ItemRevision).filter( ItemRevision.item_id == no_revision_item.id).all()) == 2 result = ensure_item_has_latest_revision_id(no_revision_item) assert result assert result.latest_revision_id == ir_one.id assert ds.hash_config( ACTIVE_CONF) == no_revision_item.latest_revision_complete_hash assert ds.durable_hash( ACTIVE_CONF, watcher.ephemeral_paths ) == no_revision_item.latest_revision_durable_hash # Undo the mock: security_monkey.watcher.watcher_registry = old_watcher_registry
def test_find_deleted_batch(self): """ This will use the IAMRole watcher, since that already has batching support. :return: """ from security_monkey.watchers.iam.iam_role import IAMRole self.setup_batch_db() # Set everything up: watcher = IAMRole(accounts=[self.account.name]) watcher.current_account = (self.account, 0) watcher.technology = self.technology items = [] for x in range(0, 5): mod_conf = dict(ACTIVE_CONF) mod_conf["name"] = "SomeRole{}".format(x) mod_conf[ "Arn"] = ARN_PREFIX + ":iam::012345678910:role/SomeRole{}".format( x) items.append(SomeTestItem().from_slurp( mod_conf, account_name=self.account.name)) mod_aspd = dict(ASPD) mod_aspd[ "Arn"] = ARN_PREFIX + ":iam::012345678910:role/SomeRole{}".format( x) mod_aspd["RoleName"] = "SomeRole{}".format(x) watcher.total_list.append(mod_aspd) watcher.find_changes(items) # Check for deleted items: watcher.find_deleted_batch({}) assert len(watcher.deleted_items) == 0 # Check that nothing was deleted: for x in range(0, 5): item_revision = ItemRevision.query.join( (Item, ItemRevision.id == Item.latest_revision_id)).filter( Item.arn == ARN_PREFIX + ":iam::012345678910:role/SomeRole{}".format(x), ).one() assert item_revision.active # Create some issues for testing purposes: db.session.add( ItemAudit(score=10, issue="IAM Role has full admin permissions.", notes=json.dumps(item_revision.config), item_id=item_revision.item_id)) db.session.add( ItemAudit(score=9001, issue="Some test issue", notes="{}", item_id=item_revision.item_id)) db.session.commit() assert len(ItemAudit.query.all()) == len(items) * 2 # Remove the last two items: removed_arns = [] removed_arns.append(watcher.total_list.pop()["Arn"]) removed_arns.append(watcher.total_list.pop()["Arn"]) # Check for deleted items again: watcher.find_deleted_batch({}) assert len(watcher.deleted_items) == 2 # Check that the last two items were deleted: for arn in removed_arns: item_revision = ItemRevision.query.join( (Item, ItemRevision.id == Item.latest_revision_id)).filter( Item.arn == arn, ).one() assert not item_revision.active # Check that the current ones weren't deleted: for current_item in watcher.total_list: item_revision = ItemRevision.query.join( (Item, ItemRevision.id == Item.latest_revision_id)).filter( Item.arn == current_item["Arn"], ).one() assert item_revision.active assert len( ItemAudit.query.filter( ItemAudit.item_id == item_revision.item_id).all()) == 2
def test_celery_ignore_tech(self, mock_store_exception, mock_expired_exceptions, mock_account_tech, mock_purge, mock_setup): import security_monkey.celeryconfig security_monkey.celeryconfig.security_monkey_watcher_ignore = { "policy" } from security_monkey.task_scheduler.beat import setup_the_tasks from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.watchers.iam.managed_policy import ManagedPolicy from security_monkey.auditors.iam.iam_role import IAMRoleAuditor from security_monkey.auditors.iam.iam_policy import IAMPolicyAuditor # Stop the watcher registry from stepping on everyone's toes: import security_monkey.watcher import security_monkey.monitors security_monkey.watcher.watcher_registry = { IAMRole.index: IAMRole, ManagedPolicy.index: ManagedPolicy } security_monkey.monitors.watcher_registry = security_monkey.watcher.watcher_registry # Set up the monitors: test_account = Account.query.filter( Account.name == "TEST_ACCOUNT1").one() role_watcher = IAMRole(accounts=[test_account.name]) mp_watcher = ManagedPolicy(accounts=[test_account.name]) batched_monitor = Monitor(IAMRole, test_account) batched_monitor.watcher = role_watcher batched_monitor.auditors = [ IAMRoleAuditor(accounts=[test_account.name]) ] normal_monitor = Monitor(ManagedPolicy, test_account) normal_monitor.watcher = mp_watcher normal_monitor.auditors = [ IAMPolicyAuditor(accounts=[test_account.name]) ] import security_monkey.task_scheduler.tasks old_get_monitors = security_monkey.task_scheduler.tasks.get_monitors security_monkey.task_scheduler.tasks.get_monitors = lambda x, y, z: [ batched_monitor, normal_monitor ] setup_the_tasks(mock.Mock()) assert mock_setup.called assert mock_purge.called assert not mock_store_exception.called # "apply_async" where the immediately scheduled tasks assert mock_account_tech.apply_async.called # The ".s" are the scheduled tasks. Too lazy to grab the intervals out. assert mock_account_tech.s.called assert mock_expired_exceptions.s.called assert mock_expired_exceptions.apply_async.called # Policy should not be called at all: for mocked_call in mock_account_tech.s.call_args_list: assert mocked_call[0][1] == "iamrole" for mocked_call in mock_account_tech.apply_async.call_args_list: assert mocked_call[0][0][1] == "iamrole" security_monkey.task_scheduler.tasks.get_monitors = old_get_monitors
def test_report_batch_changes(self): from security_monkey.alerter import Alerter from security_monkey.reporter import Reporter from security_monkey.datastore import Item, ItemRevision, ItemAudit from security_monkey.monitors import Monitor from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.auditors.iam.iam_role import IAMRoleAuditor account_type_result = AccountType.query.filter( AccountType.name == "AWS").one() db.session.add(account_type_result) db.session.commit() test_account = Account(identifier="012345678910", name="TEST_ACCOUNT", account_type_id=account_type_result.id, notes="TEST_ACCOUNT1", third_party=False, active=True) watcher = IAMRole(accounts=[test_account.name]) db.session.commit() watcher.batched_size = 3 # should loop 4 times self.add_roles() # Set up the monitor: batched_monitor = Monitor(IAMRole, test_account) batched_monitor.watcher = watcher batched_monitor.auditors = [ IAMRoleAuditor(accounts=[test_account.name]) ] # Set up the Reporter: import security_monkey.reporter old_all_monitors = security_monkey.reporter.all_monitors security_monkey.reporter.all_monitors = lambda x, y: [] test_reporter = Reporter() test_reporter.all_monitors = [batched_monitor] test_reporter.account_alerter = Alerter( watchers_auditors=test_reporter.all_monitors, account=test_account.name) import security_monkey.scheduler # import security_monkey.monitors # old_get_monitors = security_monkey.scheduler.get_monitors security_monkey.scheduler.get_monitors = lambda x, y, z: [ batched_monitor ] # Moto screws up the IAM Role ARN -- so we need to fix it: original_slurp_list = watcher.slurp_list original_slurp = watcher.slurp def mock_slurp_list(): items, exception_map = original_slurp_list() for item in watcher.total_list: item["Arn"] = "arn:aws:iam::012345678910:role/{}".format( item["RoleName"]) return items, exception_map def mock_slurp(): batched_items, exception_map = original_slurp() for item in batched_items: item.arn = "arn:aws:iam::012345678910:role/{}".format( item.name) item.config["Arn"] = item.arn item.config["RoleId"] = item.name # Need this to stay the same return batched_items, exception_map watcher.slurp_list = mock_slurp_list watcher.slurp = mock_slurp test_reporter.run(account=test_account.name) # Check that all items were added to the DB: assert len(Item.query.all()) == 11 # Check that we have exactly 11 item revisions: assert len(ItemRevision.query.all()) == 11 # Check that there are audit issues for all 11 items: assert len(ItemAudit.query.all()) == 11 mock_iam().stop() mock_sts().stop() # Something isn't cleaning itself up properly and causing other core tests to fail. # This is the solution: security_monkey.reporter.all_monitors = old_all_monitors import monitor_mock security_monkey.scheduler.get_monitors = monitor_mock.mock_get_monitors
def test_find_batch_changes(self, mock_fix_orphaned): """ Runs through a full find job via the IAMRole watcher, as that supports batching. However, this is mostly testing the logic through each function call -- this is not going to do any boto work and that will instead be mocked out. :return: """ from security_monkey.task_scheduler.tasks import manual_run_change_finder from security_monkey.monitors import Monitor from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.auditors.iam.iam_role import IAMRoleAuditor test_account = Account(name="TEST_ACCOUNT1") watcher = IAMRole(accounts=[test_account.name]) technology = Technology(name="iamrole") db.session.add(technology) db.session.commit() watcher.batched_size = 3 # should loop 4 times ## CREATE MOCK IAM ROLES ## client = boto3.client("iam") aspd = { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": "sts:AssumeRole", "Principal": { "Service": "ec2.amazonaws.com" } }] } for x in range(0, 11): # Create the IAM Role via Moto: aspd["Statement"][0][ "Resource"] = ARN_PREFIX + ":iam:012345678910:role/roleNumber{}".format( x) client.create_role(Path="/", RoleName="roleNumber{}".format(x), AssumeRolePolicyDocument=json.dumps(aspd, indent=4)) client.put_role_policy(RoleName="roleNumber{}".format(x), PolicyName="testpolicy", PolicyDocument=json.dumps(OPEN_POLICY, indent=4)) # Set up the monitor: batched_monitor = Monitor(IAMRole, test_account) batched_monitor.watcher = watcher batched_monitor.auditors = [ IAMRoleAuditor(accounts=[test_account.name]) ] import security_monkey.task_scheduler.tasks old_get_monitors = security_monkey.task_scheduler.tasks.get_monitors security_monkey.task_scheduler.tasks.get_monitors = lambda x, y, z: [ batched_monitor ] # Moto screws up the IAM Role ARN -- so we need to fix it: original_slurp_list = watcher.slurp_list original_slurp = watcher.slurp def mock_slurp_list(): items, exception_map = original_slurp_list() for item in watcher.total_list: item["Arn"] = ARN_PREFIX + ":iam::012345678910:role/{}".format( item["RoleName"]) return items, exception_map def mock_slurp(): batched_items, exception_map = original_slurp() for item in batched_items: item.arn = ARN_PREFIX + ":iam::012345678910:role/{}".format( item.name) item.config["Arn"] = item.arn item.config["RoleId"] = item.name # Need this to stay the same return batched_items, exception_map watcher.slurp_list = mock_slurp_list watcher.slurp = mock_slurp manual_run_change_finder([test_account.name], [watcher.index]) assert mock_fix_orphaned.called # Check that all items were added to the DB: assert len(Item.query.all()) == 11 # Check that we have exactly 11 item revisions: assert len(ItemRevision.query.all()) == 11 # Check that there are audit issues for all 11 items: assert len(ItemAudit.query.all()) == 11 # Delete two of the items: managedPolicy = client.list_attached_role_policies( RoleName="roleNumber9") for each in managedPolicy['AttachedPolicies']: print("Detaching ", each) client.detach_role_policy(RoleName="roleNumber9", PolicyArn=each['PolicyArn']) inlinePolicy = client.list_role_policies(RoleName="roleNumber9") for each in inlinePolicy['PolicyNames']: print("Deleting ", each) client.delete_role_policy(RoleName="roleNumber9", PolicyName=each) instanceProfiles = client.list_instance_profiles_for_role( RoleName="roleNumber9") for each in instanceProfiles['InstanceProfiles']: print("Removing role from instance profile ", each) client.remove_role_from_instance_profile( RoleName="roleNumber9", InstanceProfileName=each['InstanceProfileName']) client.delete_role(RoleName="roleNumber9") managedPolicy = client.list_attached_role_policies( RoleName="roleNumber10") for each in managedPolicy['AttachedPolicies']: print("Detaching ", each) client.detach_role_policy(RoleName="roleNumber10", PolicyArn=each['PolicyArn']) inlinePolicy = client.list_role_policies(RoleName="roleNumber10") for each in inlinePolicy['PolicyNames']: print("Deleting ", each) client.delete_role_policy(RoleName="roleNumber10", PolicyName=each) instanceProfiles = client.list_instance_profiles_for_role( RoleName="roleNumber10") for each in instanceProfiles['InstanceProfiles']: print("Removing role from instance profile ", each) client.remove_role_from_instance_profile( RoleName="roleNumber10", InstanceProfileName=each['InstanceProfileName']) client.delete_role(RoleName="roleNumber10") # Run the it again: watcher.current_account = None # Need to reset the watcher manual_run_change_finder([test_account.name], [watcher.index]) # Check that nothing new was added: assert len(Item.query.all()) == 11 # There should be the same number of issues and 2 more revisions: assert len(ItemAudit.query.all()) == 11 assert len(ItemRevision.query.all()) == 13 # Check that the deleted roles show as being inactive: ir = ItemRevision.query.join((Item, ItemRevision.id == Item.latest_revision_id)) \ .filter(Item.arn.in_( [ARN_PREFIX + ":iam::012345678910:role/roleNumber9", ARN_PREFIX + ":iam::012345678910:role/roleNumber10"])).all() assert len(ir) == 2 assert not ir[0].active assert not ir[1].active # Finally -- test with a slurp list exception (just checking that things don't blow up): import security_monkey.watchers.iam.iam_role old_list_roles = security_monkey.watchers.iam.iam_role.list_roles def mock_slurp_list_with_exception(): security_monkey.watchers.iam.iam_role.list_roles = lambda **kwargs: 1 / 0 items, exception_map = original_slurp_list() assert len(exception_map) > 0 return items, exception_map watcher.slurp_list = mock_slurp_list_with_exception watcher.current_account = None # Need to reset the watcher manual_run_change_finder([test_account.name], [watcher.index]) security_monkey.task_scheduler.tasks.get_monitors = old_get_monitors security_monkey.watchers.iam.iam_role.list_roles = old_list_roles
def test_report_batch_changes(self): from security_monkey.alerter import Alerter from security_monkey.reporter import Reporter from security_monkey.datastore import Item, ItemRevision, ItemAudit from security_monkey.monitors import Monitor from security_monkey.watchers.iam.iam_role import IAMRole from security_monkey.auditors.iam.iam_role import IAMRoleAuditor account_type_result = AccountType.query.filter(AccountType.name == "AWS").one() db.session.add(account_type_result) db.session.commit() test_account = Account(name="TEST_ACCOUNT") watcher = IAMRole(accounts=[test_account.name]) db.session.commit() watcher.batched_size = 3 # should loop 4 times self.add_roles() # Set up the monitor: batched_monitor = Monitor(IAMRole, test_account) batched_monitor.watcher = watcher batched_monitor.auditors = [IAMRoleAuditor(accounts=[test_account.name])] # Set up the Reporter: import security_monkey.reporter old_all_monitors = security_monkey.reporter.all_monitors security_monkey.reporter.all_monitors = lambda x, y: [] test_reporter = Reporter() test_reporter.all_monitors = [batched_monitor] test_reporter.account_alerter = Alerter(watchers_auditors=test_reporter.all_monitors, account=test_account.name) import security_monkey.scheduler # import security_monkey.monitors # old_get_monitors = security_monkey.scheduler.get_monitors security_monkey.scheduler.get_monitors = lambda x, y, z: [batched_monitor] # Moto screws up the IAM Role ARN -- so we need to fix it: original_slurp_list = watcher.slurp_list original_slurp = watcher.slurp def mock_slurp_list(): exception_map = original_slurp_list() for item in watcher.total_list: item["Arn"] = "arn:aws:iam::012345678910:role/{}".format(item["RoleName"]) return exception_map def mock_slurp(): batched_items, exception_map = original_slurp() for item in batched_items: item.arn = "arn:aws:iam::012345678910:role/{}".format(item.name) item.config["Arn"] = item.arn item.config["RoleId"] = item.name # Need this to stay the same return batched_items, exception_map watcher.slurp_list = mock_slurp_list watcher.slurp = mock_slurp test_reporter.run(account=test_account.name) # Check that all items were added to the DB: assert len(Item.query.all()) == 11 # Check that we have exactly 11 item revisions: assert len(ItemRevision.query.all()) == 11 # Check that there are audit issues for all 11 items: assert len(ItemAudit.query.all()) == 11 mock_iam().stop() mock_sts().stop() # Something isn't cleaning itself up properly and causing other core tests to fail. # This is the solution: security_monkey.reporter.all_monitors = old_all_monitors import monitor_mock security_monkey.scheduler.get_monitors = monitor_mock.mock_get_monitors