mirror of
https://github.com/falcosecurity/falco.git
synced 2025-07-09 12:43:31 +00:00
Add a test for checking that compile_output::clone() returns equal values, specifically in the case of derived values. Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
357 lines
11 KiB
C++
357 lines
11 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
/*
|
|
Copyright (C) 2023 The Falco Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <sinsp.h>
|
|
#include <filter_check_list.h>
|
|
#include <filter.h>
|
|
#include <eventformatter.h>
|
|
|
|
#include <falco_engine.h>
|
|
#include "indexed_vector.h"
|
|
#include "evttype_index_ruleset.h"
|
|
|
|
#include "rule_loader_reader.h"
|
|
#include "rule_loader_collector.h"
|
|
#include "rule_loader_compiler.h"
|
|
|
|
namespace {
|
|
|
|
struct test_object_info {
|
|
std::string name;
|
|
std::string property;
|
|
};
|
|
|
|
struct test_compile_output : public rule_loader::compile_output {
|
|
test_compile_output() = default;
|
|
virtual ~test_compile_output() = default;
|
|
virtual std::unique_ptr<compile_output> clone() const override {
|
|
return std::make_unique<test_compile_output>(*this);
|
|
}
|
|
|
|
std::set<std::string> defined_test_properties;
|
|
};
|
|
|
|
class test_compiler : public rule_loader::compiler {
|
|
public:
|
|
test_compiler() = default;
|
|
virtual ~test_compiler() = default;
|
|
|
|
std::unique_ptr<rule_loader::compile_output> new_compile_output() override {
|
|
return std::make_unique<test_compile_output>();
|
|
}
|
|
|
|
void compile(rule_loader::configuration& cfg,
|
|
const rule_loader::collector& col,
|
|
rule_loader::compile_output& out) const override;
|
|
};
|
|
|
|
class test_collector : public rule_loader::collector {
|
|
public:
|
|
test_collector() = default;
|
|
virtual ~test_collector() = default;
|
|
|
|
indexed_vector<test_object_info> test_object_infos;
|
|
};
|
|
|
|
class test_reader : public rule_loader::reader {
|
|
public:
|
|
test_reader() = default;
|
|
virtual ~test_reader() = default;
|
|
|
|
protected:
|
|
rule_loader::context create_context(const YAML::Node& item,
|
|
const rule_loader::context& parent) {
|
|
return rule_loader::context(item,
|
|
rule_loader::context::EXTENSION_ITEM,
|
|
"test object",
|
|
parent);
|
|
};
|
|
|
|
void read_item(rule_loader::configuration& cfg,
|
|
rule_loader::collector& collector,
|
|
const YAML::Node& item,
|
|
const rule_loader::context& parent) override {
|
|
test_collector& test_col = dynamic_cast<test_collector&>(collector);
|
|
|
|
if(item["test_object"].IsDefined()) {
|
|
rule_loader::context tmp = create_context(item, parent);
|
|
test_object_info obj;
|
|
std::string name;
|
|
std::string property;
|
|
|
|
decode_val(item, "test_object", name, tmp);
|
|
decode_val(item, "property", property, tmp);
|
|
|
|
obj.name = name;
|
|
obj.property = property;
|
|
|
|
test_col.test_object_infos.insert(obj, obj.name);
|
|
} else {
|
|
rule_loader::reader::read_item(cfg, collector, item, parent);
|
|
}
|
|
};
|
|
};
|
|
|
|
class test_ruleset : public evttype_index_ruleset {
|
|
public:
|
|
explicit test_ruleset(std::shared_ptr<sinsp_filter_factory> factory):
|
|
evttype_index_ruleset(factory) {};
|
|
virtual ~test_ruleset() = default;
|
|
|
|
void add_compile_output(const rule_loader::compile_output& compile_output,
|
|
falco_common::priority_type min_priority,
|
|
const std::string& source) {
|
|
evttype_index_ruleset::add_compile_output(compile_output, min_priority, source);
|
|
|
|
std::shared_ptr<filter_ruleset> ruleset;
|
|
get_engine_state().get_ruleset(source, ruleset);
|
|
EXPECT_EQ(this, ruleset.get());
|
|
|
|
const test_compile_output& test_output =
|
|
dynamic_cast<const test_compile_output&>(compile_output);
|
|
|
|
defined_properties = test_output.defined_test_properties;
|
|
};
|
|
|
|
std::set<std::string> defined_properties;
|
|
};
|
|
|
|
class test_ruleset_factory : public filter_ruleset_factory {
|
|
public:
|
|
explicit test_ruleset_factory(std::shared_ptr<sinsp_filter_factory> factory):
|
|
m_filter_factory(factory) {}
|
|
|
|
virtual ~test_ruleset_factory() = default;
|
|
|
|
inline std::shared_ptr<filter_ruleset> new_ruleset() override {
|
|
return std::make_shared<test_ruleset>(m_filter_factory);
|
|
}
|
|
|
|
std::shared_ptr<sinsp_filter_factory> m_filter_factory;
|
|
};
|
|
}; // namespace
|
|
|
|
void test_compiler::compile(rule_loader::configuration& cfg,
|
|
const rule_loader::collector& col,
|
|
rule_loader::compile_output& out) const {
|
|
rule_loader::compiler::compile(cfg, col, out);
|
|
|
|
const test_collector& test_col = dynamic_cast<const test_collector&>(col);
|
|
|
|
test_compile_output& test_output = dynamic_cast<test_compile_output&>(out);
|
|
|
|
for(auto& test_obj : test_col.test_object_infos) {
|
|
test_output.defined_test_properties.insert(test_obj.property);
|
|
}
|
|
}
|
|
|
|
static std::string content = R"END(
|
|
|
|
- test_object: test
|
|
property: my-value
|
|
|
|
- test_object: test2
|
|
property: other-value
|
|
|
|
- list: shell_binaries
|
|
items: [sh, bash]
|
|
|
|
- macro: spawned_process
|
|
condition: evt.type=execve and proc.name in (shell_binaries)
|
|
|
|
- rule: test info rule
|
|
desc: A test info rule
|
|
condition: spawned_process
|
|
output: A test info rule matched (evt.type=%evt.type proc.name=%proc.name)
|
|
priority: INFO
|
|
source: syscall
|
|
tags: [process]
|
|
|
|
- rule: test k8s_audit rule
|
|
desc: A k8s audit test rule
|
|
condition: ka.target.resource=deployments
|
|
output: A k8s audit rule matched (ka.verb=%ka.verb resource=%ka.target.resource)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [process]
|
|
|
|
- rule: test debug rule
|
|
desc: A test debug rule
|
|
condition: spawned_process and proc.name="bash"
|
|
output: A test debug rule matched (evt.type=%evt.type proc.name=%proc.name)
|
|
priority: DEBUG
|
|
source: syscall
|
|
tags: [process]
|
|
)END";
|
|
|
|
static std::string syscall_source_name = "syscall";
|
|
|
|
static std::shared_ptr<rule_loader::configuration> create_configuration(
|
|
sinsp& inspector,
|
|
sinsp_filter_check_list& filterchecks,
|
|
indexed_vector<falco_source>& sources) {
|
|
auto filter_factory = std::make_shared<sinsp_filter_factory>(&inspector, filterchecks);
|
|
auto formatter_factory =
|
|
std::make_shared<sinsp_evt_formatter_factory>(&inspector, filterchecks);
|
|
auto ruleset_factory = std::make_shared<evttype_index_ruleset_factory>(filter_factory);
|
|
|
|
falco_source syscall_source;
|
|
syscall_source.name = syscall_source_name;
|
|
syscall_source.ruleset = ruleset_factory->new_ruleset();
|
|
syscall_source.ruleset_factory = ruleset_factory;
|
|
syscall_source.filter_factory = filter_factory;
|
|
syscall_source.formatter_factory = formatter_factory;
|
|
|
|
sources.insert(syscall_source, syscall_source_name);
|
|
|
|
return std::make_shared<rule_loader::configuration>(content, sources, "test configuration");
|
|
}
|
|
|
|
static void load_rules(sinsp& inspector,
|
|
sinsp_filter_check_list& filterchecks,
|
|
std::unique_ptr<rule_loader::compile_output>& compile_output,
|
|
indexed_vector<falco_source>& sources) {
|
|
std::shared_ptr<rule_loader::configuration> cfg =
|
|
create_configuration(inspector, filterchecks, sources);
|
|
|
|
rule_loader::reader reader;
|
|
rule_loader::collector collector;
|
|
rule_loader::compiler compiler;
|
|
|
|
EXPECT_TRUE(reader.read(*cfg, collector));
|
|
|
|
compile_output = compiler.new_compile_output();
|
|
|
|
compiler.compile(*cfg, collector, *compile_output);
|
|
}
|
|
|
|
TEST(engine_loader_alt_loader, load_rules) {
|
|
sinsp inspector;
|
|
sinsp_filter_check_list filterchecks;
|
|
std::unique_ptr<rule_loader::compile_output> compile_output;
|
|
indexed_vector<falco_source> sources;
|
|
|
|
load_rules(inspector, filterchecks, compile_output, sources);
|
|
|
|
// Note that the k8s_audit rule will be skipped as load_rules
|
|
// only adds a syscall source.
|
|
EXPECT_EQ(compile_output->lists.size(), 1);
|
|
EXPECT_TRUE(compile_output->lists.at("shell_binaries") != nullptr);
|
|
|
|
EXPECT_EQ(compile_output->macros.size(), 1);
|
|
EXPECT_TRUE(compile_output->macros.at("spawned_process") != nullptr);
|
|
|
|
EXPECT_EQ(compile_output->rules.size(), 2);
|
|
EXPECT_TRUE(compile_output->rules.at("test info rule") != nullptr);
|
|
EXPECT_TRUE(compile_output->rules.at("test debug rule") != nullptr);
|
|
}
|
|
|
|
TEST(engine_loader_alt_loader, pass_compile_output_to_ruleset) {
|
|
sinsp inspector;
|
|
sinsp_filter_check_list filterchecks;
|
|
std::unique_ptr<rule_loader::compile_output> compile_output;
|
|
indexed_vector<falco_source> sources;
|
|
|
|
load_rules(inspector, filterchecks, compile_output, sources);
|
|
|
|
std::shared_ptr<filter_ruleset> ruleset = sources.at(syscall_source_name)->ruleset;
|
|
|
|
ruleset->add_compile_output(*compile_output,
|
|
falco_common::PRIORITY_INFORMATIONAL,
|
|
syscall_source_name);
|
|
|
|
// Enable all rules for a ruleset id. Because the compile
|
|
// output contained one rule with priority >= INFO, that rule
|
|
// should be enabled.
|
|
uint16_t ruleset_id = 0;
|
|
ruleset->enable("", filter_ruleset::match_type::substring, ruleset_id);
|
|
|
|
EXPECT_EQ(ruleset->enabled_count(ruleset_id), 1);
|
|
}
|
|
|
|
TEST(engine_loader_alt_loader, falco_engine_alternate_loader) {
|
|
falco_engine engine;
|
|
sinsp inspector;
|
|
sinsp_filter_check_list filterchecks;
|
|
|
|
auto filter_factory = std::make_shared<sinsp_filter_factory>(&inspector, filterchecks);
|
|
auto formatter_factory =
|
|
std::make_shared<sinsp_evt_formatter_factory>(&inspector, filterchecks);
|
|
auto ruleset_factory = std::make_shared<test_ruleset_factory>(filter_factory);
|
|
|
|
engine.add_source(syscall_source_name, filter_factory, formatter_factory, ruleset_factory);
|
|
|
|
auto reader = std::make_shared<test_reader>();
|
|
auto collector = std::make_shared<test_collector>();
|
|
auto compiler = std::make_shared<test_compiler>();
|
|
|
|
engine.set_rule_reader(reader);
|
|
engine.set_rule_collector(collector);
|
|
engine.set_rule_compiler(compiler);
|
|
|
|
EXPECT_EQ(reader, engine.get_rule_reader());
|
|
EXPECT_EQ(collector, engine.get_rule_collector());
|
|
EXPECT_EQ(compiler, engine.get_rule_compiler());
|
|
|
|
engine.load_rules(content, "test_rules.yaml");
|
|
|
|
EXPECT_EQ(collector->test_object_infos.size(), 2);
|
|
|
|
std::shared_ptr<filter_ruleset> ruleset = engine.ruleset_for_source(syscall_source_name);
|
|
std::set<std::string>& defined_properties =
|
|
std::dynamic_pointer_cast<test_ruleset>(ruleset)->defined_properties;
|
|
|
|
EXPECT_TRUE(defined_properties.find("my-value") != defined_properties.end());
|
|
EXPECT_TRUE(defined_properties.find("other-value") != defined_properties.end());
|
|
EXPECT_TRUE(defined_properties.find("not-exists-value") == defined_properties.end());
|
|
};
|
|
|
|
TEST(engine_loader_alt_loader, clone_compile_output) {
|
|
sinsp inspector;
|
|
sinsp_filter_check_list filterchecks;
|
|
indexed_vector<falco_source> sources;
|
|
|
|
std::shared_ptr<rule_loader::configuration> cfg =
|
|
create_configuration(inspector, filterchecks, sources);
|
|
|
|
test_reader reader;
|
|
test_collector collector;
|
|
test_compiler compiler;
|
|
|
|
EXPECT_TRUE(reader.read(*cfg, collector));
|
|
|
|
std::unique_ptr<rule_loader::compile_output> compile_output = compiler.new_compile_output();
|
|
|
|
compiler.compile(*cfg, collector, *compile_output);
|
|
|
|
const test_compile_output& original_ref =
|
|
dynamic_cast<const test_compile_output&>(*(compile_output.get()));
|
|
|
|
std::unique_ptr<rule_loader::compile_output> copy = compile_output->clone();
|
|
const test_compile_output& copy_ref = dynamic_cast<const test_compile_output&>(*(copy.get()));
|
|
|
|
EXPECT_EQ(copy_ref.lists, original_ref.lists);
|
|
EXPECT_EQ(copy_ref.macros, original_ref.macros);
|
|
EXPECT_EQ(copy_ref.rules, original_ref.rules);
|
|
EXPECT_EQ(copy_ref.defined_test_properties, original_ref.defined_test_properties);
|
|
}
|