Skip to content

Commit

Permalink
new(engine): add selective overrides
Browse files Browse the repository at this point in the history
Signed-off-by: Luca Guerra <luca@guerra.sh>
  • Loading branch information
LucaGuerra committed Dec 19, 2023
1 parent f2d0c42 commit 19debcc
Show file tree
Hide file tree
Showing 6 changed files with 662 additions and 47 deletions.
233 changes: 233 additions & 0 deletions unit_tests/engine/test_rule_loader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
#include <gtest/gtest.h>

#include "falco_engine.h"
#include "rule_loader_reader.h"
#include "rule_loader_compiler.h"

class engine_loader_test : public ::testing::Test {
protected:
void SetUp() override
{
m_sample_ruleset = "sample-ruleset";
m_sample_source = falco_common::syscall_source;

// create a falco engine ready to load the ruleset
m_engine.reset(new falco_engine());
m_filter_factory = std::shared_ptr<gen_event_filter_factory>(
new sinsp_filter_factory(nullptr, m_filterlist));
m_formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
new sinsp_evt_formatter_factory(nullptr, m_filterlist));
m_engine->add_source(m_sample_source, m_filter_factory, m_formatter_factory);
}

void TearDown() override
{

}

bool load_rules(std::string rules_content, std::string rules_filename)
{
bool ret = false;
falco::load_result::rules_contents_t rc = {{rules_filename, rules_content}};
m_load_result = m_engine->load_rules(rules_content, rules_filename);
m_load_result_string = m_load_result->as_string(true, rc);
ret = m_load_result->successful();

if (ret)
{
m_engine->enable_rule("", true, m_sample_ruleset);
}

return ret;
}

std::string m_sample_ruleset;
std::string m_sample_source;
sinsp_filter_check_list m_filterlist;
std::shared_ptr<gen_event_filter_factory> m_filter_factory;
std::shared_ptr<gen_event_formatter_factory> m_formatter_factory;
std::unique_ptr<falco_engine> m_engine;
std::unique_ptr<falco::load_result> m_load_result;
std::string m_load_result_string;
};

std::string s_sample_ruleset = "sample-ruleset";
std::string s_sample_source = falco_common::syscall_source;

TEST_F(engine_loader_test, list_append)
{
std::string rules_content = R"END(
- list: shell_binaries
items: [ash, bash, csh, ksh, sh, tcsh, zsh, dash]
- rule: legit_rule
desc: legit rule description
condition: evt.type=open and proc.name in (shell_binaries)
output: user=%user.name command=%proc.cmdline file=%fd.name
priority: INFO
- list: shell_binaries
items: [pwsh]
override:
items: append
)END";

std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;

auto rule_description = m_engine->describe_rule(&rule_name, {});
ASSERT_EQ(rule_description["rules"][0]["details"]["condition_compiled"].template get<std::string>(),
"(evt.type = open and proc.name in (ash, bash, csh, ksh, sh, tcsh, zsh, dash, pwsh))");
}

TEST_F(engine_loader_test, condition_append)
{
std::string rules_content = R"END(
- macro: interactive
condition: >
((proc.aname=sshd and proc.name != sshd) or
proc.name=systemd-logind or proc.name=login)
- rule: legit_rule
desc: legit rule description
condition: evt.type=open and interactive
output: user=%user.name command=%proc.cmdline file=%fd.name
priority: INFO
- macro: interactive
condition: or proc.name = ssh
override:
condition: append
)END";

std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;

auto rule_description = m_engine->describe_rule(&rule_name, {});
ASSERT_EQ(rule_description["rules"][0]["details"]["condition_compiled"].template get<std::string>(),
"(evt.type = open and (((proc.aname = sshd and proc.name != sshd) or proc.name = systemd-logind or proc.name = login) or proc.name = ssh))");
}

TEST_F(engine_loader_test, rule_override_append)
{
std::string rules_content = R"END(
- rule: legit_rule
desc: legit rule description
condition: evt.type=open
output: user=%user.name command=%proc.cmdline file=%fd.name
priority: INFO
- rule: legit_rule
desc: with append
condition: and proc.name = cat
output: proc=%proc.name
override:
desc: append
condition: append
output: append
)END";

std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;

auto rule_description = m_engine->describe_rule(&rule_name, {});
ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get<std::string>(),
"evt.type=open and proc.name = cat");

ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get<std::string>(),
"user=%user.name command=%proc.cmdline file=%fd.name proc=%proc.name");

ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get<std::string>(),
"legit rule description with append");
}


TEST_F(engine_loader_test, rule_append)
{
std::string rules_content = R"END(
- rule: legit_rule
desc: legit rule description
condition: evt.type=open
output: user=%user.name command=%proc.cmdline file=%fd.name
priority: INFO
- rule: legit_rule
condition: and proc.name = cat
append: true
)END";

std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;

auto rule_description = m_engine->describe_rule(&rule_name, {});
ASSERT_EQ(rule_description["rules"][0]["details"]["condition_compiled"].template get<std::string>(),
"(evt.type = open and proc.name = cat)");
}


TEST_F(engine_loader_test, rule_override_replace)
{
std::string rules_content = R"END(
- rule: legit_rule
desc: legit rule description
condition: evt.type=open
output: user=%user.name command=%proc.cmdline file=%fd.name
priority: INFO
- rule: legit_rule
desc: a replaced legit description
condition: evt.type = close
override:
desc: replace
condition: replace
)END";

std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;

auto rule_description = m_engine->describe_rule(&rule_name, {});
ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get<std::string>(),
"evt.type = close");

ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get<std::string>(),
"user=%user.name command=%proc.cmdline file=%fd.name");

ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get<std::string>(),
"a replaced legit description");
}

TEST_F(engine_loader_test, rule_override_append_replace)
{
std::string rules_content = R"END(
- rule: legit_rule
desc: legit rule description
condition: evt.type = close
output: user=%user.name command=%proc.cmdline file=%fd.name
priority: INFO
- rule: legit_rule
desc: a replaced legit description
condition: and proc.name = cat
priority: WARNING
override:
desc: replace
condition: append
priority: replace
)END";

std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;

auto rule_description = m_engine->describe_rule(&rule_name, {});
ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get<std::string>(),
"evt.type = close and proc.name = cat");

ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get<std::string>(),
"user=%user.name command=%proc.cmdline file=%fd.name");

ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get<std::string>(),
"a replaced legit description");

ASSERT_EQ(rule_description["rules"][0]["info"]["priority"].template get<std::string>(),
"Warning");
}
8 changes: 7 additions & 1 deletion userspace/engine/rule_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ static const std::string item_type_strings[] = {
"condition expression",
"rule output",
"rule output expression",
"rule priority"
"rule priority",
"overrides"
};

const std::string& rule_loader::context::item_type_as_string(enum item_type it)
Expand Down Expand Up @@ -553,6 +554,11 @@ rule_loader::rule_info::rule_info(context &ctx)
{
}

rule_loader::rule_update_info::rule_update_info(context &ctx)
: ctx(ctx), cond_ctx(ctx)
{
}

rule_loader::rule_load_exception::rule_load_exception(falco::load_result::error_code ec, const std::string& msg, const context& ctx)
: ec(ec), msg(msg), ctx(ctx)
{
Expand Down
38 changes: 37 additions & 1 deletion userspace/engine/rule_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ limitations under the License.

#include <string>
#include <vector>
#include <optional>
#include <yaml-cpp/yaml.h>
#include <nlohmann/json.hpp>
#include "falco_source.h"
Expand Down Expand Up @@ -56,7 +57,8 @@ namespace rule_loader
CONDITION_EXPRESSION,
RULE_OUTPUT,
RULE_OUTPUT_EXPRESSION,
RULE_PRIORITY
RULE_PRIORITY,
OVERRIDE
};

static const std::string& item_type_as_string(enum item_type it);
Expand Down Expand Up @@ -451,4 +453,38 @@ namespace rule_loader
bool warn_evttypes;
bool skip_if_unknown_filter;
};

/*!
\brief Represents infos about a rule update (append or replace) request
*/

struct rule_update_info
{
rule_update_info(context &ctx);
~rule_update_info() = default;
rule_update_info(rule_update_info&&) = default;
rule_update_info& operator = (rule_update_info&&) = default;
rule_update_info(const rule_update_info&) = default;
rule_update_info& operator = (const rule_update_info&) = default;

bool has_any_value()
{
return cond.has_value() || output.has_value() || desc.has_value() || tags.has_value() ||
exceptions.has_value() || priority.has_value() || enabled.has_value() ||
warn_evttypes.has_value() || skip_if_unknown_filter.has_value();
}

context ctx;
context cond_ctx;
std::string name;
std::optional<std::string> cond;
std::optional<std::string> output;
std::optional<std::string> desc;
std::optional<std::set<std::string>> tags;
std::optional<std::vector<rule_exception_info>> exceptions;
std::optional<falco_common::priority_type> priority;
std::optional<bool> enabled;
std::optional<bool> warn_evttypes;
std::optional<bool> skip_if_unknown_filter;
};
};
Loading

0 comments on commit 19debcc

Please sign in to comment.