2018-08-27 14:25:28 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
|
|
|
* 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.
|
2018-08-27 14:25:28 +00:00
|
|
|
*/
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
#include <osquery/tests/integration/tables/helper.h>
|
|
|
|
|
|
|
|
#include <osquery/database.h>
|
|
|
|
#include <osquery/registry.h>
|
|
|
|
#include <osquery/system.h>
|
|
|
|
|
|
|
|
#include <osquery/utils/conversions/tryto.h>
|
|
|
|
|
2018-08-27 14:25:28 +00:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
#include <boost/algorithm/string.hpp>
|
2018-08-31 13:28:26 +00:00
|
|
|
#include <boost/asio.hpp>
|
2018-08-27 14:25:28 +00:00
|
|
|
#include <boost/filesystem.hpp>
|
2018-08-30 17:15:51 +00:00
|
|
|
#include <boost/io/detail/quoted_manip.hpp>
|
2018-09-10 03:47:52 +00:00
|
|
|
#include <boost/regex.hpp>
|
2018-08-27 14:25:28 +00:00
|
|
|
#include <boost/uuid/string_generator.hpp>
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
#include <unordered_set>
|
2018-08-27 14:25:28 +00:00
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
DECLARE_bool(disable_database);
|
|
|
|
|
|
|
|
namespace table_tests {
|
|
|
|
|
2018-08-27 14:25:28 +00:00
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
2018-08-29 17:48:49 +00:00
|
|
|
bool CronValuesCheck::operator()(const std::string& string) const {
|
2018-08-29 15:16:23 +00:00
|
|
|
// Fast asterisk check, its most common
|
|
|
|
if (string == "*") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
// In case of special values like @reboot or @yearly time columns are empty
|
|
|
|
if (string.empty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-29 15:16:23 +00:00
|
|
|
// Specific value check
|
|
|
|
auto cast_result = tryTo<int64_t>(string);
|
|
|
|
if (cast_result) {
|
|
|
|
// its int, so we can do easy validation
|
|
|
|
int64_t int_value = cast_result.get();
|
|
|
|
return (int_value >= min_ && int_value <= max_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check */3 format
|
|
|
|
if (boost::starts_with(string, "*/")) {
|
|
|
|
std::string subvalue = string.substr(2);
|
|
|
|
auto subvalue_int = tryTo<int64_t>(subvalue);
|
|
|
|
return subvalue_int.isValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> components;
|
|
|
|
boost::split(components, string, boost::is_any_of(","));
|
|
|
|
for (auto component : components) {
|
|
|
|
// Predefined value check like: sun, mon
|
|
|
|
boost::algorithm::to_lower(component);
|
|
|
|
if (values_.find(component) != values_.end()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// just number
|
|
|
|
if (tryTo<int64_t>(component)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
std::vector<std::string> sub_components;
|
|
|
|
boost::split(sub_components, component, boost::is_any_of("-"));
|
|
|
|
if (sub_components.size() == 2) {
|
|
|
|
if (tryTo<int64_t>(sub_components[0]) &&
|
|
|
|
tryTo<int64_t>(sub_components[1])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// sub_components.size() > 2 || sub_components.size() == 1
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-29 17:48:49 +00:00
|
|
|
bool IntMinMaxCheck::operator()(const std::string& string) const {
|
2018-08-30 09:56:08 +00:00
|
|
|
auto cast_result = tryTo<int64_t>(string);
|
2018-08-27 14:25:28 +00:00
|
|
|
if (!cast_result) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-30 09:56:08 +00:00
|
|
|
auto const value = cast_result.get();
|
2018-08-27 14:25:28 +00:00
|
|
|
return value >= min_ && value <= max_;
|
|
|
|
}
|
|
|
|
|
2018-08-29 17:48:49 +00:00
|
|
|
bool SpecificValuesCheck::operator()(const std::string& string) const {
|
2018-08-27 14:25:28 +00:00
|
|
|
return set_.find(string) != set_.end();
|
|
|
|
}
|
|
|
|
|
2018-08-31 13:28:26 +00:00
|
|
|
bool verifyIpAddress(std::string const& value) {
|
|
|
|
auto err = boost::system::error_code{};
|
|
|
|
boost::asio::ip::make_address(value, err);
|
|
|
|
return !err;
|
|
|
|
}
|
|
|
|
|
2018-09-10 03:47:52 +00:00
|
|
|
bool verifyEmptyStringOrIpAddress(std::string const& value) {
|
|
|
|
return value.empty() ? true : verifyIpAddress(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool verifyMacAddress(std::string const& value) {
|
2018-12-10 15:08:02 +00:00
|
|
|
if (value == "incomplete") {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
boost::smatch match;
|
|
|
|
// IEEE 802: six groups of two hexadecimal digits, separated by '-' or ':'
|
|
|
|
boost::regex rxMacAddress("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$");
|
|
|
|
return boost::regex_match(value, match, rxMacAddress);
|
|
|
|
}
|
2018-09-10 03:47:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
bool verifyUidGid(std::string const& value) {
|
|
|
|
auto const id_exp = tryTo<int64_t>(value);
|
|
|
|
if (!id_exp) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return id_exp.get() >= -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
QueryData execute_query(std::string query) {
|
2019-03-04 17:12:22 +00:00
|
|
|
auto instance = SQLiteDBManager::get();
|
|
|
|
QueryData rows;
|
|
|
|
Status status = queryInternal(query, rows, instance);
|
|
|
|
EXPECT_TRUE(status.ok()) << "Query execution failed with error: "
|
|
|
|
<< boost::io::quoted(status.what());
|
|
|
|
return rows;
|
2018-08-27 14:25:28 +00:00
|
|
|
}
|
|
|
|
|
2019-03-04 17:12:22 +00:00
|
|
|
void validate_row(const Row& row, const ValidatatioMap& validation_map) {
|
2018-08-31 13:50:49 +00:00
|
|
|
for (auto const& rec : row) {
|
|
|
|
EXPECT_NE(validation_map.count(rec.first), std::size_t{0})
|
|
|
|
<< "Unexpected column " << boost::io::quoted(rec.first) << " in a row";
|
|
|
|
}
|
2018-08-29 19:16:00 +00:00
|
|
|
for (auto iter : validation_map) {
|
|
|
|
std::string key = iter.first;
|
|
|
|
auto row_data_iter = row.find(key);
|
|
|
|
ASSERT_NE(row_data_iter, row.end())
|
2018-08-30 17:15:51 +00:00
|
|
|
<< "Could not find column " << boost::io::quoted(key)
|
|
|
|
<< " in the generated columns";
|
2018-08-29 19:16:00 +00:00
|
|
|
std::string value = row_data_iter->second;
|
|
|
|
ValidatatioDataType validator = iter.second;
|
|
|
|
if (validator.type() == typeid(int)) {
|
|
|
|
int flags = boost::get<int>(validator);
|
|
|
|
ASSERT_TRUE(validate_value_using_flags(value, flags))
|
2018-08-30 17:15:51 +00:00
|
|
|
<< "Standard validator of the column " << boost::io::quoted(key)
|
|
|
|
<< " with value " << boost::io::quoted(value) << " failed";
|
2018-08-29 19:16:00 +00:00
|
|
|
} else {
|
|
|
|
ASSERT_TRUE(boost::get<CustomCheckerType>(validator)(value))
|
2018-08-30 17:15:51 +00:00
|
|
|
<< "Custom validator of the column " << boost::io::quoted(key)
|
|
|
|
<< " with value " << boost::io::quoted(value) << " failed";
|
2018-08-29 19:16:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-21 18:54:31 +00:00
|
|
|
void validate_rows(const std::vector<Row>& rows,
|
2019-03-04 17:12:22 +00:00
|
|
|
const ValidatatioMap& validation_map) {
|
2018-08-27 14:25:28 +00:00
|
|
|
for (auto row : rows) {
|
2018-08-29 19:16:00 +00:00
|
|
|
validate_row(row, validation_map);
|
2018-08-27 14:25:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
bool is_valid_hex(const std::string& value) {
|
2018-08-27 14:25:28 +00:00
|
|
|
for (auto ch : value) {
|
|
|
|
if (!std::isxdigit(ch)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
bool validate_value_using_flags(const std::string& value,
|
2018-08-27 14:25:28 +00:00
|
|
|
int flags) {
|
|
|
|
if ((flags & NonEmpty) > 0) {
|
|
|
|
if (value.length() == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & NonNull)) {
|
|
|
|
if (value == "null") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & NonZero)) {
|
|
|
|
if (value == "0") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & IntType) > 0) {
|
2018-08-29 11:53:38 +00:00
|
|
|
auto cast_result = tryTo<int64_t>(value);
|
2018-08-27 14:25:28 +00:00
|
|
|
if (!cast_result) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-30 09:56:08 +00:00
|
|
|
auto intValue = cast_result.get();
|
2018-11-13 13:28:26 +00:00
|
|
|
if ((flags & NonNegativeInt) == NonNegativeInt) {
|
2018-08-27 14:25:28 +00:00
|
|
|
if (intValue < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-11-13 13:28:26 +00:00
|
|
|
if ((flags & NonNegativeOrErrorInt) == NonNegativeOrErrorInt) {
|
2018-09-04 12:48:12 +00:00
|
|
|
if (intValue < -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-08-27 14:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & FileOnDisk) > 0) {
|
|
|
|
auto path = fs::path(value);
|
|
|
|
auto status = fs::status(path);
|
|
|
|
if (!fs::exists(status) || !fs::is_regular_file(status)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & DirectoryOnDisk) > 0) {
|
|
|
|
auto path = fs::path(value);
|
|
|
|
auto status = fs::status(path);
|
|
|
|
if (!fs::exists(status) || !fs::is_directory(status)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & MD5) > 0) {
|
|
|
|
if (!is_valid_hex(value) || value.size() != 32) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & SHA1) > 0) {
|
|
|
|
if (!is_valid_hex(value) || value.size() != 40) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & SHA256) > 0) {
|
|
|
|
if (!is_valid_hex(value) || value.size() != 64) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & Bool) > 0) {
|
|
|
|
if (value.length() != 1 || (value != "1" && value != "0")) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & ValidUUID) > 0) {
|
|
|
|
try {
|
|
|
|
boost::uuids::string_generator()(value);
|
|
|
|
} catch (...) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2018-09-21 18:54:31 +00:00
|
|
|
|
|
|
|
void setUpEnvironment() {
|
|
|
|
Initializer::platformSetup();
|
|
|
|
registryAndPluginInit();
|
|
|
|
FLAGS_disable_database = true;
|
|
|
|
DatabasePlugin::setAllowOpen(true);
|
|
|
|
DatabasePlugin::initPlugin();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace table_tests
|
2018-08-27 14:25:28 +00:00
|
|
|
} // namespace osquery
|