From bd6b20b7d12d3d03132ca26f3a98b33c00daeacd Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sat, 26 Oct 2024 23:31:34 +0100 Subject: [PATCH 1/8] Organise collections into a single module --- cmake/objects.cmake | 1 + src/context.cpp | 38 +- src/context.hpp | 6 +- src/module.cpp | 121 +++++++ src/module.hpp | 123 +++++++ src/parser/parser_v1.cpp | 12 +- src/rule.hpp | 7 + src/ruleset.hpp | 47 +-- src/ruleset_builder.cpp | 3 +- src/utils.cpp | 3 +- tests/unit/collection_test.cpp | 630 --------------------------------- 11 files changed, 282 insertions(+), 709 deletions(-) create mode 100644 src/module.cpp create mode 100644 src/module.hpp delete mode 100644 tests/unit/collection_test.cpp diff --git a/cmake/objects.cmake b/cmake/objects.cmake index efc8011de..53948f975 100644 --- a/cmake/objects.cmake +++ b/cmake/objects.cmake @@ -9,6 +9,7 @@ set(LIBDDWAF_SOURCE ${libddwaf_SOURCE_DIR}/src/object.cpp ${libddwaf_SOURCE_DIR}/src/object_store.cpp ${libddwaf_SOURCE_DIR}/src/collection.cpp + ${libddwaf_SOURCE_DIR}/src/module.cpp ${libddwaf_SOURCE_DIR}/src/expression.cpp ${libddwaf_SOURCE_DIR}/src/ruleset_info.cpp ${libddwaf_SOURCE_DIR}/src/ip_utils.cpp diff --git a/src/context.cpp b/src/context.cpp index f66c57bde..5b45970f4 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -9,7 +9,6 @@ #include #include "clock.hpp" -#include "collection.hpp" #include "context.hpp" #include "ddwaf.h" #include "event.hpp" @@ -86,7 +85,7 @@ DDWAF_RET_CODE context::run(optional_ref persistent, ddwaf::timer deadline{std::chrono::microseconds(timeout)}; // If this is a new run but no rule care about those new params, let's skip the run - if (!is_first_run() && !store_.has_new_targets()) { + if (!store_.has_new_targets()) { return DDWAF_OK; } @@ -235,38 +234,9 @@ std::vector context::eval_rules( { std::vector events; - auto eval_collection = [&](const auto &type, const auto &collection) { - auto it = collection_cache_.find(type); - if (it == collection_cache_.end()) { - auto [new_it, res] = collection_cache_.emplace(type, collection_cache{}); - it = new_it; - } - collection.match(events, store_, it->second, policy, ruleset_->rule_matchers, deadline); - }; - - // Evaluate user priority collections first - for (auto &[type, collection] : ruleset_->user_priority_collections) { - DDWAF_DEBUG("Evaluating user priority collection '{}'", type); - eval_collection(type, collection); - } - - // Evaluate priority collections first - for (auto &[type, collection] : ruleset_->base_priority_collections) { - DDWAF_DEBUG("Evaluating priority collection '{}'", type); - eval_collection(type, collection); - } - - // Evaluate regular collection after - for (auto &[type, collection] : ruleset_->user_collections) { - DDWAF_DEBUG("Evaluating user collection '{}'", type); - eval_collection(type, collection); - } - - // Evaluate regular collection after - for (auto &[type, collection] : ruleset_->base_collections) { - DDWAF_DEBUG("Evaluating base collection '{}'", type); - eval_collection(type, collection); - } + DDWAF_DEBUG("Evaluating rules"); + ruleset_->rules.eval( + events, store_, collection_cache_, policy, ruleset_->rule_matchers, deadline); return events; } diff --git a/src/context.hpp b/src/context.hpp index df6dcc2bf..ca1a2715c 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -33,7 +33,6 @@ class context { { rule_filter_cache_.reserve(ruleset_->rule_filters.size()); input_filter_cache_.reserve(ruleset_->input_filters.size()); - collection_cache_.reserve(ruleset_->collection_types.size()); } context(const context &) = delete; @@ -53,7 +52,6 @@ class context { std::vector eval_rules(const exclusion::context_policy &policy, ddwaf::timer &deadline); protected: - bool is_first_run() const { return collection_cache_.empty(); } bool check_new_rule_targets() const { // NOLINTNEXTLINE(readability-use-anyofallof) @@ -83,12 +81,12 @@ class context { memory::unordered_map processor_cache_; // Caches of filters and conditions - memory::unordered_map rule_filter_cache_{}; + memory::unordered_map rule_filter_cache_; memory::unordered_map input_filter_cache_; exclusion::context_policy exclusion_policy_; // Cache of collections to avoid processing once a result has been obtained - memory::unordered_map collection_cache_{}; + collection_module::cache_type collection_cache_{}; }; class context_wrapper { diff --git a/src/module.cpp b/src/module.cpp new file mode 100644 index 000000000..f7d6f8510 --- /dev/null +++ b/src/module.cpp @@ -0,0 +1,121 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include "clock.hpp" +#include "context_allocator.hpp" +#include "event.hpp" +#include "exception.hpp" +#include "exclusion/common.hpp" +#include "log.hpp" +#include "matcher/base.hpp" +#include "module.hpp" +#include "object_store.hpp" +#include "rule.hpp" + +namespace ddwaf { + +namespace { +std::optional match_rule(ddwaf::rule &rule, const object_store &store, + rule::cache_type &cache, const exclusion::context_policy &policy, + const std::unordered_map> &dynamic_matchers, + ddwaf::timer &deadline) +{ + const auto &id = rule.get_id(); + + if (deadline.expired()) { + DDWAF_INFO("Ran out of time while evaluating rule '{}'", id); + throw timeout_exception(); + } + + if (!rule.is_enabled()) { + DDWAF_DEBUG("Rule '{}' is disabled", id); + return std::nullopt; + } + + std::string_view action_override; + auto exclusion = policy.find(&rule); + if (exclusion.mode == exclusion::filter_mode::bypass) { + DDWAF_DEBUG("Bypassing rule '{}'", id); + return std::nullopt; + } + + if (exclusion.mode == exclusion::filter_mode::monitor) { + DDWAF_DEBUG("Monitoring rule '{}'", id); + action_override = "monitor"; + } else if (exclusion.mode == exclusion::filter_mode::custom) { + action_override = exclusion.action_override; + DDWAF_DEBUG("Evaluating rule '{}' with custom action '{}'", id, action_override); + } else { + DDWAF_DEBUG("Evaluating rule '{}'", id); + } + + try { + std::optional event; + event = rule.match(store, cache, exclusion.objects, dynamic_matchers, deadline); + + if (event.has_value()) { + event->action_override = action_override; + } + + return event; + } catch (const ddwaf::timeout_exception &) { + DDWAF_INFO("Ran out of time while evaluating rule '{}'", id); + throw; + } + + return std::nullopt; +} + +} // namespace + +void collection_module::eval(std::vector &events, object_store &store, module_cache &cache, + const exclusion::context_policy &exclusion, + const std::unordered_map> &dynamic_matchers, + ddwaf::timer &deadline) const +{ + if (cache.rules.size() != rules_.size()) { + cache.rules.resize(rules_.size()); + cache.collections.reserve(collections_.size()); + } + + for (const auto &collection : collections_) { + auto &collection_cache = cache.collections[collection.name]; + if (collection_cache.type >= collection.type) { + // If the result was cached but ephemeral, clear it. Note that this is + // just a workaround taking advantage of the order of evaluation of + // collections. Collections might be removed in the future altogether. + if (collection_cache.type == collection.type && collection_cache.ephemeral) { + collection_cache.type = collection_type::none; + collection_cache.ephemeral = false; + } else { + continue; + } + } + + for (std::size_t i = collection.start; i < collection.end; ++i) { + auto &rule = *rules_[i]; + auto event = + match_rule(rule, store, cache.rules[i], exclusion, dynamic_matchers, deadline); + if (event.has_value()) { + collection_cache.type = collection.type; + collection_cache.ephemeral = event->ephemeral; + + events.emplace_back(std::move(*event)); + DDWAF_DEBUG("Found event on rule {}", rule.get_id()); + break; + } + } + } +} + +} // namespace ddwaf diff --git a/src/module.hpp b/src/module.hpp new file mode 100644 index 000000000..15cb85ece --- /dev/null +++ b/src/module.hpp @@ -0,0 +1,123 @@ +// 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. + +#pragma once + +#include "context_allocator.hpp" +#include "event.hpp" +#include "exclusion/rule_filter.hpp" +#include "rule.hpp" + +namespace ddwaf { + +enum class collection_type : uint8_t { none = 0, regular = 1, priority = 2 }; + +struct collection_cache { + collection_type type{collection_type::none}; + bool ephemeral{false}; +}; + +struct module_cache { + memory::unordered_map collections; + memory::vector rules; +}; + +class collection_module { +public: + using cache_type = module_cache; + using iterator = std::vector>::iterator; + using const_iterator = std::vector>::const_iterator; + + collection_module() = default; + ~collection_module() = default; + collection_module(const collection_module &) = default; + collection_module(collection_module &&) noexcept = default; + collection_module &operator=(const collection_module &) = default; + collection_module &operator=(collection_module &&) noexcept = default; + + void eval(std::vector &events, object_store &store, module_cache &cache, + const exclusion::context_policy &exclusion, + const std::unordered_map> &dynamic_matchers, + ddwaf::timer &deadline) const; + + [[nodiscard]] bool empty() const noexcept { return rules_.empty(); } + [[nodiscard]] std::size_t size() const noexcept { return rules_.size(); } + + iterator begin() { return rules_.begin(); } + [[nodiscard]] const_iterator begin() const { return rules_.begin(); } + + iterator end() { return rules_.end(); } + [[nodiscard]] const_iterator end() const { return rules_.end(); } + +protected: + struct rule_collection { + std::string_view name; + collection_type type; + std::size_t start; + std::size_t end; + }; + + collection_module( + std::vector &&collections, std::vector> &&rules) + : collections_(std::move(collections)), rules_(std::move(rules)) + {} + + std::vector collections_; + std::vector> rules_; + + friend class collection_module_builder; +}; + +class collection_module_builder { +public: + void insert(const std::shared_ptr &rule) { rules_.emplace_back(rule); } + + collection_module build() + { + std::sort(rules_.begin(), rules_.end(), [](const auto &left, const auto &right) { + auto ltype = left->get_collection(); + auto rtype = right->get_collection(); + + auto res = ltype.compare(rtype); + + return res < 0 || + (res == 0 && ((!left->get_actions().empty() && right->get_actions().empty()) || + (left->get_actions().empty() == right->get_actions().empty() && + left->get_source() > right->get_source()))); + }); + + // Compute the ranges of each collection + std::vector collections; + if (!rules_.empty()) { + auto current_type = rules_[0]->get_collection(); + collections.emplace_back(current_type, + rules_[0]->get_actions().empty() ? collection_type::regular + : collection_type::priority, + 0, rules_.size()); + + for (std::size_t i = 1; i < rules_.size(); ++i) { + auto this_type = rules_[i]->get_collection(); + if (this_type != current_type) { + collections.back().end = i; + + current_type = this_type; + collections.emplace_back(current_type, + rules_[0]->get_actions().empty() ? collection_type::regular + : collection_type::priority, + i, rules_.size()); + } + } + } + + return {std::move(collections), std::move(rules_)}; + } + +protected: + // Keep track of the first index of the collection + std::unordered_map collection_start_; + std::vector> rules_; +}; +} // namespace ddwaf diff --git a/src/parser/parser_v1.cpp b/src/parser/parser_v1.cpp index edb141d32..bf9992491 100644 --- a/src/parser/parser_v1.cpp +++ b/src/parser/parser_v1.cpp @@ -123,7 +123,8 @@ std::shared_ptr parse_expression(parameter::vector &conditions_array } void parseRule(parameter::map &rule, base_section_info &info, - std::unordered_set &rule_ids, ddwaf::ruleset &rs, ddwaf::object_limits limits) + std::unordered_set &rule_ids, + std::vector> &rules, ddwaf::object_limits limits) { auto id = at(rule, "id"); if (rule_ids.find(id) != rule_ids.end()) { @@ -168,7 +169,7 @@ void parseRule(parameter::map &rule, base_section_info &info, std::string(id), at(rule, "name"), std::move(tags), std::move(expression)); rule_ids.emplace(rule_ptr->get_id()); - rs.insert_rule(rule_ptr); + rules.emplace_back(rule_ptr); info.add_loaded(rule_ptr->get_id()); } catch (const std::exception &e) { DDWAF_WARN("failed to parse rule '{}': {}", id, e.what()); @@ -182,7 +183,8 @@ void parse( parameter::map &ruleset, base_ruleset_info &info, ddwaf::ruleset &rs, object_limits limits) { auto rules_array = at(ruleset, "events"); - rs.rules.reserve(rules_array.size()); + std::vector> rules; + rules.reserve(rules_array.size()); auto §ion = info.add_section("rules"); @@ -191,13 +193,13 @@ void parse( const auto &rule_param = rules_array[i]; try { auto rule = static_cast(rule_param); - parseRule(rule, section, rule_ids, rs, limits); + parseRule(rule, section, rule_ids, rules, limits); } catch (const std::exception &e) { DDWAF_WARN("{}", e.what()); section.add_failed("index:" + to_string(i), e.what()); } } - + rs.insert_rules(rules, {}); if (rs.rules.empty()) { throw ddwaf::parsing_error("no valid rules found"); } diff --git a/src/rule.hpp b/src/rule.hpp index a449e56f1..947f8d5ed 100644 --- a/src/rule.hpp +++ b/src/rule.hpp @@ -37,6 +37,11 @@ class rule { if (!expr_) { throw std::invalid_argument("rule constructed with null expression"); } + + auto type = get_tag("type"); + auto mod = get_tag_or("module", "waf"); + + collection_ = ddwaf::fmt::format("{}.{}", mod, type); } rule(const rule &) = delete; @@ -71,6 +76,7 @@ class rule { source_type get_source() const { return source_; } const std::string &get_id() const { return id_; } const std::string &get_name() const { return name_; } + std::string_view get_collection() const { return collection_; } std::string_view get_tag(const std::string &tag) const { @@ -116,6 +122,7 @@ class rule { std::unordered_map ancillary_tags_; std::shared_ptr expr_; std::vector actions_; + std::string collection_; }; } // namespace ddwaf diff --git a/src/ruleset.hpp b/src/ruleset.hpp index 19e9edd0e..4bdb37bae 100644 --- a/src/ruleset.hpp +++ b/src/ruleset.hpp @@ -11,9 +11,9 @@ #include #include "action_mapper.hpp" -#include "collection.hpp" #include "exclusion/input_filter.hpp" #include "exclusion/rule_filter.hpp" +#include "module.hpp" #include "obfuscator.hpp" #include "processor/base.hpp" #include "rule.hpp" @@ -22,33 +22,20 @@ namespace ddwaf { struct ruleset { - void insert_rule(const std::shared_ptr &rule) + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + void insert_rules(const std::vector> &base_rules, + const std::vector> &user_rules) { - rules.emplace_back(rule); - std::string_view type = rule->get_tag("type"); - std::string_view mod = rule->get_tag_or("module", "waf"); - - auto [it, res] = collection_types.emplace(ddwaf::fmt::format("{}.{}", mod, type)); - const auto &collection = *it; - if (rule->get_actions().empty()) { - if (rule->get_source() == rule::source_type::user) { - user_collections[collection].insert(rule); - } else { - base_collections[collection].insert(rule); - } - } else { - if (rule->get_source() == rule::source_type::user) { - user_priority_collections[collection].insert(rule); - } else { - base_priority_collections[collection].insert(rule); - } + collection_module_builder builder; + for (const auto &rule : base_rules) { + builder.insert(rule); + rule->get_addresses(rule_addresses); } - rule->get_addresses(rule_addresses); - } - - void insert_rules(const std::vector> &rules_) - { - for (const auto &rule : rules_) { insert_rule(rule); } + for (const auto &rule : user_rules) { + builder.insert(rule); + rule->get_addresses(rule_addresses); + } + rules = builder.build(); } template @@ -164,19 +151,13 @@ struct ruleset { std::unordered_map> rule_filters; std::unordered_map> input_filters; - std::vector> rules; std::unordered_map> rule_matchers; std::unordered_map> exclusion_matchers; std::vector> scanners; std::shared_ptr actions; - // The key used to organise collections is "${rule.module}.${rule.type}" - std::unordered_set collection_types; - std::unordered_map user_priority_collections; - std::unordered_map base_priority_collections; - std::unordered_map user_collections; - std::unordered_map base_collections; + collection_module rules; std::unordered_map rule_addresses; std::unordered_map filter_addresses; diff --git a/src/ruleset_builder.cpp b/src/ruleset_builder.cpp index 4ebe3e1a8..84f308ff1 100644 --- a/src/ruleset_builder.cpp +++ b/src/ruleset_builder.cpp @@ -203,8 +203,7 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules } auto rs = std::make_shared(); - rs->insert_rules(final_base_rules_.items()); - rs->insert_rules(final_user_rules_.items()); + rs->insert_rules(final_base_rules_.items(), final_user_rules_.items()); rs->insert_filters(rule_filters_); rs->insert_filters(input_filters_); rs->insert_preprocessors(preprocessors_); diff --git a/src/utils.cpp b/src/utils.cpp index 354c630ff..796788c1f 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -5,6 +5,7 @@ // Copyright 2021 Datadog, Inc. #include +#include #include #include @@ -52,7 +53,7 @@ ddwaf_object clone(ddwaf_object *input) ddwaf_object_invalid(&tmp); ddwaf_object copy; - std::list> queue; + std::deque> queue; clone_helper(*input, copy); if (is_container(input)) { diff --git a/tests/unit/collection_test.cpp b/tests/unit/collection_test.cpp deleted file mode 100644 index c41fc2f6d..000000000 --- a/tests/unit/collection_test.cpp +++ /dev/null @@ -1,630 +0,0 @@ -// 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 "collection.hpp" -#include "common/gtest_utils.hpp" -#include "condition/scalar_condition.hpp" -#include "matcher/exact_match.hpp" -#include "matcher/ip_match.hpp" - -using namespace ddwaf; -using namespace std::literals; - -namespace { - -template struct TestCollection : public testing::Test {}; - -// In the absence of actions, priority collections should behave as regular collections -using CollectionTypes = ::testing::Types; -TYPED_TEST_SUITE(TestCollection, CollectionTypes); - -// Validate that a rule within the collection matches only once -TYPED_TEST(TestCollection, SingleRuleMatch) -{ - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - - TypeParam rule_collection; - rule_collection.insert(rule); - - collection_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 events; - ddwaf::timer deadline{2s}; - rule_collection.match(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 events; - ddwaf::timer deadline{2s}; - rule_collection.match(events, store, cache, {}, {}, deadline); - - EXPECT_EQ(events.size(), 0); - } -} - -// Validate that once there's a match for a collection, a second match isn't possible -TYPED_TEST(TestCollection, MultipleRuleCachedMatch) -{ - std::vector> rules; - TypeParam rule_collection; - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category1"}}; - - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - rules.emplace_back(rule); - rule_collection.insert(rule); - } - - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("usr.id"); - builder.end_condition(std::vector{"admin"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category2"}}; - - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); - - rules.emplace_back(rule); - rule_collection.insert(rule); - } - - ddwaf::object_store store; - collection_cache cache; - - { - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin")); - store.insert(root); - - std::vector events; - ddwaf::timer deadline{2s}; - rule_collection.match(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 events; - ddwaf::timer deadline{2s}; - rule_collection.match(events, store, cache, {}, {}, deadline); - - EXPECT_EQ(events.size(), 0); - } -} - -// Validate that after a failed match, the collection can still produce a match -TYPED_TEST(TestCollection, MultipleRuleFailAndMatch) -{ - std::vector> rules; - TypeParam rule_collection; - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category1"}}; - - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - rules.emplace_back(rule); - rule_collection.insert(rule); - } - - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("usr.id"); - builder.end_condition(std::vector{"admin"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category2"}}; - - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); - - rules.emplace_back(rule); - rule_collection.insert(rule); - } - - ddwaf::object_store store; - collection_cache cache; - - { - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admino")); - store.insert(root); - - std::vector events; - ddwaf::timer deadline{2s}; - rule_collection.match(events, store, cache, {}, {}, deadline); - - EXPECT_EQ(events.size(), 0); - } - - { - 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 events; - ddwaf::timer deadline{2s}; - rule_collection.match(events, store, cache, {}, {}, deadline); - - EXPECT_EQ(events.size(), 1); - } -} - -// Validate that the rule cache is acted on -TYPED_TEST(TestCollection, SingleRuleMultipleCalls) -{ - test::expression_builder builder(2); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - builder.start_condition(); - builder.add_argument(); - builder.add_target("usr.id"); - builder.end_condition(std::vector{"admin"}); - - std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - - TypeParam rule_collection; - rule_collection.insert(rule); - - collection_cache cache; - { - 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")); - - ddwaf::object_store store; - store.insert(root); - - std::vector events; - ddwaf::timer deadline{2s}; - rule_collection.match(events, store, cache, {}, {}, deadline); - - EXPECT_EQ(events.size(), 0); - } - - { - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin")); - - ddwaf::object_store store; - store.insert(root); - - std::vector events; - ddwaf::timer deadline{2s}; - rule_collection.match(events, store, cache, {}, {}, deadline); - - EXPECT_EQ(events.size(), 1); - } -} - -// Validate that a match in a priority collection prevents further regular matches -TEST(TestPriorityCollection, NoRegularMatchAfterPriorityMatch) -{ - std::vector> rules; - collection regular; - priority_collection priority; - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category1"}}; - - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - rules.emplace_back(rule); - regular.insert(rule); - } - - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("usr.id"); - builder.end_condition(std::vector{"admin"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category2"}}; - - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); - - rules.emplace_back(rule); - priority.insert(rule); - } - - ddwaf::object_store store; - - collection_cache cache; - { - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin")); - store.insert(root); - - std::vector events; - ddwaf::timer deadline{2s}; - priority.match(events, store, cache, {}, {}, deadline); - - ASSERT_EQ(events.size(), 1); - ASSERT_EQ(events[0].rule->get_actions().size(), 1); - EXPECT_STREQ(events[0].rule->get_actions()[0].data(), "redirect"); - } - { - 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 events; - ddwaf::timer deadline{2s}; - regular.match(events, store, cache, {}, {}, deadline); - - EXPECT_EQ(events.size(), 0); - } -} - -// Validate that a match in a regular collection doesn't inhibit a match in a -// priority collection -TEST(TestPriorityCollection, PriorityMatchAfterRegularMatch) -{ - std::vector> rules; - collection regular; - priority_collection priority; - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category1"}}; - - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - rules.emplace_back(rule); - regular.insert(rule); - } - - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("usr.id"); - builder.end_condition(std::vector{"admin"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category2"}}; - - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); - - rules.emplace_back(rule); - priority.insert(rule); - } - - ddwaf::object_store store; - - collection_cache cache; - { - 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 events; - ddwaf::timer deadline{2s}; - regular.match(events, store, cache, {}, {}, deadline); - - EXPECT_EQ(events.size(), 1); - EXPECT_TRUE(events[0].rule->get_actions().empty()); - } - - { - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin")); - store.insert(root); - - std::vector events; - ddwaf::timer deadline{2s}; - priority.match(events, store, cache, {}, {}, deadline); - - ASSERT_EQ(events.size(), 1); - ASSERT_EQ(events[0].rule->get_actions().size(), 1); - EXPECT_STREQ(events[0].rule->get_actions()[0].data(), "redirect"); - } -} - -// Validate that a match in a priority collection prevents another match -TEST(TestPriorityCollection, NoPriorityMatchAfterPriorityMatch) -{ - std::vector> rules; - priority_collection priority; - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category1"}}; - - auto rule = std::make_shared( - "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); - - rules.emplace_back(rule); - priority.insert(rule); - } - - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("usr.id"); - builder.end_condition(std::vector{"admin"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category2"}}; - - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); - - rules.emplace_back(rule); - priority.insert(rule); - } - - ddwaf::object_store store; - - collection_cache cache; - { - 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 events; - ddwaf::timer deadline{2s}; - priority.match(events, store, cache, {}, {}, deadline); - - ASSERT_EQ(events.size(), 1); - ASSERT_EQ(events[0].rule->get_actions().size(), 1); - EXPECT_STREQ(events[0].rule->get_actions()[0].data(), "block"); - } - - { - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin")); - store.insert(root); - - std::vector events; - ddwaf::timer deadline{2s}; - priority.match(events, store, cache, {}, {}, deadline); - - ASSERT_EQ(events.size(), 0); - } -} - -// Validate that an ephemeral match in a priority collection doesn't another match -TEST(TestPriorityCollection, NoPriorityMatchAfterEphemeralPriorityMatch) -{ - std::vector> rules; - priority_collection priority; - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category1"}}; - - auto rule = std::make_shared( - "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); - - rules.emplace_back(rule); - priority.insert(rule); - } - - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("usr.id"); - builder.end_condition(std::vector{"admin"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category2"}}; - - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); - - rules.emplace_back(rule); - priority.insert(rule); - } - - ddwaf::object_store store; - - collection_cache cache; - { - auto scope = store.get_eval_scope(); - - 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, object_store::attribute::ephemeral); - - std::vector events; - ddwaf::timer deadline{2s}; - priority.match(events, store, cache, {}, {}, deadline); - - ASSERT_EQ(events.size(), 1); - ASSERT_EQ(events[0].rule->get_actions().size(), 1); - EXPECT_STREQ(events[0].rule->get_actions()[0].data(), "block"); - } - - { - auto scope = store.get_eval_scope(); - - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin")); - store.insert(root); - - std::vector events; - ddwaf::timer deadline{2s}; - priority.match(events, store, cache, {}, {}, deadline); - - ASSERT_EQ(events.size(), 1); - } -} - -// Validate that an ephemeral match in a priority collection prevents another match -// within the same evaluation -TEST(TestPriorityCollection, EphemeralPriorityMatchNoOtherMatches) -{ - std::vector> rules; - priority_collection priority; - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("http.client_ip"); - builder.end_condition(std::vector{"192.168.0.1"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category1"}}; - - auto rule = std::make_shared( - "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); - - rules.emplace_back(rule); - priority.insert(rule); - } - - { - test::expression_builder builder(1); - builder.start_condition(); - builder.add_argument(); - builder.add_target("usr.id"); - builder.end_condition(std::vector{"admin"}); - - std::unordered_map tags{ - {"type", "type"}, {"category", "category2"}}; - - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); - - rules.emplace_back(rule); - priority.insert(rule); - } - - ddwaf::timer deadline{2s}; - ddwaf::object_store store; - - collection_cache cache; - { - 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, object_store::attribute::ephemeral); - } - - { - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin")); - store.insert(root); - } - - std::vector events; - priority.match(events, store, cache, {}, {}, deadline); - - ASSERT_EQ(events.size(), 1); - ASSERT_EQ(events[0].rule->get_actions().size(), 1); - EXPECT_STREQ(events[0].rule->get_actions()[0].data(), "block"); -} - -} // namespace From c1ebdc013b15cd9a44dfcfa33307adee6bb8d2d1 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 27 Oct 2024 09:55:18 +0000 Subject: [PATCH 2/8] Fix tests --- cmake/objects.cmake | 1 - src/collection.cpp | 121 ------------------ src/collection.hpp | 66 ---------- src/module.cpp | 1 + src/module.hpp | 25 ++-- tests/unit/context_test.cpp | 241 +++++++++++++++++------------------- tests/unit/ruleset_test.cpp | 180 +++++++++++++-------------- 7 files changed, 216 insertions(+), 419 deletions(-) delete mode 100644 src/collection.cpp delete mode 100644 src/collection.hpp diff --git a/cmake/objects.cmake b/cmake/objects.cmake index 53948f975..4cedc712a 100644 --- a/cmake/objects.cmake +++ b/cmake/objects.cmake @@ -8,7 +8,6 @@ set(LIBDDWAF_SOURCE ${libddwaf_SOURCE_DIR}/src/event.cpp ${libddwaf_SOURCE_DIR}/src/object.cpp ${libddwaf_SOURCE_DIR}/src/object_store.cpp - ${libddwaf_SOURCE_DIR}/src/collection.cpp ${libddwaf_SOURCE_DIR}/src/module.cpp ${libddwaf_SOURCE_DIR}/src/expression.cpp ${libddwaf_SOURCE_DIR}/src/ruleset_info.cpp diff --git a/src/collection.cpp b/src/collection.cpp deleted file mode 100644 index 5cc3fc1e2..000000000 --- a/src/collection.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include - -#include "clock.hpp" -#include "collection.hpp" -#include "context_allocator.hpp" -#include "event.hpp" -#include "exception.hpp" -#include "exclusion/common.hpp" -#include "log.hpp" -#include "matcher/base.hpp" -#include "object_store.hpp" -#include "rule.hpp" - -namespace ddwaf { - -std::optional match_rule(rule *rule, const object_store &store, - memory::unordered_map &cache, - const exclusion::context_policy &policy, - const std::unordered_map> &dynamic_matchers, - ddwaf::timer &deadline) -{ - const auto &id = rule->get_id(); - - if (deadline.expired()) { - DDWAF_INFO("Ran out of time while evaluating rule '{}'", id); - throw timeout_exception(); - } - - if (!rule->is_enabled()) { - DDWAF_DEBUG("Rule '{}' is disabled", id); - return std::nullopt; - } - - std::string_view action_override; - auto exclusion = policy.find(rule); - if (exclusion.mode == exclusion::filter_mode::bypass) { - DDWAF_DEBUG("Bypassing rule '{}'", id); - return std::nullopt; - } - - if (exclusion.mode == exclusion::filter_mode::monitor) { - DDWAF_DEBUG("Monitoring rule '{}'", id); - action_override = "monitor"; - } else if (exclusion.mode == exclusion::filter_mode::custom) { - action_override = exclusion.action_override; - DDWAF_DEBUG("Evaluating rule '{}' with custom action '{}'", id, action_override); - } else { - DDWAF_DEBUG("Evaluating rule '{}'", id); - } - - try { - auto it = cache.find(rule); - if (it == cache.end()) { - auto [new_it, res] = cache.emplace(rule, rule::cache_type{}); - it = new_it; - } - - rule::cache_type &rule_cache = it->second; - std::optional event; - event = rule->match(store, rule_cache, exclusion.objects, dynamic_matchers, deadline); - - if (event.has_value()) { - event->action_override = action_override; - } - - return event; - } catch (const ddwaf::timeout_exception &) { - DDWAF_INFO("Ran out of time while evaluating rule '{}'", id); - throw; - } - - return std::nullopt; -} - -template -void base_collection::match(std::vector &events, object_store &store, - collection_cache &cache, const exclusion::context_policy &exclusion, - const std::unordered_map> &dynamic_matchers, - ddwaf::timer &deadline) const -{ - if (cache.result >= Derived::type()) { - // If the result was cached but ephemeral, clear it. Note that this is - // just a workaround taking advantage of the order of evaluation of - // collections. Collections might be removed in the future altogether. - if (cache.result == Derived::type() && cache.ephemeral) { - cache.result = collection_type::none; - cache.ephemeral = false; - } else { - return; - } - } - - for (auto *rule : rules_) { - auto event = - match_rule(rule, store, cache.rule_cache, exclusion, dynamic_matchers, deadline); - if (event.has_value()) { - cache.result = Derived::type(); - cache.ephemeral = event->ephemeral; - - events.emplace_back(std::move(*event)); - DDWAF_DEBUG("Found event on rule {}", rule->get_id()); - break; - } - } -} - -template class base_collection; -template class base_collection; - -} // namespace ddwaf diff --git a/src/collection.hpp b/src/collection.hpp deleted file mode 100644 index 6f4e883ce..000000000 --- a/src/collection.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// 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. - -#pragma once - -#include "context_allocator.hpp" -#include "event.hpp" -#include "exclusion/rule_filter.hpp" -#include "rule.hpp" - -namespace ddwaf { - -enum class collection_type : uint8_t { none = 0, regular = 1, priority = 2 }; - -// Collections are used to organize rules depending on their rule.type field. The overall goal -// behind the collection concept is to group rules of a similar nature and only evaluate as many of -// those rules until there is a match. Priority collections and regular collections only differ on -// how they interact with the cache, e.g. a priority collection will try to match even if there has -// already been a match in a regular collection. - -// The collection cache is shared by both priority and regular collections, this ensures that -// regular collections for which there is an equivalent priority collection of the same type, -// aren't processed when the respective priority collection has already had a match. -struct collection_cache { - bool ephemeral{false}; - collection_type result{collection_type::none}; - memory::unordered_map rule_cache; -}; - -template class base_collection { -public: - using object_set = std::unordered_set; - using cache_type = collection_cache; - - base_collection() = default; - ~base_collection() = default; - base_collection(const base_collection &) = default; - base_collection(base_collection &&) noexcept = default; - base_collection &operator=(const base_collection &) = default; - base_collection &operator=(base_collection &&) noexcept = default; - - void insert(const std::shared_ptr &rule) { rules_.emplace_back(rule.get()); } - - void match(std::vector &events, object_store &store, collection_cache &cache, - const exclusion::context_policy &exclusion, - const std::unordered_map> &dynamic_matchers, - ddwaf::timer &deadline) const; - -protected: - std::vector rules_{}; -}; - -class collection : public base_collection { -public: - static constexpr collection_type type() { return collection_type::regular; } -}; - -class priority_collection : public base_collection { -public: - static constexpr collection_type type() { return collection_type::priority; } -}; - -} // namespace ddwaf diff --git a/src/module.cpp b/src/module.cpp index f7d6f8510..ca5a947e3 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -89,6 +89,7 @@ void collection_module::eval(std::vector &events, object_store &store, mo } for (const auto &collection : collections_) { + DDWAF_DEBUG("Evaluating collection: {}", collection.name); auto &collection_cache = cache.collections[collection.name]; if (collection_cache.type >= collection.type) { // If the result was cached but ephemeral, clear it. Note that this is diff --git a/src/module.hpp b/src/module.hpp index 15cb85ece..8a766b79a 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -52,6 +52,8 @@ class collection_module { iterator end() { return rules_.end(); } [[nodiscard]] const_iterator end() const { return rules_.end(); } + std::shared_ptr operator[](std::size_t index) { return rules_.at(index); } + protected: struct rule_collection { std::string_view name; @@ -92,22 +94,23 @@ class collection_module_builder { // Compute the ranges of each collection std::vector collections; if (!rules_.empty()) { - auto current_type = rules_[0]->get_collection(); - collections.emplace_back(current_type, - rules_[0]->get_actions().empty() ? collection_type::regular - : collection_type::priority, - 0, rules_.size()); + auto current_name = rules_[0]->get_collection(); + auto current_type = rules_[0]->get_actions().empty() ? collection_type::regular + : collection_type::priority; + + collections.emplace_back(current_name, current_type, 0, rules_.size()); for (std::size_t i = 1; i < rules_.size(); ++i) { - auto this_type = rules_[i]->get_collection(); - if (this_type != current_type) { + auto this_name = rules_[i]->get_collection(); + auto this_type = rules_[i]->get_actions().empty() ? collection_type::regular + : collection_type::priority; + + if (this_type != current_type || this_name != current_name) { collections.back().end = i; + current_name = this_name; current_type = this_type; - collections.emplace_back(current_type, - rules_[0]->get_actions().empty() ? collection_type::regular - : collection_type::priority, - i, rules_.size()); + collections.emplace_back(current_name, current_type, i, rules_.size()); } } } diff --git a/tests/unit/context_test.cpp b/tests/unit/context_test.cpp index 2d893a8df..f06ba2b7c 100644 --- a/tests/unit/context_test.cpp +++ b/tests/unit/context_test.cpp @@ -132,7 +132,7 @@ TEST(TestContext, PreprocessorEval) EXPECT_CALL(*rule, match(_, _, _, _, _)).InSequence(seq).WillOnce(Return(std::nullopt)); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); ruleset->preprocessors.emplace("id", proc); ddwaf::context ctx(ruleset); @@ -164,7 +164,7 @@ TEST(TestContext, PostprocessorEval) EXPECT_CALL(*proc, eval(_, _, _, _)).InSequence(seq); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); ruleset->postprocessors.emplace("id", proc); ddwaf::context ctx(ruleset); @@ -190,7 +190,7 @@ TEST(TestContext, SkipRuleNoTargets) auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); EXPECT_CALL(*rule, match(_, _, _, _, _)).Times(0); @@ -217,7 +217,7 @@ TEST(TestContext, MatchTimeout) auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); ddwaf::timer deadline{0s}; ddwaf::test::context ctx(ruleset); @@ -244,7 +244,7 @@ TEST(TestContext, NoMatch) auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -272,7 +272,7 @@ TEST(TestContext, Match) auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -290,6 +290,7 @@ TEST(TestContext, Match) TEST(TestContext, MatchMultipleRulesInCollectionSingleRun) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -300,9 +301,8 @@ TEST(TestContext, MatchMultipleRulesInCollectionSingleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id1", "name1", std::move(tags), builder.build())); } { @@ -315,11 +315,12 @@ TEST(TestContext, MatchMultipleRulesInCollectionSingleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id2", "name2", std::move(tags), builder.build())); } + ruleset->insert_rules(rules, {}); + ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -354,6 +355,7 @@ TEST(TestContext, MatchMultipleRulesInCollectionSingleRun) TEST(TestContext, MatchMultipleRulesWithPrioritySingleRun) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -364,9 +366,8 @@ TEST(TestContext, MatchMultipleRulesWithPrioritySingleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id1", "name1", std::move(tags), builder.build())); } { @@ -379,11 +380,10 @@ TEST(TestContext, MatchMultipleRulesWithPrioritySingleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"block"}); - - ruleset->insert_rule(rule); + rules.emplace_back(std::make_shared( + "id2", "name2", std::move(tags), builder.build(), std::vector{"block"})); } + ruleset->insert_rules(rules, {}); { ddwaf::test::context ctx(ruleset); @@ -429,6 +429,7 @@ TEST(TestContext, MatchMultipleRulesWithPrioritySingleRun) TEST(TestContext, MatchMultipleRulesInCollectionDoubleRun) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -439,9 +440,8 @@ TEST(TestContext, MatchMultipleRulesInCollectionDoubleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id1", "name1", std::move(tags), builder.build())); } { @@ -454,10 +454,10 @@ TEST(TestContext, MatchMultipleRulesInCollectionDoubleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id2", "name2", std::move(tags), builder.build())); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -505,6 +505,7 @@ TEST(TestContext, MatchMultipleRulesInCollectionDoubleRun) TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityLast) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -515,9 +516,8 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityLast) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id1", "name1", std::move(tags), builder.build())); } { @@ -530,11 +530,10 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityLast) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"block"}); - - ruleset->insert_rule(rule); + rules.emplace_back(std::make_shared( + "id2", "name2", std::move(tags), builder.build(), std::vector{"block"})); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -602,6 +601,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityLast) TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -612,10 +612,8 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared( - "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); - - ruleset->insert_rule(rule); + rules.emplace_back(std::make_shared( + "id1", "name1", std::move(tags), builder.build(), std::vector{"block"})); } { @@ -628,10 +626,10 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id2", "name2", std::move(tags), builder.build())); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -681,6 +679,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -691,9 +690,8 @@ TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id1", "name1", std::move(tags), builder.build())); } { @@ -706,11 +704,10 @@ TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); - - ruleset->insert_rule(rule); + rules.emplace_back(std::make_shared("id2", "name2", std::move(tags), + builder.build(), std::vector{"redirect"})); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -776,6 +773,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) TEST(TestContext, MatchMultipleCollectionsSingleRun) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -786,9 +784,8 @@ TEST(TestContext, MatchMultipleCollectionsSingleRun) std::unordered_map tags{ {"type", "type1"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id1", "name1", std::move(tags), builder.build())); } { @@ -801,10 +798,10 @@ TEST(TestContext, MatchMultipleCollectionsSingleRun) std::unordered_map tags{ {"type", "type2"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id2", "name2", std::move(tags), builder.build())); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -823,6 +820,7 @@ TEST(TestContext, MatchMultipleCollectionsSingleRun) TEST(TestContext, MatchMultiplePriorityCollectionsSingleRun) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -833,10 +831,8 @@ TEST(TestContext, MatchMultiplePriorityCollectionsSingleRun) std::unordered_map tags{ {"type", "type1"}, {"category", "category1"}}; - auto rule = std::make_shared( - "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); - - ruleset->insert_rule(rule); + rules.emplace_back(std::make_shared( + "id1", "name1", std::move(tags), builder.build(), std::vector{"block"})); } { @@ -849,11 +845,10 @@ TEST(TestContext, MatchMultiplePriorityCollectionsSingleRun) std::unordered_map tags{ {"type", "type2"}, {"category", "category2"}}; - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); - - ruleset->insert_rule(rule); + rules.emplace_back(std::make_shared("id2", "name2", std::move(tags), + builder.build(), std::vector{"redirect"})); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -872,6 +867,7 @@ TEST(TestContext, MatchMultiplePriorityCollectionsSingleRun) TEST(TestContext, MatchMultipleCollectionsDoubleRun) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -882,9 +878,8 @@ TEST(TestContext, MatchMultipleCollectionsDoubleRun) std::unordered_map tags{ {"type", "type1"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id1", "name1", std::move(tags), builder.build())); } { @@ -897,10 +892,10 @@ TEST(TestContext, MatchMultipleCollectionsDoubleRun) std::unordered_map tags{ {"type", "type2"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("id2", "name2", std::move(tags), builder.build())); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -931,6 +926,7 @@ TEST(TestContext, MatchMultipleCollectionsDoubleRun) TEST(TestContext, MatchMultiplePriorityCollectionsDoubleRun) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -941,10 +937,8 @@ TEST(TestContext, MatchMultiplePriorityCollectionsDoubleRun) std::unordered_map tags{ {"type", "type1"}, {"category", "category1"}}; - auto rule = std::make_shared( - "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); - - ruleset->insert_rule(rule); + rules.emplace_back(std::make_shared( + "id1", "name1", std::move(tags), builder.build(), std::vector{"block"})); } { @@ -957,11 +951,10 @@ TEST(TestContext, MatchMultiplePriorityCollectionsDoubleRun) std::unordered_map tags{ {"type", "type2"}, {"category", "category2"}}; - auto rule = std::make_shared( - "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); - - ruleset->insert_rule(rule); + rules.emplace_back(std::make_shared("id2", "name2", std::move(tags), + builder.build(), std::vector{"redirect"})); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -1008,7 +1001,7 @@ TEST(TestContext, SkipRuleFilterNoTargets) rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1057,7 +1050,7 @@ TEST(TestContext, SkipRuleButNotRuleFilterNoTargets) rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1105,7 +1098,7 @@ TEST(TestContext, RuleFilterWithCondition) rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1157,7 +1150,7 @@ TEST(TestContext, RuleFilterWithEphemeralConditionMatch) rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1218,7 +1211,7 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralBypassPersistentMonitor) rule = std::make_shared("id", "name", std::move(tags), builder.build()); rule->set_actions({"block"}); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1295,7 +1288,7 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralMonitorPersistentBypass) rule = std::make_shared("id", "name", std::move(tags), builder.build()); rule->set_actions({"block"}); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1369,7 +1362,7 @@ TEST(TestContext, RuleFilterTimeout) rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1416,7 +1409,7 @@ TEST(TestContext, NoRuleFilterWithCondition) rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1464,9 +1457,8 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRules) rules.emplace_back(std::make_shared("id" + std::to_string(i), "name", std::move(tags), std::make_shared(), std::vector{})); - - ruleset->insert_rule(rules.back()); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -1538,9 +1530,8 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRules) rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), std::make_shared(), std::vector{})); - - ruleset->insert_rule(rules.back()); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -1648,9 +1639,8 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRulesWithConditions) rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), std::make_shared(), std::vector{})); - - ruleset->insert_rule(rules.back()); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -1735,9 +1725,8 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRulesWithConditions) rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), std::make_shared(), std::vector{})); - - ruleset->insert_rule(rules.back()); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -1827,7 +1816,7 @@ TEST(TestContext, SkipInputFilterNoTargets) rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1873,7 +1862,7 @@ TEST(TestContext, SkipRuleButNotInputFilterNoTargets) rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1920,7 +1909,7 @@ TEST(TestContext, InputFilterExclude) "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); ruleset->insert_filter(filter); ddwaf::timer deadline{2s}; @@ -1960,7 +1949,7 @@ TEST(TestContext, InputFilterExcludeEphemeral) "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); ruleset->insert_filter(filter); ddwaf::test::context ctx(ruleset); @@ -2011,7 +2000,7 @@ TEST(TestContext, InputFilterExcludeEphemeralReuseObject) "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); auto ruleset = test::get_default_ruleset(); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); ruleset->insert_filter(filter); ruleset->free_fn = nullptr; @@ -2046,7 +2035,7 @@ TEST(TestContext, InputFilterExcludeRule) auto ruleset = test::get_default_ruleset(); auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); { auto obj_filter = std::make_shared(); @@ -2101,7 +2090,7 @@ TEST(TestContext, InputFilterExcludeRuleEphemeral) auto ruleset = test::get_default_ruleset(); auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); { auto obj_filter = std::make_shared(); @@ -2151,7 +2140,7 @@ TEST(TestContext, InputFilterMonitorRuleEphemeral) auto ruleset = test::get_default_ruleset(); auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); { auto obj_filter = std::make_shared(); @@ -2206,7 +2195,7 @@ TEST(TestContext, InputFilterExcluderRuleEphemeralAndPersistent) auto ruleset = test::get_default_ruleset(); auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); { auto obj_filter = std::make_shared(); @@ -2267,7 +2256,7 @@ TEST(TestContext, InputFilterMonitorRuleEphemeralAndPersistent) auto ruleset = test::get_default_ruleset(); auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); { auto obj_filter = std::make_shared(); @@ -2335,7 +2324,7 @@ TEST(TestContext, InputFilterWithCondition) auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } { @@ -2424,7 +2413,7 @@ TEST(TestContext, InputFilterWithEphemeralCondition) auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } { @@ -2472,6 +2461,7 @@ TEST(TestContext, InputFilterWithEphemeralCondition) TEST(TestContext, InputFilterMultipleRules) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -2482,10 +2472,8 @@ TEST(TestContext, InputFilterMultipleRules) std::unordered_map tags{ {"type", "ip_type"}, {"category", "category"}}; - auto rule = - std::make_shared("ip_id", "name", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("ip_id", "name", std::move(tags), builder.build())); } { @@ -2498,11 +2486,10 @@ TEST(TestContext, InputFilterMultipleRules) std::unordered_map tags{ {"type", "usr_type"}, {"category", "category"}}; - auto rule = - std::make_shared("usr_id", "name", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("usr_id", "name", std::move(tags), builder.build())); } + ruleset->insert_rules(rules, {}); { auto obj_filter = std::make_shared(); @@ -2585,6 +2572,7 @@ TEST(TestContext, InputFilterMultipleRules) TEST(TestContext, InputFilterMultipleRulesMultipleFilters) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -2595,10 +2583,8 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFilters) std::unordered_map tags{ {"type", "ip_type"}, {"category", "category"}}; - auto rule = - std::make_shared("ip_id", "name", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("ip_id", "name", std::move(tags), builder.build())); } { @@ -2611,11 +2597,10 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFilters) std::unordered_map tags{ {"type", "usr_type"}, {"category", "category"}}; - auto rule = - std::make_shared("usr_id", "name", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("usr_id", "name", std::move(tags), builder.build())); } + ruleset->insert_rules(rules, {}); { auto obj_filter = std::make_shared(); @@ -2711,6 +2696,7 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFilters) TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -2721,10 +2707,8 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) std::unordered_map tags{ {"type", "ip_type"}, {"category", "category"}}; - auto rule = - std::make_shared("ip_id", "name", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("ip_id", "name", std::move(tags), builder.build())); } { @@ -2737,10 +2721,8 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) std::unordered_map tags{ {"type", "usr_type"}, {"category", "category"}}; - auto rule = - std::make_shared("usr_id", "name", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("usr_id", "name", std::move(tags), builder.build())); } { @@ -2753,11 +2735,10 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) std::unordered_map tags{ {"type", "cookie_type"}, {"category", "category"}}; - auto rule = - std::make_shared("cookie_id", "name", std::move(tags), builder.build()); - - ruleset->insert_rule(rule); + rules.emplace_back( + std::make_shared("cookie_id", "name", std::move(tags), builder.build())); } + ruleset->insert_rules(rules, {}); auto ip_rule = ruleset->rules[0]; auto usr_rule = ruleset->rules[1]; diff --git a/tests/unit/ruleset_test.cpp b/tests/unit/ruleset_test.cpp index 4af95953f..8d1c900d3 100644 --- a/tests/unit/ruleset_test.cpp +++ b/tests/unit/ruleset_test.cpp @@ -32,24 +32,24 @@ TEST(TestRuleset, InsertSingleRegularBaseRules) { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 3); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + /* //EXPECT_EQ(ruleset.base/g_collections.size(), 3);*/ + /*//EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0);*/ + /*//EXPECT_EQ(ruleset.user/g_collections.size(), 0);*/ + /*//EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0);*/ } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 3); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + /* //EXPECT_EQ(ruleset.base/g_collections.size(), 3);*/ + /*//EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0);*/ + /*//EXPECT_EQ(ruleset.user/g_collections.size(), 0);*/ + /*//EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0);*/ } } @@ -66,24 +66,24 @@ TEST(TestRuleset, InsertSinglePriorityBaseRules) { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 3); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + ////EXPECT_EQ(ruleset.base/g_collections.size(), 0); + ////EXPECT_EQ(ruleset.base/g_priority_collections.size(), 3); + ////EXPECT_EQ(ruleset.user/g_collections.size(), 0); + ////EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0); } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 3); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + ////EXPECT_EQ(ruleset.base/g_collections.size(), 0); + ////EXPECT_EQ(ruleset.base/g_priority_collections.size(), 3); + ////EXPECT_EQ(ruleset.user/g_collections.size(), 0); + ////EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0); } } @@ -100,24 +100,24 @@ TEST(TestRuleset, InsertSingleMixedBaseRules) { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 3); - EXPECT_EQ(ruleset.base_priority_collections.size(), 2); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_collections.size(), 3); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.user/g_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0); } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 3); - EXPECT_EQ(ruleset.base_priority_collections.size(), 2); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_collections.size(), 3); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.user/g_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0); } } @@ -140,25 +140,25 @@ TEST(TestRuleset, InsertSingleRegularUserRules) { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 3); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_collections.size(), 3); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0); } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 3); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_collections.size(), 3); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0); } } @@ -180,24 +180,24 @@ TEST(TestRuleset, InsertSinglePriorityUserRules) }; { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 3); + // EXPECT_EQ(ruleset.base/g_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 3); } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 3); + // EXPECT_EQ(ruleset.base/g_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 3); } } @@ -220,24 +220,24 @@ TEST(TestRuleset, InsertSingleMixedUserRules) { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 3); - EXPECT_EQ(ruleset.user_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.base/g_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_collections.size(), 3); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 2); } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 3); - EXPECT_EQ(ruleset.user_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.base/g_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_collections.size(), 3); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 2); } } @@ -260,24 +260,24 @@ TEST(TestRuleset, InsertSingleRegularMixedRules) { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 3); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 2); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_collections.size(), 3); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_collections.size(), 2); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0); } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 3); - EXPECT_EQ(ruleset.base_priority_collections.size(), 0); - EXPECT_EQ(ruleset.user_collections.size(), 2); - EXPECT_EQ(ruleset.user_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_collections.size(), 3); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_collections.size(), 2); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 0); } } @@ -299,24 +299,24 @@ TEST(TestRuleset, InsertSinglePriorityMixedRules) }; { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 3); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.base/g_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 3); + // EXPECT_EQ(ruleset.user/g_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 2); } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 6); - EXPECT_EQ(ruleset.base_collections.size(), 0); - EXPECT_EQ(ruleset.base_priority_collections.size(), 3); - EXPECT_EQ(ruleset.user_collections.size(), 0); - EXPECT_EQ(ruleset.user_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.base/g_collections.size(), 0); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 3); + // EXPECT_EQ(ruleset.user/g_collections.size(), 0); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 2); } } @@ -345,24 +345,24 @@ TEST(TestRuleset, InsertSingleMixedMixedRules) { ddwaf::ruleset ruleset; - for (const auto &rule : rules) { ruleset.insert_rule(rule); } + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 12); - EXPECT_EQ(ruleset.base_collections.size(), 3); - EXPECT_EQ(ruleset.base_priority_collections.size(), 2); - EXPECT_EQ(ruleset.user_collections.size(), 3); - EXPECT_EQ(ruleset.user_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.base/g_collections.size(), 3); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.user/g_collections.size(), 3); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 2); } { ddwaf::ruleset ruleset; - ruleset.insert_rules(rules); + ruleset.insert_rules(rules, {}); EXPECT_EQ(ruleset.rules.size(), 12); - EXPECT_EQ(ruleset.base_collections.size(), 3); - EXPECT_EQ(ruleset.base_priority_collections.size(), 2); - EXPECT_EQ(ruleset.user_collections.size(), 3); - EXPECT_EQ(ruleset.user_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.base/g_collections.size(), 3); + // EXPECT_EQ(ruleset.base/g_priority_collections.size(), 2); + // EXPECT_EQ(ruleset.user/g_collections.size(), 3); + // EXPECT_EQ(ruleset.user/g_priority_collections.size(), 2); } } From db4fa3fe42c280eb8fd9b95c930c8fec4cd0d7a8 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 27 Oct 2024 11:26:17 +0000 Subject: [PATCH 3/8] Minor fixes --- src/module.cpp | 1 + src/module.hpp | 6 ++++-- src/utils.cpp | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/module.cpp b/src/module.cpp index ca5a947e3..815a9b70f 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -3,6 +3,7 @@ // // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. +#include #include #include #include diff --git a/src/module.hpp b/src/module.hpp index 8a766b79a..838106c28 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -79,6 +79,7 @@ class collection_module_builder { collection_module build() { + using rule_collection = collection_module::rule_collection; std::sort(rules_.begin(), rules_.end(), [](const auto &left, const auto &right) { auto ltype = left->get_collection(); auto rtype = right->get_collection(); @@ -98,7 +99,7 @@ class collection_module_builder { auto current_type = rules_[0]->get_actions().empty() ? collection_type::regular : collection_type::priority; - collections.emplace_back(current_name, current_type, 0, rules_.size()); + collections.emplace_back(rule_collection{current_name, current_type, 0, rules_.size()}); for (std::size_t i = 1; i < rules_.size(); ++i) { auto this_name = rules_[i]->get_collection(); @@ -110,7 +111,8 @@ class collection_module_builder { current_name = this_name; current_type = this_type; - collections.emplace_back(current_name, current_type, i, rules_.size()); + collections.emplace_back( + rule_collection{current_name, current_type, i, rules_.size()}); } } } diff --git a/src/utils.cpp b/src/utils.cpp index 796788c1f..995ddd468 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "ddwaf.h" From 2656f12c5355b86d1430db42b2f3ff572133cd2f Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:06:23 +0000 Subject: [PATCH 4/8] Revert a small change --- src/utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils.cpp b/src/utils.cpp index 995ddd468..354c630ff 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -5,7 +5,7 @@ // Copyright 2021 Datadog, Inc. #include -#include +#include #include #include "ddwaf.h" @@ -52,7 +52,7 @@ ddwaf_object clone(ddwaf_object *input) ddwaf_object_invalid(&tmp); ddwaf_object copy; - std::deque> queue; + std::list> queue; clone_helper(*input, copy); if (is_container(input)) { From 15ad92362cd5ca8dea41d9d2fa3518f9a786a01a Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:58:16 +0000 Subject: [PATCH 5/8] Use reduced iterations bench --- .gitlab/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/benchmarks.yml b/.gitlab/benchmarks.yml index deaaa72f7..c04c2de11 100644 --- a/.gitlab/benchmarks.yml +++ b/.gitlab/benchmarks.yml @@ -11,7 +11,7 @@ benchmarks: script: - export ARTIFACTS_DIR="$(pwd)/reports" && (mkdir "${ARTIFACTS_DIR}" || :) - git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.ddbuild.io/DataDog/".insteadOf "https://github.com/DataDog/" - - git clone --branch libddwaf https://github.com/DataDog/benchmarking-platform /platform && cd /platform + - git clone --branch anilm3/reduced-iterations https://github.com/DataDog/benchmarking-platform /platform && cd /platform - ./steps/capture-hardware-software-info.sh - ./steps/run-benchmarks.sh - ./steps/analyze-results.sh From 5606ea4a2a4def8015b1c4a7b1ab6d58ba85a6d5 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 27 Oct 2024 14:34:36 +0000 Subject: [PATCH 6/8] Split shared storage and rules --- src/module.hpp | 14 +++++++------- src/ruleset.hpp | 9 +++++++-- tests/unit/context_test.cpp | 22 +++++++++++----------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/module.hpp b/src/module.hpp index 838106c28..c23cc0443 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -28,8 +28,8 @@ struct module_cache { class collection_module { public: using cache_type = module_cache; - using iterator = std::vector>::iterator; - using const_iterator = std::vector>::const_iterator; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; collection_module() = default; ~collection_module() = default; @@ -52,7 +52,7 @@ class collection_module { iterator end() { return rules_.end(); } [[nodiscard]] const_iterator end() const { return rules_.end(); } - std::shared_ptr operator[](std::size_t index) { return rules_.at(index); } + ddwaf::rule *operator[](std::size_t index) { return rules_.at(index); } protected: struct rule_collection { @@ -63,19 +63,19 @@ class collection_module { }; collection_module( - std::vector &&collections, std::vector> &&rules) + std::vector &&collections, std::vector &&rules) : collections_(std::move(collections)), rules_(std::move(rules)) {} std::vector collections_; - std::vector> rules_; + std::vector rules_; friend class collection_module_builder; }; class collection_module_builder { public: - void insert(const std::shared_ptr &rule) { rules_.emplace_back(rule); } + void insert(ddwaf::rule *rule) { rules_.emplace_back(rule); } collection_module build() { @@ -123,6 +123,6 @@ class collection_module_builder { protected: // Keep track of the first index of the collection std::unordered_map collection_start_; - std::vector> rules_; + std::vector rules_; }; } // namespace ddwaf diff --git a/src/ruleset.hpp b/src/ruleset.hpp index 4bdb37bae..bb3c1d4e9 100644 --- a/src/ruleset.hpp +++ b/src/ruleset.hpp @@ -26,13 +26,17 @@ struct ruleset { void insert_rules(const std::vector> &base_rules, const std::vector> &user_rules) { + shared_rules.reserve(base_rules.size() + user_rules.size()); + collection_module_builder builder; for (const auto &rule : base_rules) { - builder.insert(rule); + shared_rules.emplace_back(rule); + builder.insert(rule.get()); rule->get_addresses(rule_addresses); } for (const auto &rule : user_rules) { - builder.insert(rule); + shared_rules.emplace_back(rule); + builder.insert(rule.get()); rule->get_addresses(rule_addresses); } rules = builder.build(); @@ -157,6 +161,7 @@ struct ruleset { std::vector> scanners; std::shared_ptr actions; + std::vector> shared_rules; collection_module rules; std::unordered_map rule_addresses; diff --git a/tests/unit/context_test.cpp b/tests/unit/context_test.cpp index f06ba2b7c..527754b47 100644 --- a/tests/unit/context_test.cpp +++ b/tests/unit/context_test.cpp @@ -2337,7 +2337,7 @@ TEST(TestContext, InputFilterWithCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{ruleset->rules[0].get()}; + std::set eval_filters{ruleset->rules[0]}; auto filter = std::make_shared( "1", builder.build(), std::move(eval_filters), std::move(obj_filter)); @@ -2426,7 +2426,7 @@ TEST(TestContext, InputFilterWithEphemeralCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{ruleset->rules[0].get()}; + std::set eval_filters{ruleset->rules[0]}; auto filter = std::make_shared( "1", builder.build(), std::move(eval_filters), std::move(obj_filter)); @@ -2496,7 +2496,7 @@ TEST(TestContext, InputFilterMultipleRules) obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); obj_filter->insert(get_target_index("usr.id"), "usr.id"); - std::set eval_filters{ruleset->rules[0].get(), ruleset->rules[1].get()}; + std::set eval_filters{ruleset->rules[0], ruleset->rules[1]}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2606,7 +2606,7 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFilters) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{ruleset->rules[0].get()}; + std::set eval_filters{ruleset->rules[0]}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2617,7 +2617,7 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFilters) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("usr.id"), "usr.id"); - std::set eval_filters{ruleset->rules[1].get()}; + std::set eval_filters{ruleset->rules[1]}; auto filter = std::make_shared( "2", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2740,16 +2740,16 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) } ruleset->insert_rules(rules, {}); - auto ip_rule = ruleset->rules[0]; - auto usr_rule = ruleset->rules[1]; - auto cookie_rule = ruleset->rules[2]; + auto *ip_rule = ruleset->rules[0]; + auto *usr_rule = ruleset->rules[1]; + auto *cookie_rule = ruleset->rules[2]; { auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); obj_filter->insert(get_target_index("server.request.headers"), "server.request.headers"); - std::set eval_filters{ip_rule.get(), cookie_rule.get()}; + std::set eval_filters{ip_rule, cookie_rule}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2761,7 +2761,7 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) obj_filter->insert(get_target_index("usr.id"), "usr.id"); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{usr_rule.get(), ip_rule.get()}; + std::set eval_filters{usr_rule, ip_rule}; auto filter = std::make_shared( "2", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2773,7 +2773,7 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) obj_filter->insert(get_target_index("usr.id"), "usr.id"); obj_filter->insert(get_target_index("server.request.headers"), "server.request.headers"); - std::set eval_filters{usr_rule.get(), cookie_rule.get()}; + std::set eval_filters{usr_rule, cookie_rule}; auto filter = std::make_shared( "3", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); From 1f2bbed8d4c31fbba86220c0ffc3205c2859161a Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 27 Oct 2024 20:37:18 +0000 Subject: [PATCH 7/8] Add first run check --- src/context.cpp | 2 +- src/context.hpp | 1 + src/module.hpp | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/context.cpp b/src/context.cpp index 5b45970f4..522346d8f 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -85,7 +85,7 @@ DDWAF_RET_CODE context::run(optional_ref persistent, ddwaf::timer deadline{std::chrono::microseconds(timeout)}; // If this is a new run but no rule care about those new params, let's skip the run - if (!store_.has_new_targets()) { + if (!is_first_run() && !store_.has_new_targets()) { return DDWAF_OK; } diff --git a/src/context.hpp b/src/context.hpp index ca1a2715c..5ad51b5aa 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -52,6 +52,7 @@ class context { std::vector eval_rules(const exclusion::context_policy &policy, ddwaf::timer &deadline); protected: + bool is_first_run() const { return collection_cache_.empty(); } bool check_new_rule_targets() const { // NOLINTNEXTLINE(readability-use-anyofallof) diff --git a/src/module.hpp b/src/module.hpp index c23cc0443..9cd422416 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -23,6 +23,8 @@ struct collection_cache { struct module_cache { memory::unordered_map collections; memory::vector rules; + + [[nodiscard]] bool empty() const { return rules.empty(); } }; class collection_module { From f1edfe2d7dfd6646d94875c5d9b61c1894090081 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 27 Oct 2024 21:10:05 +0000 Subject: [PATCH 8/8] Initialise module cache once --- src/context.hpp | 2 ++ src/module.cpp | 5 ----- src/module.hpp | 8 +++++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/context.hpp b/src/context.hpp index 5ad51b5aa..347b03dd3 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -16,6 +16,7 @@ #include "exclusion/common.hpp" #include "exclusion/input_filter.hpp" #include "exclusion/rule_filter.hpp" +#include "module.hpp" #include "obfuscator.hpp" #include "rule.hpp" #include "ruleset.hpp" @@ -33,6 +34,7 @@ class context { { rule_filter_cache_.reserve(ruleset_->rule_filters.size()); input_filter_cache_.reserve(ruleset_->input_filters.size()); + ruleset_->rules.init_cache(collection_cache_); } context(const context &) = delete; diff --git a/src/module.cpp b/src/module.cpp index 815a9b70f..ef1177020 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -84,11 +84,6 @@ void collection_module::eval(std::vector &events, object_store &store, mo const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) const { - if (cache.rules.size() != rules_.size()) { - cache.rules.resize(rules_.size()); - cache.collections.reserve(collections_.size()); - } - for (const auto &collection : collections_) { DDWAF_DEBUG("Evaluating collection: {}", collection.name); auto &collection_cache = cache.collections[collection.name]; diff --git a/src/module.hpp b/src/module.hpp index 9cd422416..72b2c3009 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -24,7 +24,7 @@ struct module_cache { memory::unordered_map collections; memory::vector rules; - [[nodiscard]] bool empty() const { return rules.empty(); } + [[nodiscard]] bool empty() const { return collections.empty(); } }; class collection_module { @@ -56,6 +56,12 @@ class collection_module { ddwaf::rule *operator[](std::size_t index) { return rules_.at(index); } + void init_cache(module_cache &cache) const + { + cache.rules.resize(rules_.size()); + cache.collections.reserve(collections_.size()); + } + protected: struct rule_collection { std::string_view name;