2018-08-27 14:25:28 +00:00
|
|
|
/**
|
2020-08-11 20:46:54 +00:00
|
|
|
* Copyright (c) 2014-present, The osquery authors
|
2018-08-27 14:25:28 +00:00
|
|
|
*
|
2020-08-11 20:46:54 +00:00
|
|
|
* This source code is licensed as defined by the LICENSE file found in the
|
|
|
|
* root directory of this source tree.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
|
2018-08-27 14:25:28 +00:00
|
|
|
*/
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
#include <osquery/tests/integration/tables/helper.h>
|
|
|
|
|
2020-08-11 15:54:54 +00:00
|
|
|
#include <osquery/core/system.h>
|
|
|
|
#include <osquery/database/database.h>
|
|
|
|
#include <osquery/registry/registry.h>
|
2018-09-21 18:54:31 +00:00
|
|
|
|
|
|
|
#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>
|
2019-06-25 19:14:58 +00:00
|
|
|
|
|
|
|
// TODO(5591) Remove this when addressed by Boost's ASIO config.
|
|
|
|
// https://www.boost.org/doc/libs/1_67_0/boost/asio/detail/config.hpp
|
|
|
|
// Standard library support for std::string_view.
|
|
|
|
#define BOOST_ASIO_DISABLE_STD_STRING_VIEW 1
|
|
|
|
|
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
|
|
|
namespace table_tests {
|
|
|
|
|
2018-08-27 14:25:28 +00:00
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
2019-11-19 04:43:18 +00:00
|
|
|
std::string rowToString(const Row& x) {
|
|
|
|
std::stringstream o;
|
|
|
|
o << '{';
|
|
|
|
for (Row::const_iterator i = x.begin(); i != x.end(); i++) {
|
|
|
|
if (i != x.begin()) {
|
|
|
|
o << ", ";
|
|
|
|
}
|
|
|
|
o << i->first;
|
|
|
|
o << ": ";
|
|
|
|
o << boost::io::quoted(i->second);
|
|
|
|
}
|
|
|
|
o << '}';
|
|
|
|
return o.str();
|
|
|
|
}
|
|
|
|
|
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-09-26 03:35:15 +00:00
|
|
|
void validate_row(const Row& row, const ValidationMap& 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;
|
2019-09-26 03:35:15 +00:00
|
|
|
ValidationDataType validator = iter.second;
|
2018-08-29 19:16:00 +00:00
|
|
|
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)
|
2019-11-19 04:43:18 +00:00
|
|
|
<< " with value " << boost::io::quoted(value) << " failed"
|
|
|
|
<< std::endl
|
|
|
|
<< "Row: " << rowToString(row);
|
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)
|
2019-11-19 04:43:18 +00:00
|
|
|
<< " with value " << boost::io::quoted(value) << " failed"
|
|
|
|
<< std::endl
|
|
|
|
<< "Row: " << rowToString(row);
|
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-09-26 03:35:15 +00:00
|
|
|
const ValidationMap& 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;
|
|
|
|
}
|
|
|
|
|
2019-11-19 04:43:18 +00:00
|
|
|
bool validate_value_using_flags(const std::string& value, int flags) {
|
2019-12-13 01:45:15 +00:00
|
|
|
// Early return on EmptyOk
|
|
|
|
if ((flags & EmptyOk) > 0) {
|
|
|
|
if (value.length() == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Early return on NullOk
|
|
|
|
if ((flags & NullOk)) {
|
|
|
|
if (value == "null") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-27 14:25:28 +00:00
|
|
|
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
|
|
|
|
2020-06-06 01:22:44 +00:00
|
|
|
void validate_container_rows(const std::string& table_name,
|
2020-06-07 01:26:44 +00:00
|
|
|
ValidationMap& validation_map,
|
|
|
|
const std::string& sql_constraints) {
|
|
|
|
std::string extra_sql;
|
|
|
|
if (!sql_constraints.empty()) {
|
|
|
|
extra_sql = " where " + sql_constraints;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "select *, pid_with_namespace, mount_namespace_id from " +
|
|
|
|
table_name + extra_sql
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
auto rows =
|
|
|
|
execute_query("select *, pid_with_namespace, mount_namespace_id from " +
|
|
|
|
table_name + extra_sql);
|
2020-06-06 01:22:44 +00:00
|
|
|
validation_map["pid_with_namespace"] = IntType;
|
|
|
|
validation_map["mount_namespace_id"] = NormalType;
|
|
|
|
validate_rows(rows, validation_map);
|
|
|
|
}
|
|
|
|
|
2018-09-21 18:54:31 +00:00
|
|
|
void setUpEnvironment() {
|
2020-06-27 20:29:24 +00:00
|
|
|
platformSetup();
|
2018-09-21 18:54:31 +00:00
|
|
|
registryAndPluginInit();
|
2020-09-21 22:25:08 +00:00
|
|
|
initDatabasePluginForTesting();
|
2018-09-21 18:54:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace table_tests
|
2018-08-27 14:25:28 +00:00
|
|
|
} // namespace osquery
|