def testBlockableKey_NoParent(self): change = bit9.RuleChangeSet( rule_keys=[], change_type=constants.RULE_POLICY.WHITELIST, parent=None) with self.assertRaises(ValueError): change.put()
def testBlockableKey_NotABlockableKey(self): host = test_utils.CreateBit9Host() change = bit9.RuleChangeSet( rule_keys=[], change_type=constants.RULE_POLICY.WHITELIST, parent=host.key) with self.assertRaises(ValueError): change.put()
def testBlockableKey(self): change = bit9.RuleChangeSet( rule_keys=[], change_type=constants.RULE_POLICY.WHITELIST, parent=self.bit9_binary.key) change.put() self.assertEqual(self.bit9_binary.key, change.blockable_key)
def CreateRuleChangeSet(blockable_key, **kwargs): defaults = { 'change_type': constants.RULE_POLICY.BLACKLIST, 'rule_keys': [] } defaults.update(kwargs.copy()) change = bit9.RuleChangeSet(parent=blockable_key, **defaults) change.put() return change
def _CopyLocalRules(user_key, dest_host_id): """Copy over a user's local rules to a newly-associated host. NOTE: Because of the implementation of local whitelisting on Bit9, many of these new copied local rules will likely be initially unfulfilled, that is, held in Upvote and not saved to Bit9. Args: user_key: str, The user for whom the rules will be copied. dest_host_id: str, The ID of the host for which the new rules will be created. """ logging.info('Copying rules for %s to host %s', user_key.id(), dest_host_id) username = user_map.EmailToUsername(user_key.id()) host_query = bit9.Bit9Host.query(bit9.Bit9Host.users == username) src_host = yield host_query.get_async() if src_host is None: raise ndb.Return() assert src_host.key.id() != dest_host_id, ( 'User already associated with target host') # Get all local rules from that host. rules_query = bit9.Bit9Rule.query( bit9.Bit9Rule.host_id == src_host.key.id(), bit9.Bit9Rule.in_effect == True) # pylint: disable=g-explicit-bool-comparison # Get a rough idea of how many rules we're in for. Since this is a # non-critical query, we limit the max number to a fairly low bound. rule_count = yield rules_query.count_async(limit=250) logging.info('Retrieved %s%s rules to copy', '>' if rule_count == 250 else '', rule_count) # Copy the local rules to the new host. new_rules = [] for batch in query_utils.Paginate(rules_query): for rule in batch: new_rule = model_utils.CopyEntity(rule, new_parent=rule.key.parent(), host_id=dest_host_id, user_key=user_key) new_rules.append(new_rule) logging.info('Copying %s rules to new host', len(new_rules)) yield ndb.put_multi_async(new_rules) # Create the change sets necessary to submit the new rules to Bit9. changes = [] for new_rule in new_rules: change = bit9.RuleChangeSet(rule_keys=[new_rule.key], change_type=new_rule.policy, parent=new_rule.key.parent()) changes.append(change) logging.info('Creating %s RuleChangeSet', len(changes)) yield ndb.put_multi_async(changes)
def _CopyLocalRules(user_key, dest_host_id): """Copy over a user's local rules to a newly-associated host. NOTE: Because of the implementation of local whitelisting on Bit9, many of these new copied local rules will likely be initially unfulfilled, that is, held in Upvote and not saved to Bit9. Args: user_key: str, The user for whom the rules will be copied. dest_host_id: str, The ID of the host for which the new rules will be created. """ logging.info('Copying rules for user %s to host %s', user_key.id(), dest_host_id) # Query for a host belonging to the user. username = user_utils.EmailToUsername(user_key.id()) query = host_models.Bit9Host.query(host_models.Bit9Host.users == username) src_host = yield query.get_async() if src_host is None: logging.warning('User %s has no hosts to copy from', username) raise ndb.Return() src_host_id = src_host.key.id() # Query for all the Bit9Rules in effect for the given user on the chosen host. query = rule_models.Bit9Rule.query( rule_models.Bit9Rule.host_id == src_host_id, rule_models.Bit9Rule.user_key == user_key, rule_models.Bit9Rule.in_effect == True) # pylint: disable=g-explicit-bool-comparison, singleton-comparison src_rules = yield query.fetch_async() logging.info('Found a total of %d rule(s) for user %s', len(src_rules), user_key.id()) # Copy the local rules to the new host. logging.info('Copying %d rule(s) to host %s', len(src_rules), dest_host_id) new_rules = [] for src_rule in src_rules: new_rule = datastore_utils.CopyEntity(src_rule, new_parent=src_rule.key.parent(), host_id=dest_host_id, user_key=user_key) new_rules.append(new_rule) new_rule.InsertBigQueryRow() yield ndb.put_multi_async(new_rules) # Create the change sets necessary to submit the new rules to Bit9. changes = [] for new_rule in new_rules: change = bit9.RuleChangeSet(rule_keys=[new_rule.key], change_type=new_rule.policy, parent=new_rule.key.parent()) changes.append(change) logging.info('Creating %d RuleChangeSet(s)', len(changes)) yield ndb.put_multi_async(changes)
def _SetInstallerPolicy(self, blockable_id, new_policy): blockable = base_models.Blockable.get_by_id(blockable_id) # pylint: disable=g-explicit-bool-comparison, singleton-comparison installer_rule_query = rule_models.Bit9Rule.query( rule_models.Bit9Rule.in_effect == True, rule_models.Bit9Rule.policy.IN(constants.RULE_POLICY.SET_INSTALLER), ancestor=blockable.key) # pylint: enable=g-explicit-bool-comparison, singleton-comparison existing_rule = installer_rule_query.get() if existing_rule: if existing_rule.policy == new_policy: return blockable.is_installer else: existing_rule.in_effect = False existing_rule.put() # Create the Bit9Rule associated with the installer state and a change set # to commit it. new_rule = rule_models.Bit9Rule( rule_type=blockable.rule_type, in_effect=True, policy=new_policy, parent=blockable.key) new_rule.put() change = bit9_models.RuleChangeSet( rule_keys=[new_rule.key], change_type=new_rule.policy, parent=blockable.key) change.put() message = 'User %s changed installer state to %s' % ( self.user.key.id(), new_policy) tables.BINARY.InsertRow( sha256=blockable.key.id(), timestamp=datetime.datetime.utcnow(), action=constants.BLOCK_ACTION.COMMENT, state=blockable.state, score=blockable.score, platform=constants.PLATFORM.WINDOWS, client=constants.CLIENT.BIT9, first_seen_file_name=blockable.first_seen_name, cert_fingerprint=blockable.cert_id, is_compiler=blockable.is_compiler, comment=message) change_set.DeferCommitBlockableChangeSet(blockable.key) # Update the blockable's is_installer property. blockable.is_installer = new_policy == constants.RULE_POLICY.FORCE_INSTALLER blockable.put() return blockable.is_installer
def _CheckAndResolveAnomalousBlock(blockable_key, host_id): """Checks whether an unfulfilled rule already existed for this blockable. If there are unfulfilled rules, triggers an attempt to commit them back to the database. Args: blockable_key: The key of the blockable that was blocked. host_id: The host on which the block occurred. Returns: Whether the block was anomalous (i.e. whether an unfulfilled rule existed for the blockable-host pair). """ # Check and handle anomalous block events by detecting unfulfilled rules and, # if present, attempting to commit them. # pylint: disable=g-explicit-bool-comparison unfulfilled_rule_query = bit9.Bit9Rule.query( bit9.Bit9Rule.is_committed == True, bit9.Bit9Rule.is_fulfilled == False, bit9.Bit9Rule.host_id == host_id, ancestor=blockable_key).order(bit9.Bit9Rule.updated_dt) # pylint: enable=g-explicit-bool-comparison unfulfilled_rules = unfulfilled_rule_query.fetch() # Installer rules shouldn't be local (e.g. have host_id's) so they shouldn't # have been returned by the query. Still, the sanity check couldn't hurt. assert all(rule.policy in constants.RULE_POLICY.SET_EXECUTION for rule in unfulfilled_rules) if unfulfilled_rules: logging.info('Processing %s unfulfilled rules for %s', len(unfulfilled_rules), blockable_key.id()) # Mark all outstanding unfulfilled rules _except_ the most recent one as # fulfilled as we're going to ignore them. for rule in unfulfilled_rules[:-1]: rule.is_fulfilled = True # Mark the most recent unfulfilled rule as uncommitted as we're going to # commit it. unfulfilled_rules[-1].is_committed = False # Create and trigger a change set to commit the most recent rule. change = bit9.RuleChangeSet(rule_keys=[unfulfilled_rules[-1].key], change_type=unfulfilled_rules[-1].policy, parent=blockable_key) ndb.put_multi(unfulfilled_rules + [change]) change_set.DeferCommitBlockableChangeSet(blockable_key) return bool(unfulfilled_rules)
def _CreateRuleChangeSet(self, rules_future, new_policy): """Creates a RuleChangeSet and trigger an attempted commit.""" rules = rules_future.get_result() # If there are no rules to be created (rare but possible), we can just drop # the change set entirely. if not rules: return keys = [rule.key for rule in rules] change = bit9.RuleChangeSet( rule_keys=keys, change_type=new_policy, parent=self.blockable.key) change.put() # Attempt to commit the change set in a deferred task. # NOTE: If we're in a transaction, we should only send out the # request to Bit9 once the RuleChangeSet has been successfully created. # If we're not in a transaction, this executes immediately. ndb.get_context().call_on_commit(self._TriggerCommit)
def _SetInstallerPolicy(self, blockable_id, new_policy): blockable = base_db.Blockable.get_by_id(blockable_id) # pylint: disable=g-explicit-bool-comparison installer_rule_query = bit9_db.Bit9Rule.query( bit9_db.Bit9Rule.in_effect == True, bit9_db.Bit9Rule.policy.IN(constants.RULE_POLICY.SET_INSTALLER), ancestor=blockable.key) # pylint: enable=g-explicit-bool-comparison existing_rule = installer_rule_query.get() if existing_rule: if existing_rule.policy == new_policy: return blockable.is_installer else: existing_rule.in_effect = False existing_rule.put() # Create the Bit9Rule associated with the installer state and a change set # to commit it. new_rule = bit9_db.Bit9Rule( rule_type=blockable.rule_type, in_effect=True, policy=new_policy, parent=blockable.key) new_rule.put() change = bit9_db.RuleChangeSet( rule_keys=[new_rule.key], change_type=new_rule.policy, parent=blockable.key) change.put() base_db.AuditLog.Create( blockable, 'User %s changing installer state to %s' % ( self.user.key.id(), new_policy)) change_set.DeferCommitBlockableChangeSet(blockable.key) # Update the blockable's is_installer property. blockable.is_installer = new_policy == constants.RULE_POLICY.FORCE_INSTALLER blockable.put() return blockable.is_installer