TEST_F(ConfigTests, test_config_parser) {
  // Register a config parser plugin.
  Registry::add<TestConfigParserPlugin>("config_parser", "test");
  Registry::get("config_parser", "test")->setUp();

  {
    // Access the parser's data without having updated the configuration.
    ConfigDataInstance config;
    const auto& test_data = config.getParsedData("test");

    // Expect the setUp method to have run and set blank defaults.
    // Accessing an invalid property tree key will abort.
    ASSERT_EQ(test_data.get_child("dictionary").count(""), 0);
  }

  // Update or load the config, expect the parser to be called.
  Config::update(
      {{"source1",
        "{\"dictionary\": {\"key1\": \"value1\"}, \"list\": [\"first\"]}"}});
  ASSERT_TRUE(TestConfigParserPlugin::update_called);

  {
    // Now access the parser's data AFTER updating the config (no longer blank)
    ConfigDataInstance config;
    const auto& test_data = config.getParsedData("test");

    // Expect a value that existed in the configuration.
    EXPECT_EQ(test_data.count("dictionary"), 1);
    EXPECT_EQ(test_data.get("dictionary.key1", ""), "value1");
    // Expect a value for every key the parser requested.
    // Every requested key will be present, event if the key's tree is empty.
    EXPECT_EQ(test_data.count("dictionary2"), 1);
    // Expect the parser-created data item.
    EXPECT_EQ(test_data.count("dictionary3"), 1);
    EXPECT_EQ(test_data.get("dictionary3.key2", ""), "value2");
  }

  // Update from a secondary source into a dictionary.
  // Expect that the keys in the top-level dictionary are merged.
  Config::update({{"source2", "{\"dictionary\": {\"key3\": \"value3\"}}"}});
  // Update from a third source into a list.
  // Expect that the items from each source in the top-level list are merged.
  Config::update({{"source3", "{\"list\": [\"second\"]}"}});

  {
    ConfigDataInstance config;
    const auto& test_data = config.getParsedData("test");

    EXPECT_EQ(test_data.count("dictionary"), 1);
    EXPECT_EQ(test_data.get("dictionary.key1", ""), "value1");
    EXPECT_EQ(test_data.get("dictionary.key3", ""), "value3");
    EXPECT_EQ(test_data.count("list"), 1);
    EXPECT_EQ(test_data.get_child("list").count(""), 2);
  }
}
Exemple #2
0
Status YARAEventSubscriber::init() {
  Status status;

  ConfigDataInstance config;
  const auto& yara_config = config.getParsedData("yara");
  if (yara_config.count("file_paths") == 0)
    return Status(0, "OK");
  const auto& yara_paths = yara_config.get_child("file_paths");
  const auto& file_map = config.files();
  for (const auto& yara_path_element : yara_paths) {
    // Subscribe to each file for the given key (category).
    if (file_map.count(yara_path_element.first) == 0) {
      VLOG(1) << "Key in yara.file_paths not found in file_paths: "
              << yara_path_element.first;
      continue;
    }

    for (const auto& file : file_map.at(yara_path_element.first)) {
      VLOG(1) << "Added YARA listener to: " << file;
      auto mc = createSubscriptionContext();
      mc->path = file;
      mc->mask = FILE_CHANGE_MASK;
      mc->recursive = true;
      subscribe(&YARAEventSubscriber::Callback,
                mc,
                (void*)(&yara_path_element.first));
    }
  }

  return Status(0, "OK");
}
Exemple #3
0
Status YARAEventSubscriber::Callback(const FileEventContextRef& ec,
                                     const void* user_data) {
  if (user_data == nullptr) {
    return Status(1, "No YARA category string provided");
  }

  if (ec->action != "UPDATED" && ec->action != "CREATED") {
    return Status(1, "Invalid action");
  }

  Row r;
  r["action"] = ec->action;
  r["target_path"] = ec->path;
  r["category"] = *(std::string*)user_data;

  // Only FSEvents transactions updates (inotify is a no-op).
  r["transaction_id"] = INTEGER(ec->transaction_id);

  // These are default values, to be updated in YARACallback.
  r["count"] = INTEGER(0);
  r["matches"] = std::string("");
  r["strings"] = std::string("");
  r["tags"] = std::string("");

  ConfigDataInstance config;
  const auto& parser = config.getParser("yara");
  if (parser == nullptr)
    return Status(1, "ConfigParser unknown.");
  const auto& yaraParser = std::static_pointer_cast<YARAConfigParserPlugin>(parser);
  auto rules = yaraParser->rules();

  // Use the category as a lookup into the yara file_paths. The value will be
  // a list of signature groups to scan with.
  auto category = r.at("category");
  const auto& yara_config = config.getParsedData("yara");
  const auto& yara_paths = yara_config.get_child("file_paths");
  const auto& sig_groups = yara_paths.find(category);
  for (const auto& rule : sig_groups->second) {
    const std::string group = rule.second.data();
    int result = yr_rules_scan_file(rules[group],
                                    ec->path.c_str(),
                                    SCAN_FLAGS_FAST_MODE,
                                    YARACallback,
                                    (void*)&r,
                                    0);

    if (result != ERROR_SUCCESS) {
      return Status(1, "YARA error: " + std::to_string(result));
    }
  }

  if (ec->action != "" && r.at("matches").size() > 0) {
    add(r, ec->time);
  }

  return Status(0, "OK");
}