2017-12-19 00:04:06 +00:00
|
|
|
/**
|
2016-02-11 19:48:58 +00:00
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
2016-01-21 08:23:05 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
2019-02-19 18:52:19 +00:00
|
|
|
* This source code is licensed in accordance with the terms specified in
|
|
|
|
* the LICENSE file found in the root directory of this source tree.
|
2016-01-21 08:23:05 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <osquery/database.h>
|
2018-09-21 18:54:31 +00:00
|
|
|
#include <osquery/flags.h>
|
|
|
|
#include <osquery/registry.h>
|
|
|
|
#include <osquery/system.h>
|
2016-01-21 08:23:05 +00:00
|
|
|
|
2019-01-14 11:31:17 +00:00
|
|
|
#include <osquery/utils/json/json.h>
|
2016-01-21 08:23:05 +00:00
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
#include <gtest/gtest.h>
|
2018-03-21 16:39:50 +00:00
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
#include <limits>
|
|
|
|
|
2018-03-21 16:39:50 +00:00
|
|
|
namespace rj = rapidjson;
|
|
|
|
|
2016-01-21 08:23:05 +00:00
|
|
|
namespace osquery {
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
DECLARE_bool(disable_database);
|
|
|
|
|
|
|
|
class DatabaseTests : public testing::Test {
|
|
|
|
public:
|
|
|
|
void SetUp() override {
|
|
|
|
Initializer::platformSetup();
|
|
|
|
registryAndPluginInit();
|
|
|
|
|
|
|
|
// Force registry to use ephemeral database plugin
|
|
|
|
FLAGS_disable_database = true;
|
|
|
|
DatabasePlugin::setAllowOpen(true);
|
|
|
|
DatabasePlugin::initPlugin();
|
|
|
|
}
|
|
|
|
};
|
2016-01-21 08:23:05 +00:00
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
TEST_F(DatabaseTests, test_set_value_str) {
|
|
|
|
auto s = setDatabaseValue(kLogs, "str", "{}");
|
2016-01-21 08:23:05 +00:00
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
}
|
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
TEST_F(DatabaseTests, test_set_value_int) {
|
|
|
|
auto s = setDatabaseValue(kLogs, "int", -1);
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
}
|
2016-01-21 08:23:05 +00:00
|
|
|
|
2018-07-12 14:10:52 +00:00
|
|
|
TEST_F(DatabaseTests, test_set_str_batch) {
|
|
|
|
DatabaseStringValueList batch = {
|
2018-08-06 11:49:07 +00:00
|
|
|
{"str1", "{a}"}, {"str2", "{b}"}, {"str3", "{c}"}};
|
2018-07-12 14:10:52 +00:00
|
|
|
|
|
|
|
auto s = setDatabaseBatch(kLogs, batch);
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
}
|
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
TEST_F(DatabaseTests, test_set_value_mix1) {
|
|
|
|
auto s = setDatabaseValue(kLogs, "intstr", -1);
|
|
|
|
EXPECT_TRUE(s.ok());
|
2016-01-21 08:23:05 +00:00
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
s = setDatabaseValue(kLogs, "intstr", "{}");
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DatabaseTests, test_set_value_mix2) {
|
|
|
|
auto s = setDatabaseValue(kLogs, "strint", "{}");
|
2016-01-21 08:23:05 +00:00
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
s = setDatabaseValue(kLogs, "strint", -1);
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DatabaseTests, test_get_value_does_not_exist) {
|
2016-01-21 08:23:05 +00:00
|
|
|
// Unknown keys return failed, but will return empty data.
|
2018-06-02 19:38:29 +00:00
|
|
|
std::string value;
|
|
|
|
auto s = getDatabaseValue(kLogs, "does_not_exist", value);
|
2016-01-21 08:23:05 +00:00
|
|
|
EXPECT_FALSE(s.ok());
|
|
|
|
EXPECT_TRUE(value.empty());
|
|
|
|
}
|
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
TEST_F(DatabaseTests, test_get_value_str) {
|
|
|
|
std::string expected;
|
|
|
|
for (unsigned char i = std::numeric_limits<unsigned char>::min();
|
|
|
|
i < std::numeric_limits<unsigned char>::max();
|
|
|
|
i++) {
|
|
|
|
if (std::isprint(i)) {
|
|
|
|
expected += i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setDatabaseValue(kLogs, "str", expected);
|
|
|
|
|
|
|
|
std::string value;
|
|
|
|
auto s = getDatabaseValue(kLogs, "str", value);
|
|
|
|
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
EXPECT_EQ(value, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DatabaseTests, test_get_value_int) {
|
|
|
|
int expected = std::numeric_limits<int>::min();
|
|
|
|
setDatabaseValue(kLogs, "int", expected);
|
|
|
|
|
|
|
|
int value = 0;
|
|
|
|
auto s = getDatabaseValue(kLogs, "int", value);
|
|
|
|
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
EXPECT_EQ(value, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DatabaseTests, test_get_value_mix1) {
|
|
|
|
int expected = std::numeric_limits<int>::max();
|
|
|
|
setDatabaseValue(kLogs, "strint", "{}");
|
|
|
|
setDatabaseValue(kLogs, "strint", expected);
|
|
|
|
|
|
|
|
int value;
|
|
|
|
auto s = getDatabaseValue(kLogs, "strint", value);
|
|
|
|
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
EXPECT_EQ(value, expected);
|
|
|
|
}
|
|
|
|
|
2018-07-12 14:10:52 +00:00
|
|
|
TEST_F(DatabaseTests, test_get_str_batch) {
|
|
|
|
DatabaseStringValueList batch = {
|
2018-08-06 11:49:07 +00:00
|
|
|
{"str1", "{a}"}, {"str2", "{b}"}, {"str3", "{c}"}};
|
2018-07-12 14:10:52 +00:00
|
|
|
auto s = setDatabaseBatch(kLogs, batch);
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
|
|
|
|
for (const auto& p : batch) {
|
|
|
|
const auto& key = p.first;
|
|
|
|
const auto& expected_value = p.second;
|
|
|
|
|
|
|
|
std::string value;
|
|
|
|
s = getDatabaseValue(kLogs, key, value);
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
|
|
|
|
EXPECT_EQ(value, expected_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
TEST_F(DatabaseTests, test_get_value_mix2) {
|
|
|
|
std::string expected = "{}";
|
|
|
|
setDatabaseValue(kLogs, "intstr", -1);
|
|
|
|
setDatabaseValue(kLogs, "intstr", expected);
|
|
|
|
|
|
|
|
std::string value;
|
|
|
|
auto s = getDatabaseValue(kLogs, "intstr", value);
|
|
|
|
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
EXPECT_EQ(value, expected);
|
|
|
|
}
|
|
|
|
|
2016-01-21 08:23:05 +00:00
|
|
|
TEST_F(DatabaseTests, test_scan_values) {
|
|
|
|
setDatabaseValue(kLogs, "1", "0");
|
2018-06-02 19:38:29 +00:00
|
|
|
setDatabaseValue(kLogs, "2", 0);
|
2016-01-21 08:23:05 +00:00
|
|
|
setDatabaseValue(kLogs, "3", "0");
|
|
|
|
|
|
|
|
std::vector<std::string> keys;
|
|
|
|
auto s = scanDatabaseKeys(kLogs, keys);
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
EXPECT_GT(keys.size(), 2U);
|
|
|
|
|
|
|
|
keys.clear();
|
2018-06-02 19:38:29 +00:00
|
|
|
s = scanDatabaseKeys(kLogs, keys, 3);
|
2016-01-21 08:23:05 +00:00
|
|
|
EXPECT_TRUE(s.ok());
|
2018-06-02 19:38:29 +00:00
|
|
|
EXPECT_EQ(keys.size(), 3U);
|
2016-01-21 08:23:05 +00:00
|
|
|
}
|
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
TEST_F(DatabaseTests, test_delete_values_str) {
|
2016-01-21 08:23:05 +00:00
|
|
|
setDatabaseValue(kLogs, "k", "0");
|
|
|
|
|
|
|
|
std::string value;
|
|
|
|
getDatabaseValue(kLogs, "k", value);
|
|
|
|
EXPECT_FALSE(value.empty());
|
|
|
|
|
|
|
|
auto s = deleteDatabaseValue(kLogs, "k");
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
|
|
|
|
// Make sure the key has been deleted.
|
|
|
|
value.clear();
|
|
|
|
s = getDatabaseValue(kLogs, "k", value);
|
|
|
|
EXPECT_FALSE(s.ok());
|
|
|
|
EXPECT_TRUE(value.empty());
|
|
|
|
}
|
2018-03-21 16:39:50 +00:00
|
|
|
|
2018-06-02 19:38:29 +00:00
|
|
|
TEST_F(DatabaseTests, test_delete_values_int) {
|
|
|
|
int expected = 0;
|
|
|
|
setDatabaseValue(kLogs, "k", expected);
|
|
|
|
|
|
|
|
int value;
|
|
|
|
getDatabaseValue(kLogs, "k", value);
|
|
|
|
EXPECT_EQ(value, expected);
|
|
|
|
|
|
|
|
auto s = deleteDatabaseValue(kLogs, "k");
|
|
|
|
EXPECT_TRUE(s.ok());
|
|
|
|
|
|
|
|
// Make sure the key has been deleted.
|
|
|
|
value = -5;
|
|
|
|
s = getDatabaseValue(kLogs, "k", value);
|
|
|
|
EXPECT_FALSE(s.ok());
|
|
|
|
EXPECT_EQ(value, -5);
|
|
|
|
}
|
|
|
|
|
2018-04-24 20:39:22 +00:00
|
|
|
TEST_F(DatabaseTests, test_ptree_upgrade_to_rj_empty_v0v1) {
|
|
|
|
auto empty_results{"{}"};
|
|
|
|
auto status = setDatabaseValue(kQueries, "old_empty_results", empty_results);
|
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
|
|
|
|
// Stage our database to be pre-upgrade to ensure the logic runs
|
2018-07-30 13:25:07 +00:00
|
|
|
status = setDatabaseValue(kPersistentSettings, kDbVersionKey, "0");
|
2018-04-24 20:39:22 +00:00
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
|
2018-07-30 13:25:07 +00:00
|
|
|
status = upgradeDatabase(1);
|
2018-04-24 20:39:22 +00:00
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
|
|
|
|
std::string new_empty_list;
|
|
|
|
status = getDatabaseValue(kQueries, "old_empty_results", new_empty_list);
|
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
|
|
|
|
rj::Document empty_list;
|
|
|
|
EXPECT_FALSE(empty_list.Parse(new_empty_list).HasParseError());
|
|
|
|
EXPECT_TRUE(empty_list.IsArray());
|
|
|
|
|
|
|
|
// Expect our DB upgrade logic to have been set
|
|
|
|
std::string db_results_version{""};
|
2018-07-30 13:25:07 +00:00
|
|
|
getDatabaseValue(kPersistentSettings, kDbVersionKey, db_results_version);
|
|
|
|
EXPECT_EQ(db_results_version, "1");
|
2018-04-24 20:39:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DatabaseTests, test_ptree_upgrade_to_rj_results_v0v1) {
|
2018-03-21 16:39:50 +00:00
|
|
|
auto bad_json =
|
|
|
|
"{\"\":{\"disabled\":\"0\",\"network_name\":\"BTWifi-Starbucks\"},\"\":{"
|
|
|
|
"\"disabled\":\"0\",\"network_name\":\"Lobo-Guest\"},\"\":{\"disabled\":"
|
|
|
|
"\"0\",\"network_name\":\"GoogleGuest\"}}";
|
|
|
|
auto status = setDatabaseValue(kQueries, "bad_wifi_json", bad_json);
|
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
|
|
|
|
// Add an integer value to ensure we don't munge non-json objects
|
|
|
|
status = setDatabaseValue(kQueries, "bad_wifi_jsonepoch", "1521583712");
|
2018-04-24 20:39:22 +00:00
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
|
|
|
|
// Stage our database to be pre-upgrade to ensure the logic runs
|
2018-07-30 13:25:07 +00:00
|
|
|
status = setDatabaseValue(kPersistentSettings, kDbVersionKey, "0");
|
2018-04-24 20:39:22 +00:00
|
|
|
EXPECT_TRUE(status.ok());
|
2018-03-21 16:39:50 +00:00
|
|
|
|
|
|
|
rj::Document bad_doc;
|
|
|
|
|
|
|
|
// Potential bug with RJ, in that parsing should fail with empty keys
|
|
|
|
// EXPECT_TRUE(bad_doc.Parse(bad_json).HasParseError());
|
|
|
|
EXPECT_FALSE(bad_doc.IsArray());
|
|
|
|
|
2018-07-30 13:25:07 +00:00
|
|
|
status = upgradeDatabase(1);
|
2018-03-21 16:39:50 +00:00
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
|
|
|
|
std::string good_json;
|
|
|
|
status = getDatabaseValue(kQueries, "bad_wifi_json", good_json);
|
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
|
|
|
|
rj::Document clean_doc;
|
|
|
|
EXPECT_FALSE(clean_doc.Parse(good_json).HasParseError());
|
|
|
|
EXPECT_TRUE(clean_doc.IsArray());
|
|
|
|
EXPECT_EQ(clean_doc.Size(), 3U);
|
|
|
|
|
|
|
|
// Ensure our non-json thing was not destroyed
|
|
|
|
std::string query_epoch{""};
|
|
|
|
status = getDatabaseValue(kQueries, "bad_wifi_jsonepoch", query_epoch);
|
|
|
|
auto ulepoch = std::stoull(query_epoch);
|
|
|
|
EXPECT_EQ(ulepoch, 1521583712U);
|
2018-04-24 20:39:22 +00:00
|
|
|
|
|
|
|
// Expect our DB upgrade logic to have been set
|
|
|
|
std::string db_results_version{""};
|
|
|
|
getDatabaseValue(kPersistentSettings, "results_version", db_results_version);
|
2018-07-30 13:25:07 +00:00
|
|
|
EXPECT_EQ(db_results_version, "1");
|
2018-03-21 16:39:50 +00:00
|
|
|
}
|
2018-08-01 23:20:37 +00:00
|
|
|
|
|
|
|
TEST_F(DatabaseTests, test_migration_v1v2) {
|
|
|
|
/* Testing migration from 1 to 2 */
|
|
|
|
Status status = setDatabaseValue(kPersistentSettings, kDbVersionKey, "1");
|
|
|
|
ASSERT_TRUE(status.ok());
|
|
|
|
|
|
|
|
status = setDatabaseValue(
|
|
|
|
kEvents, "data.audit.process_events.0123456789", "event_data");
|
|
|
|
ASSERT_TRUE(status.ok());
|
|
|
|
|
|
|
|
status = upgradeDatabase(2);
|
|
|
|
ASSERT_TRUE(status.ok());
|
|
|
|
|
|
|
|
std::string value;
|
|
|
|
status = getDatabaseValue(kPersistentSettings, kDbVersionKey, value);
|
|
|
|
EXPECT_EQ(value, "2");
|
|
|
|
|
|
|
|
status =
|
|
|
|
getDatabaseValue(kEvents, "data.audit.process_events.0123456789", value);
|
|
|
|
EXPECT_FALSE(status.ok());
|
|
|
|
|
|
|
|
status = getDatabaseValue(
|
|
|
|
kEvents, "data.auditeventpublisher.process_events.0123456789", value);
|
|
|
|
EXPECT_TRUE(status.ok());
|
|
|
|
EXPECT_EQ(value, "event_data");
|
|
|
|
}
|
|
|
|
|
2018-07-12 14:10:52 +00:00
|
|
|
} // namespace osquery
|