Skip to content

Commit

Permalink
Add more tests, improvements, etc
Browse files Browse the repository at this point in the history
  • Loading branch information
Anilm3 committed Nov 25, 2024
1 parent 5815ee8 commit 1b3a53d
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 124 deletions.
32 changes: 16 additions & 16 deletions src/builder/module_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@

namespace ddwaf {

// Helpers
inline bool user_rule_precedence(
const core_rule::source_type left, const core_rule::source_type right)
{
return left > right;
}

inline bool base_rule_precedence(
const core_rule::source_type left, const core_rule::source_type right)
{
return left < right;
}

inline std::string_view type_grouping_key(const core_rule *rule) { return rule->get_type(); }
inline constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; }

// The module builder can be used to build a single module
class rule_module_builder {
public:
Expand Down Expand Up @@ -55,22 +71,6 @@ class rule_module_set_builder {
const std::vector<std::shared_ptr<core_rule>> &rules);

protected:
// Helpers
static bool user_rule_precedence(
const core_rule::source_type left, const core_rule::source_type right)
{
return left > right;
}

static bool base_rule_precedence(
const core_rule::source_type left, const core_rule::source_type right)
{
return left < right;
}

static std::string_view type_grouping_key(const core_rule *rule) { return rule->get_type(); }
static constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; }

std::array<rule_module_builder, rule_module_count> builders_{{
// Network-ACL
{base_rule_precedence, null_grouping_key, rule_module::expiration_policy::non_expiring},
Expand Down
10 changes: 1 addition & 9 deletions src/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,21 +225,13 @@ exclusion::context_policy &context::eval_filters(ddwaf::timer &deadline)
std::vector<event> context::eval_rules(
const exclusion::context_policy &policy, ddwaf::timer &deadline)
{
static auto no_deadline = endless_timer();

std::vector<ddwaf::event> events;

for (std::size_t i = 0; i < ruleset_->rule_modules.size(); ++i) {
const auto &mod = ruleset_->rule_modules[i];
auto &cache = rule_module_cache_[i];

rule_module::verdict_type verdict = rule_module::verdict_type::none;
if (mod.may_expire()) {
verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline);
} else {
verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, no_deadline);
}

auto verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline);
if (verdict == rule_module::verdict_type::block) {
break;
}
Expand Down
19 changes: 15 additions & 4 deletions src/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ std::pair<std::optional<event>, verdict_type> eval_rule(const core_rule &rule,

} // namespace

ddwaf::timer &rule_module::get_deadline(ddwaf::timer &deadline) const
{
static auto no_deadline = endless_timer();
return may_expire() ? deadline : no_deadline;
}

verdict_type rule_module::eval_with_collections(std::vector<event> &events, object_store &store,
cache_type &cache, const exclusion::context_policy &exclusion,
const std::unordered_map<std::string, std::shared_ptr<matcher::base>> &dynamic_matchers,
Expand Down Expand Up @@ -132,22 +138,27 @@ verdict_type rule_module::eval(std::vector<event> &events, object_store &store,
const std::unordered_map<std::string, std::shared_ptr<matcher::base>> &dynamic_matchers,
ddwaf::timer &deadline) const
{
auto &apt_deadline = get_deadline(deadline);

if (collections_.empty()) {
auto final_verdict = verdict_type::none;
for (std::size_t i = 0; i < rules_.size(); ++i) {
const auto &rule = *rules_[i];
auto &rule_cache = cache.rules[i];

auto [event, verdict] =
eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, deadline);
eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, apt_deadline);
if (event.has_value()) {
events.emplace_back(std::move(*event));
DDWAF_DEBUG("Found event on rule {}", rule.get_id());
return verdict;
if (verdict > final_verdict) {
final_verdict = verdict;
}
}
}
return verdict_type::none;
return final_verdict;
}

return eval_with_collections(events, store, cache, exclusion, dynamic_matchers, deadline);
return eval_with_collections(events, store, cache, exclusion, dynamic_matchers, apt_deadline);
}
} // namespace ddwaf
2 changes: 2 additions & 0 deletions src/module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class rule_module {
const std::unordered_map<std::string, std::shared_ptr<matcher::base>> &dynamic_matchers,
ddwaf::timer &deadline) const;

ddwaf::timer &get_deadline(ddwaf::timer &deadline) const;

struct rule_collection {
std::string_view name;
verdict_type type;
Expand Down
95 changes: 0 additions & 95 deletions tests/unit/context_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -679,101 +679,6 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst)
}
}

// TODO: collections don't work like that any longer
/*TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet)*/
/*{*/
/*auto ruleset = test::get_default_ruleset();*/
/*std::vector<std::shared_ptr<core_rule>> rules;*/
/*{*/
/*test::expression_builder builder(1);*/
/*builder.start_condition();*/
/*builder.add_argument();*/
/*builder.add_target("http.client_ip");*/
/*builder.end_condition<matcher::ip_match>(std::vector<std::string_view>{"192.168.0.1"});*/

/*std::unordered_map<std::string, std::string> tags{*/
/*{"type", "type"}, {"category", "category1"}};*/

/*rules.emplace_back(*/
/*std::make_shared<core_rule>("id1", "name1", std::move(tags), builder.build()));*/
/*}*/

/*{*/
/*test::expression_builder builder(1);*/
/*builder.start_condition();*/
/*builder.add_argument();*/
/*builder.add_target("usr.id");*/
/*builder.end_condition<matcher::exact_match>(std::vector<std::string>{"admin"});*/

/*std::unordered_map<std::string, std::string> tags{*/
/*{"type", "type"}, {"category", "category2"}};*/

/*rules.emplace_back(std::make_shared<core_rule>("id2", "name2", std::move(tags),*/
/*builder.build(), std::vector<std::string>{"redirect"}));*/
/*}*/
/*ruleset->insert_rules(rules, {});*/

/*ddwaf::timer deadline{2s};*/
/*ddwaf::test::context ctx(ruleset);*/

/*{*/
/*ddwaf_object root;*/
/*ddwaf_object tmp;*/
/*ddwaf_object_map(&root);*/
/*ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1"));*/
/*ctx.insert(root);*/

/*auto events = ctx.eval_rules({}, deadline);*/
/*EXPECT_EQ(events.size(), 1);*/

/*auto &event = events[0];*/
/*EXPECT_STREQ(event.rule->get_id().data(), "id1");*/
/*EXPECT_STREQ(event.rule->get_name().data(), "name1");*/
/*EXPECT_STREQ(event.rule->get_tag("type").data(), "type");*/
/*EXPECT_STREQ(event.rule->get_tag("category").data(), "category1");*/
/*EXPECT_TRUE(event.rule->get_actions().empty());*/

/*auto &match = event.matches[0];*/
/*EXPECT_STREQ(match.args[0].resolved.c_str(), "192.168.0.1");*/
/*EXPECT_STREQ(match.highlights[0].c_str(), "192.168.0.1");*/
/*EXPECT_STREQ(match.operator_name.data(), "ip_match");*/
/*EXPECT_STREQ(match.operator_value.data(), "");*/
/*EXPECT_STREQ(match.args[0].address.data(), "http.client_ip");*/
/*EXPECT_TRUE(match.args[0].key_path.empty());*/
/*}*/

/*{*/
/*// An existing match in a collection will not inhibit a match in a*/
/*// priority collection.*/
/*ddwaf_object root;*/
/*ddwaf_object tmp;*/
/*ddwaf_object_map(&root);*/
/*ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin"));*/
/*ctx.insert(root);*/

/*auto events = ctx.eval_rules({}, deadline);*/
/*EXPECT_EQ(events.size(), 1);*/

/*auto &event = events[0];*/
/*EXPECT_EQ(events.size(), 1);*/
/*EXPECT_STREQ(event.rule->get_id().data(), "id2");*/
/*EXPECT_STREQ(event.rule->get_name().data(), "name2");*/
/*EXPECT_STREQ(event.rule->get_tag("type").data(), "type");*/
/*EXPECT_STREQ(event.rule->get_tag("category").data(), "category2");*/
/*std::vector<std::string> expected_actions{"redirect"};*/
/*EXPECT_EQ(event.rule->get_actions(), expected_actions);*/
/*EXPECT_EQ(event.matches.size(), 1);*/

/*auto &match = event.matches[0];*/
/*EXPECT_STREQ(match.args[0].resolved.c_str(), "admin");*/
/*EXPECT_STREQ(match.highlights[0].c_str(), "admin");*/
/*EXPECT_STREQ(match.operator_name.data(), "exact_match");*/
/*EXPECT_STREQ(match.operator_value.data(), "");*/
/*EXPECT_STREQ(match.args[0].address.data(), "usr.id");*/
/*EXPECT_TRUE(match.args[0].key_path.empty());*/
/*}*/
/*}*/

TEST(TestContext, MatchMultipleCollectionsSingleRun)
{
auto ruleset = test::get_default_ruleset();
Expand Down
144 changes: 144 additions & 0 deletions tests/unit/module_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Unless explicitly stated otherwise all files in this repository are
// dual-licensed under the Apache-2.0 License or BSD-3-Clause License.
//
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2021 Datadog, Inc.

#include "clock.hpp"
#include "common/gtest_utils.hpp"
#include "condition/scalar_condition.hpp"
#include "matcher/exact_match.hpp"
#include "matcher/ip_match.hpp"
#include "module.hpp"

using namespace ddwaf;
using namespace std::literals;

namespace {

TEST(TestModule, SingleRuleMatch)
{
test::expression_builder builder(1);
builder.start_condition();
builder.add_argument();
builder.add_target("http.client_ip");
builder.end_condition<matcher::ip_match>(std::vector<std::string_view>{"192.168.0.1"});

std::unordered_map<std::string, std::string> tags{{"type", "type"}, {"category", "category"}};

auto rule = std::make_shared<core_rule>("id", "name", std::move(tags), builder.build());

rule_module_builder mod_builder{base_rule_precedence, null_grouping_key};
mod_builder.insert(rule.get());

auto mod = mod_builder.build();

rule_module_cache cache;
mod.init_cache(cache);

ddwaf::object_store store;
{
ddwaf_object root;
ddwaf_object tmp;
ddwaf_object_map(&root);
ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1"));

store.insert(root);

std::vector<event> events;
ddwaf::timer deadline = endless_timer();
mod.eval(events, store, cache, {}, {}, deadline);

EXPECT_EQ(events.size(), 1);
}

{
ddwaf_object root;
ddwaf_object tmp;
ddwaf_object_map(&root);
ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1"));

store.insert(root);
std::vector<event> events;
ddwaf::timer deadline = endless_timer();
mod.eval(events, store, cache, {}, {}, deadline);

EXPECT_EQ(events.size(), 0);
}
}

TEST(TestModule, NonExpiringModule)
{
test::expression_builder builder(1);
builder.start_condition();
builder.add_argument();
builder.add_target("http.client_ip");
builder.end_condition<matcher::ip_match>(std::vector<std::string_view>{"192.168.0.1"});

std::unordered_map<std::string, std::string> tags{{"type", "type"}, {"category", "category"}};

auto rule = std::make_shared<core_rule>("id", "name", std::move(tags), builder.build());

rule_module_builder mod_builder{
base_rule_precedence, null_grouping_key, rule_module::expiration_policy::non_expiring};
mod_builder.insert(rule.get());

auto mod = mod_builder.build();

rule_module_cache cache;
mod.init_cache(cache);

ddwaf::object_store store;
{
ddwaf_object root;
ddwaf_object tmp;
ddwaf_object_map(&root);
ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1"));

store.insert(root);

std::vector<event> events;
ddwaf::timer deadline{0s};
mod.eval(events, store, cache, {}, {}, deadline);

EXPECT_EQ(events.size(), 1);
}
}

TEST(TestModule, ExpiringModule)
{
test::expression_builder builder(1);
builder.start_condition();
builder.add_argument();
builder.add_target("http.client_ip");
builder.end_condition<matcher::ip_match>(std::vector<std::string_view>{"192.168.0.1"});

std::unordered_map<std::string, std::string> tags{{"type", "type"}, {"category", "category"}};

auto rule = std::make_shared<core_rule>("id", "name", std::move(tags), builder.build());

rule_module_builder mod_builder{
base_rule_precedence, null_grouping_key, rule_module::expiration_policy::expiring};
mod_builder.insert(rule.get());

auto mod = mod_builder.build();

rule_module_cache cache;
mod.init_cache(cache);

ddwaf::object_store store;
{
ddwaf_object root;
ddwaf_object tmp;
ddwaf_object_map(&root);
ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1"));

store.insert(root);

std::vector<event> events;
ddwaf::timer deadline{0s};
EXPECT_THROW(mod.eval(events, store, cache, {}, {}, deadline), ddwaf::timeout_exception);
}
}

} // namespace

0 comments on commit 1b3a53d

Please sign in to comment.