Beispiel #1
0
 def testBlockableKey_NoParent(self):
     change = rule_models.RuleChangeSet(
         rule_keys=[],
         change_type=constants.RULE_POLICY.WHITELIST,
         parent=None)
     with self.assertRaises(ValueError):
         change.put()
Beispiel #2
0
 def testBlockableKey_NotABlockableKey(self):
     host = test_utils.CreateBit9Host()
     change = rule_models.RuleChangeSet(
         rule_keys=[],
         change_type=constants.RULE_POLICY.WHITELIST,
         parent=host.key)
     with self.assertRaises(ValueError):
         change.put()
Beispiel #3
0
    def testBlockableKey(self):
        change = rule_models.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)
Beispiel #4
0
def CreateRuleChangeSet(blockable_key, **kwargs):
    defaults = {
        'change_type': constants.RULE_POLICY.BLACKLIST,
        'rule_keys': []
    }
    defaults.update(kwargs.copy())
    change = rule_models.RuleChangeSet(parent=blockable_key, **defaults)
    change.put()
    return change
Beispiel #5
0
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 = rule_models.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)
Beispiel #6
0
    def _SetInstallerPolicy(self, blockable_id, new_policy):
        blockable = binary_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 = rule_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,
                                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
Beispiel #7
0
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, singleton-comparison
  unfulfilled_rule_query = rule_models.Bit9Rule.query(
      rule_models.Bit9Rule.is_committed == True,
      rule_models.Bit9Rule.is_fulfilled == False,
      rule_models.Bit9Rule.host_id == host_id,
      ancestor=blockable_key
  ).order(rule_models.Bit9Rule.updated_dt)
  # pylint: enable=g-explicit-bool-comparison, singleton-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 _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

    # Revise the Rule creation time to now. This will ensure that this
    # unfulfilled Rule will once again get picked up by the 'fast' and 'slow'
    # retry crons below. This should help fulfill such Rules in a *slightly*
    # more timely manner, in cases where an unfulfilled Rule ages out of the
    # week-long retry period, but is later executed by the corresponding user.
    unfulfilled_rules[-1].recorded_dt = datetime.datetime.utcnow()

    # Create and trigger a change set to commit the most recent rule.
    change = rule_models.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)