From fbee78fccdecd7e8b9d4d6d5b8480e1868cb26da Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:15:49 +0000 Subject: [PATCH 01/32] Rename rule to core_rule --- src/builder/processor_builder.cpp | 2 +- src/collection.cpp | 8 +- src/collection.hpp | 6 +- src/event.cpp | 4 +- src/event.hpp | 4 +- src/exclusion/common.hpp | 16 +- src/exclusion/input_filter.cpp | 2 +- src/exclusion/input_filter.hpp | 8 +- src/exclusion/rule_filter.cpp | 2 +- src/exclusion/rule_filter.hpp | 9 +- src/log.hpp | 17 +- src/parameter.hpp | 3 +- src/parser/actions_parser.cpp | 1 + src/parser/parser.hpp | 2 +- src/parser/parser_v1.cpp | 2 +- src/parser/rule_parser.cpp | 4 +- src/parser/specification.hpp | 2 +- src/rule.hpp | 49 +++- src/ruleset.hpp | 10 +- src/ruleset_builder.cpp | 18 +- src/ruleset_builder.hpp | 4 +- src/sha256.cpp | 42 +-- tests/common/base_utils.hpp | 36 +-- tests/common/gtest_utils.hpp | 42 +-- tests/unit/collection_test.cpp | 46 ++-- tests/unit/context_test.cpp | 271 +++++++++---------- tests/unit/event_serializer_test.cpp | 18 +- tests/unit/exclusion/input_filter_test.cpp | 66 ++--- tests/unit/exclusion/rule_filter_test.cpp | 27 +- tests/unit/mkmap_test.cpp | 14 +- tests/unit/parser_transformers_test.cpp | 4 +- tests/unit/rule_test.cpp | 47 ++-- tests/unit/ruleset_test.cpp | 96 +++---- tests/unit/transformer/manager_test.cpp | 22 +- tests/unit/transformer/transformer_utils.hpp | 62 ++--- tests/unit/utils_test.cpp | 8 +- validator/assert.hpp | 14 +- 37 files changed, 485 insertions(+), 503 deletions(-) diff --git a/src/builder/processor_builder.cpp b/src/builder/processor_builder.cpp index c5f61a026..de6e71ae4 100644 --- a/src/builder/processor_builder.cpp +++ b/src/builder/processor_builder.cpp @@ -86,7 +86,7 @@ concept has_build_with_scanners = requires(typed_processor_builder b, Spec spec, Scanners scanners) { { b.build(spec, scanners) - } -> std::same_as>; + } -> std::same_as>; }; template diff --git a/src/collection.cpp b/src/collection.cpp index 5cc3fc1e2..6b89fc4c7 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -24,8 +24,8 @@ namespace ddwaf { -std::optional match_rule(rule *rule, const object_store &store, - memory::unordered_map &cache, +std::optional match_rule(core_rule *rule, const object_store &store, + memory::unordered_map &cache, const exclusion::context_policy &policy, const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) @@ -62,11 +62,11 @@ std::optional match_rule(rule *rule, const object_store &store, try { auto it = cache.find(rule); if (it == cache.end()) { - auto [new_it, res] = cache.emplace(rule, rule::cache_type{}); + auto [new_it, res] = cache.emplace(rule, core_rule::cache_type{}); it = new_it; } - rule::cache_type &rule_cache = it->second; + core_rule::cache_type &rule_cache = it->second; std::optional event; event = rule->match(store, rule_cache, exclusion.objects, dynamic_matchers, deadline); diff --git a/src/collection.hpp b/src/collection.hpp index 6f4e883ce..08044d656 100644 --- a/src/collection.hpp +++ b/src/collection.hpp @@ -27,7 +27,7 @@ enum class collection_type : uint8_t { none = 0, regular = 1, priority = 2 }; struct collection_cache { bool ephemeral{false}; collection_type result{collection_type::none}; - memory::unordered_map rule_cache; + memory::unordered_map rule_cache; }; template class base_collection { @@ -42,7 +42,7 @@ template class base_collection { 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 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, @@ -50,7 +50,7 @@ template class base_collection { ddwaf::timer &deadline) const; protected: - std::vector rules_{}; + std::vector rules_{}; }; class collection : public base_collection { diff --git a/src/event.cpp b/src/event.cpp index 2d6c64342..03d06fdcb 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -151,7 +151,7 @@ void add_action_to_tracker(action_tracker &actions, std::string_view id, action_ } } -void serialize_rule(const ddwaf::rule &rule, ddwaf_object &rule_map) +void serialize_rule(const core_rule &rule, ddwaf_object &rule_map) { ddwaf_object tmp; ddwaf_object tags_map; @@ -186,7 +186,7 @@ void serialize_empty_rule(ddwaf_object &rule_map) ddwaf_object_map_add(&rule_map, "tags", &tags_map); } -void serialize_and_consolidate_rule_actions(const ddwaf::rule &rule, ddwaf_object &rule_map, +void serialize_and_consolidate_rule_actions(const core_rule &rule, ddwaf_object &rule_map, std::string_view action_override, action_tracker &actions, ddwaf_object &stack_id) { const auto &rule_actions = rule.get_actions(); diff --git a/src/event.hpp b/src/event.hpp index 1d1d34445..325245ac0 100644 --- a/src/event.hpp +++ b/src/event.hpp @@ -15,10 +15,10 @@ namespace ddwaf { -class rule; +class core_rule; struct event { - const ddwaf::rule *rule{nullptr}; + const core_rule *rule{nullptr}; std::vector matches; bool ephemeral{false}; std::string_view action_override; diff --git a/src/exclusion/common.hpp b/src/exclusion/common.hpp index aa4437c52..05730a342 100644 --- a/src/exclusion/common.hpp +++ b/src/exclusion/common.hpp @@ -15,7 +15,7 @@ namespace ddwaf { -class rule; +class core_rule; namespace exclusion { @@ -69,19 +69,19 @@ struct rule_policy_ref { }; struct context_policy { - std::unordered_map persistent; - std::unordered_map ephemeral; + std::unordered_map persistent; + std::unordered_map ephemeral; [[nodiscard]] bool empty() const { return persistent.empty() && ephemeral.empty(); } [[nodiscard]] std::size_t size() const { return persistent.size() + ephemeral.size(); } - bool contains(const rule *key) const + bool contains(const core_rule *key) const { return persistent.contains(key) || ephemeral.contains(key); } - rule_policy_ref find(const rule *key) const + rule_policy_ref find(const core_rule *key) const { auto p_it = persistent.find(key); auto e_it = ephemeral.find(key); @@ -109,8 +109,8 @@ struct context_policy { {p_policy.objects, e_policy.objects}}; } - void add_rule_exclusion(const ddwaf::rule *rule, filter_mode mode, std::string_view action, - bool ephemeral_exclusion) + void add_rule_exclusion( + const core_rule *rule, filter_mode mode, std::string_view action, bool ephemeral_exclusion) { auto &rule_policy = ephemeral_exclusion ? ephemeral : persistent; @@ -122,7 +122,7 @@ struct context_policy { } } - void add_input_exclusion(const ddwaf::rule *rule, const object_set &objects) + void add_input_exclusion(const core_rule *rule, const object_set &objects) { if (!objects.persistent.empty()) { auto &rule_policy = persistent[rule]; diff --git a/src/exclusion/input_filter.cpp b/src/exclusion/input_filter.cpp index dfcdc7330..2e3305dae 100644 --- a/src/exclusion/input_filter.cpp +++ b/src/exclusion/input_filter.cpp @@ -26,7 +26,7 @@ namespace ddwaf::exclusion { using excluded_set = input_filter::excluded_set; input_filter::input_filter(std::string id, std::shared_ptr expr, - std::set rule_targets, std::shared_ptr filter) + std::set rule_targets, std::shared_ptr filter) : id_(std::move(id)), expr_(std::move(expr)), rule_targets_(std::move(rule_targets)), filter_(std::move(filter)) { diff --git a/src/exclusion/input_filter.hpp b/src/exclusion/input_filter.hpp index b4869fe9e..3d604ee6d 100644 --- a/src/exclusion/input_filter.hpp +++ b/src/exclusion/input_filter.hpp @@ -20,7 +20,7 @@ namespace ddwaf::exclusion { class input_filter { public: struct excluded_set { - const std::set &rules; + const std::set &rules; object_set objects; }; @@ -29,8 +29,8 @@ class input_filter { object_filter::cache_type object_filter_cache; }; - input_filter(std::string id, std::shared_ptr expr, std::set rule_targets, - std::shared_ptr filter); + input_filter(std::string id, std::shared_ptr expr, + std::set rule_targets, std::shared_ptr filter); input_filter(const input_filter &) = delete; input_filter &operator=(const input_filter &) = delete; input_filter(input_filter &&) = default; @@ -52,7 +52,7 @@ class input_filter { protected: std::string id_; std::shared_ptr expr_; - const std::set rule_targets_; + const std::set rule_targets_; std::shared_ptr filter_; }; diff --git a/src/exclusion/rule_filter.cpp b/src/exclusion/rule_filter.cpp index b117a1a83..0c1dae165 100644 --- a/src/exclusion/rule_filter.cpp +++ b/src/exclusion/rule_filter.cpp @@ -25,7 +25,7 @@ namespace ddwaf::exclusion { using excluded_set = rule_filter::excluded_set; rule_filter::rule_filter(std::string id, std::shared_ptr expr, - std::set rule_targets, filter_mode mode, std::string action) + std::set rule_targets, filter_mode mode, std::string action) : id_(std::move(id)), expr_(std::move(expr)), mode_(mode), action_(std::move(action)) { if (!expr_) { diff --git a/src/exclusion/rule_filter.hpp b/src/exclusion/rule_filter.hpp index db9be61b7..5eacf53b6 100644 --- a/src/exclusion/rule_filter.hpp +++ b/src/exclusion/rule_filter.hpp @@ -20,7 +20,7 @@ namespace ddwaf::exclusion { class rule_filter { public: struct excluded_set { - const std::unordered_set &rules; + const std::unordered_set &rules; bool ephemeral{false}; filter_mode mode{filter_mode::none}; std::string_view action; @@ -28,8 +28,9 @@ class rule_filter { using cache_type = expression::cache_type; - rule_filter(std::string id, std::shared_ptr expr, std::set rule_targets, - filter_mode mode = filter_mode::bypass, std::string action = {}); + rule_filter(std::string id, std::shared_ptr expr, + std::set rule_targets, filter_mode mode = filter_mode::bypass, + std::string action = {}); rule_filter(const rule_filter &) = delete; rule_filter &operator=(const rule_filter &) = delete; rule_filter(rule_filter &&) = default; @@ -52,7 +53,7 @@ class rule_filter { protected: std::string id_; std::shared_ptr expr_; - std::unordered_set rule_targets_; + std::unordered_set rule_targets_; filter_mode mode_; std::string action_{}; }; diff --git a/src/log.hpp b/src/log.hpp index 2543c8f33..133a9e586 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -49,17 +49,16 @@ constexpr const char *base_name(const char *path) } # define DDWAF_LOG_HELPER(level, function, file, line, fmt_str, ...) \ - { \ - if (ddwaf::logger::valid(level)) { \ - constexpr const char *filename = base_name(file); \ - auto message = ddwaf::fmt::format(fmt_str, ##__VA_ARGS__); \ - ddwaf::logger::log( \ - level, function, filename, line, message.c_str(), message.size()); \ - } \ - } + { \ + if (ddwaf::logger::valid(level)) { \ + constexpr const char *filename = base_name(file); \ + auto message = ddwaf::fmt::format(fmt_str, ##__VA_ARGS__); \ + ddwaf::logger::log(level, function, filename, line, message.c_str(), message.size()); \ + } \ + } # define DDWAF_LOG(level, fmt, ...) \ - DDWAF_LOG_HELPER(level, __func__, __FILE__, __LINE__, fmt, ##__VA_ARGS__) + DDWAF_LOG_HELPER(level, __func__, __FILE__, __LINE__, fmt, ##__VA_ARGS__) #endif #if DDWAF_COMPILE_LOG_LEVEL <= DDWAF_COMPILE_LOG_TRACE diff --git a/src/parameter.hpp b/src/parameter.hpp index 0fb9fb3c7..cd02bb1a1 100644 --- a/src/parameter.hpp +++ b/src/parameter.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -25,7 +26,7 @@ class parameter : public ddwaf_object { using string_set = std::unordered_set; parameter() = default; - // NOLINTNEXTLINE(google-explicit-constructor) + // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) parameter(const ddwaf_object &arg) : _ddwaf_object() { *((ddwaf_object *)this) = arg; } parameter(const parameter &) = default; diff --git a/src/parser/actions_parser.cpp b/src/parser/actions_parser.cpp index 9ef773d7c..ad111bc89 100644 --- a/src/parser/actions_parser.cpp +++ b/src/parser/actions_parser.cpp @@ -36,6 +36,7 @@ void validate_and_add_redirect( { auto it = parameters.find("location"); if (it == parameters.end() || it->second.empty()) { + // TODO add a log message builder.alias_default_action_to("block", id); return; } diff --git a/src/parser/parser.hpp b/src/parser/parser.hpp index 3e3bd5a08..ac251362e 100644 --- a/src/parser/parser.hpp +++ b/src/parser/parser.hpp @@ -32,7 +32,7 @@ namespace v2 { rule_spec_container parse_rules(parameter::vector &rule_array, base_section_info &info, std::unordered_map &rule_data_ids, const object_limits &limits, - rule::source_type source = rule::source_type::base); + core_rule::source_type source = core_rule::source_type::base); matcher_container parse_data(parameter::vector &data_array, std::unordered_map &data_ids_to_type, base_section_info &info); diff --git a/src/parser/parser_v1.cpp b/src/parser/parser_v1.cpp index edb141d32..a7b85f2da 100644 --- a/src/parser/parser_v1.cpp +++ b/src/parser/parser_v1.cpp @@ -164,7 +164,7 @@ void parseRule(parameter::map &rule, base_section_info &info, throw ddwaf::parsing_error("missing key 'type'"); } - auto rule_ptr = std::make_shared( + auto rule_ptr = std::make_shared( std::string(id), at(rule, "name"), std::move(tags), std::move(expression)); rule_ids.emplace(rule_ptr->get_id()); diff --git a/src/parser/rule_parser.cpp b/src/parser/rule_parser.cpp index 3d2eadbe7..44ad6120f 100644 --- a/src/parser/rule_parser.cpp +++ b/src/parser/rule_parser.cpp @@ -28,7 +28,7 @@ namespace { rule_spec parse_rule(parameter::map &rule, std::unordered_map &rule_data_ids, const object_limits &limits, - rule::source_type source, address_container &addresses) + core_rule::source_type source, address_container &addresses) { std::vector rule_transformers; auto data_source = ddwaf::data_source::values; @@ -68,7 +68,7 @@ rule_spec parse_rule(parameter::map &rule, rule_spec_container parse_rules(parameter::vector &rule_array, base_section_info &info, std::unordered_map &rule_data_ids, const object_limits &limits, - rule::source_type source) + core_rule::source_type source) { rule_spec_container rules; for (unsigned i = 0; i < rule_array.size(); ++i) { diff --git a/src/parser/specification.hpp b/src/parser/specification.hpp index 750ba2c1e..d741b4fc5 100644 --- a/src/parser/specification.hpp +++ b/src/parser/specification.hpp @@ -21,7 +21,7 @@ namespace ddwaf::parser { struct rule_spec { bool enabled; - rule::source_type source; + core_rule::source_type source; std::string name; std::unordered_map tags; std::shared_ptr expr; diff --git a/src/rule.hpp b/src/rule.hpp index a449e56f1..712adc87a 100644 --- a/src/rule.hpp +++ b/src/rule.hpp @@ -22,30 +22,39 @@ namespace ddwaf { -class rule { +// A core rule constitutes the most important type of entity within the +// evaluation process. These rules are "request-bound", i.e. they are used to +// specifically analyse request data, as opposed to other types of rules such +// as threshold rules which analyse data across requests. +class core_rule { public: enum class source_type : uint8_t { base = 1, user = 2 }; using cache_type = expression::cache_type; - rule(std::string id, std::string name, std::unordered_map tags, + core_rule(std::string id, std::string name, std::unordered_map tags, std::shared_ptr expr, std::vector actions = {}, bool enabled = true, source_type source = source_type::base) : enabled_(enabled), source_(source), id_(std::move(id)), name_(std::move(name)), - tags_(std::move(tags)), expr_(std::move(expr)), actions_(std::move(actions)) + tags_(std::move(tags)), actions_(std::move(actions)), expr_(std::move(expr)) { if (!expr_) { throw std::invalid_argument("rule constructed with null expression"); } + + // If the tag is not present, the default is `waf` + mod_ = get_tag_or("module", "waf"); + // Type is guaranteed to be present + type_ = get_tag("type"); } - rule(const rule &) = delete; - rule &operator=(const rule &) = delete; + core_rule(const core_rule &) = delete; + core_rule &operator=(const core_rule &) = delete; - rule(rule &&rhs) noexcept = default; - rule &operator=(rule &&rhs) = default; + core_rule(core_rule &&rhs) noexcept = default; + core_rule &operator=(core_rule &&rhs) = default; - virtual ~rule() = default; + virtual ~core_rule() = default; virtual std::optional match(const object_store &store, cache_type &cache, const exclusion::object_set_ref &objects_excluded, @@ -68,9 +77,12 @@ class rule { [[nodiscard]] bool is_enabled() const { return enabled_; } void toggle(bool value) { enabled_ = value; } - source_type get_source() const { return source_; } - const std::string &get_id() const { return id_; } - const std::string &get_name() const { return name_; } + [[nodiscard]] source_type get_source() const { return source_; } + + std::string_view get_id() const { return id_; } + std::string_view get_name() const { return name_; } + std::string_view get_type() const { return type_; } + std::string_view get_module() const { return mod_; } std::string_view get_tag(const std::string &tag) const { @@ -98,24 +110,33 @@ class rule { } } + [[nodiscard]] bool has_actions() const { return !actions_.empty(); } const std::vector &get_actions() const { return actions_; } + void set_actions(std::vector new_actions) { actions_ = std::move(new_actions); } void get_addresses(std::unordered_map &addresses) const { return expr_->get_addresses(addresses); } - void set_actions(std::vector new_actions) { actions_ = std::move(new_actions); } - protected: + // General metadata bool enabled_{true}; source_type source_; std::string id_; std::string name_; std::unordered_map tags_; + std::vector actions_; + + // Frequently accessed tags + std::string type_; + std::string mod_; + + // Tags provided through rules_override std::unordered_map ancillary_tags_; + + // Evaluable expression encompassing all the rule's conditions std::shared_ptr expr_; - std::vector actions_; }; } // namespace ddwaf diff --git a/src/ruleset.hpp b/src/ruleset.hpp index 19e9edd0e..143c97392 100644 --- a/src/ruleset.hpp +++ b/src/ruleset.hpp @@ -22,7 +22,7 @@ namespace ddwaf { struct ruleset { - void insert_rule(const std::shared_ptr &rule) + void insert_rule(const std::shared_ptr &rule) { rules.emplace_back(rule); std::string_view type = rule->get_tag("type"); @@ -31,13 +31,13 @@ struct ruleset { 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) { + if (rule->get_source() == core_rule::source_type::user) { user_collections[collection].insert(rule); } else { base_collections[collection].insert(rule); } } else { - if (rule->get_source() == rule::source_type::user) { + if (rule->get_source() == core_rule::source_type::user) { user_priority_collections[collection].insert(rule); } else { base_priority_collections[collection].insert(rule); @@ -46,7 +46,7 @@ struct ruleset { rule->get_addresses(rule_addresses); } - void insert_rules(const std::vector> &rules_) + void insert_rules(const std::vector> &rules_) { for (const auto &rule : rules_) { insert_rule(rule); } } @@ -164,7 +164,7 @@ struct ruleset { std::unordered_map> rule_filters; std::unordered_map> input_filters; - std::vector> rules; + std::vector> rules; std::unordered_map> rule_matchers; std::unordered_map> exclusion_matchers; diff --git a/src/ruleset_builder.cpp b/src/ruleset_builder.cpp index 4ebe3e1a8..a17e70318 100644 --- a/src/ruleset_builder.cpp +++ b/src/ruleset_builder.cpp @@ -47,10 +47,10 @@ constexpr ruleset_builder::change_state operator&( namespace { -std::set references_to_rules( - const std::vector &references, const indexer &rules) +std::set references_to_rules( + const std::vector &references, const indexer &rules) { - std::set rule_refs; + std::set rule_refs; if (!references.empty()) { for (const auto &ref : references) { if (ref.type == parser::reference_type::id) { @@ -95,7 +95,7 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules // Initially, new rules are generated from their spec for (const auto &[id, spec] : base_rules_) { - auto rule_ptr = std::make_shared( + auto rule_ptr = std::make_shared( id, spec.name, spec.tags, spec.expr, spec.actions, spec.enabled, spec.source); final_base_rules_.emplace(rule_ptr); } @@ -150,7 +150,7 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules // Initially, new rules are generated from their spec for (const auto &[id, spec] : user_rules_) { - auto rule_ptr = std::make_shared( + auto rule_ptr = std::make_shared( id, spec.name, spec.tags, spec.expr, spec.actions, spec.enabled, spec.source); if (!rule_ptr->is_enabled()) { // Skip disabled rules @@ -202,7 +202,7 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules } } - auto rs = std::make_shared(); + auto rs = std::make_shared(); rs->insert_rules(final_base_rules_.items()); rs->insert_rules(final_user_rules_.items()); rs->insert_filters(rule_filters_); @@ -220,7 +220,7 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules // again that there are rules available. if (rs->rules.empty()) { DDWAF_WARN("No valid rules found"); - throw ddwaf::parsing_error("no valid or enabled rules found"); + throw parsing_error("no valid or enabled rules found"); } return rs; @@ -292,7 +292,7 @@ ruleset_builder::change_state ruleset_builder::load(parameter::map &root, base_r decltype(rule_data_ids_) rule_data_ids; auto new_user_rules = parser::v2::parse_rules( - rules, section, rule_data_ids, limits_, rule::source_type::user); + rules, section, rule_data_ids, limits_, core_rule::source_type::user); user_rules_ = std::move(new_user_rules); } else { DDWAF_DEBUG("Clearing all custom rules"); @@ -309,7 +309,7 @@ ruleset_builder::change_state ruleset_builder::load(parameter::map &root, base_r // If we haven't received rules and our base ruleset is empty, the // WAF can't proceed. DDWAF_WARN("No valid rules found"); - throw ddwaf::parsing_error("no valid rules found"); + throw parsing_error("no valid rules found"); } it = root.find("rules_data"); diff --git a/src/ruleset_builder.hpp b/src/ruleset_builder.hpp index 453e752f5..4d163492f 100644 --- a/src/ruleset_builder.hpp +++ b/src/ruleset_builder.hpp @@ -96,8 +96,8 @@ class ruleset_builder { // These are the contents of the latest generated ruleset // Rules - indexer final_base_rules_; - indexer final_user_rules_; + indexer final_base_rules_; + indexer final_user_rules_; // Filters std::unordered_map> rule_filters_; diff --git a/src/sha256.cpp b/src/sha256.cpp index 128cba194..63e0e1346 100644 --- a/src/sha256.cpp +++ b/src/sha256.cpp @@ -36,15 +36,15 @@ namespace { * Idea behind separate cases for pre-defined lengths is to let the * compiler decide if it's appropriate to unroll small loops. */ -#define ROTATE(a, n) (((a) << (n)) | (((a) & 0xffffffff) >> (32 - (n)))) +#define ROTATE(a, n) (((a) << (n)) | (((a)&0xffffffff) >> (32 - (n)))) #define CHAR_TO_UINT32(c, l) \ - { \ - (l) = ((static_cast(*((c)++))) << 24); \ - (l) |= ((static_cast(*((c)++))) << 16); \ - (l) |= ((static_cast(*((c)++))) << 8); \ - (l) |= ((static_cast(*((c)++)))); \ - } + { \ + (l) = ((static_cast(*((c)++))) << 24); \ + (l) |= ((static_cast(*((c)++))) << 16); \ + (l) |= ((static_cast(*((c)++))) << 8); \ + (l) |= ((static_cast(*((c)++)))); \ + } #define UINT8_TO_HEX_CHAR(u) static_cast((u) < 10 ? (u) + '0' : (u)-10 + 'a') @@ -61,22 +61,22 @@ namespace { #define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define ROUND_00_15(i, a, b, c, d, e, f, g, h) \ - { \ - T1 += (h) + Sigma1(e) + Ch(e, f, g) + K256[i]; \ - (h) = Sigma0(a) + Maj(a, b, c); \ - (d) += T1; \ - (h) += T1; \ - } + { \ + T1 += (h) + Sigma1(e) + Ch(e, f, g) + K256[i]; \ + (h) = Sigma0(a) + Maj(a, b, c); \ + (d) += T1; \ + (h) += T1; \ + } #define ROUND_16_63(i, a, b, c, d, e, f, g, h, X) \ - { \ - s0 = (X)[((i) + 1) & 0x0f]; \ - s0 = sigma0(s0); \ - s1 = (X)[((i) + 14) & 0x0f]; \ - s1 = sigma1(s1); \ - T1 = (X)[(i) & 0x0f] += s0 + s1 + (X)[((i) + 9) & 0x0f]; \ - ROUND_00_15(i, a, b, c, d, e, f, g, h); \ - } + { \ + s0 = (X)[((i) + 1) & 0x0f]; \ + s0 = sigma0(s0); \ + s1 = (X)[((i) + 14) & 0x0f]; \ + s1 = sigma1(s1); \ + T1 = (X)[(i)&0x0f] += s0 + s1 + (X)[((i) + 9) & 0x0f]; \ + ROUND_00_15(i, a, b, c, d, e, f, g, h); \ + } // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/tests/common/base_utils.hpp b/tests/common/base_utils.hpp index 4dbaaa8bb..4ad0c6444 100644 --- a/tests/common/base_utils.hpp +++ b/tests/common/base_utils.hpp @@ -15,29 +15,29 @@ #define SHORT_TIME 1 #define DDWAF_OBJECT_INVALID \ - { \ - NULL, 0, {NULL}, 0, DDWAF_OBJ_INVALID \ - } + { \ + NULL, 0, {NULL}, 0, DDWAF_OBJ_INVALID \ + } #define DDWAF_OBJECT_MAP \ - { \ - NULL, 0, {NULL}, 0, DDWAF_OBJ_MAP \ - } + { \ + NULL, 0, {NULL}, 0, DDWAF_OBJ_MAP \ + } #define DDWAF_OBJECT_ARRAY \ - { \ - NULL, 0, {NULL}, 0, DDWAF_OBJ_ARRAY \ - } + { \ + NULL, 0, {NULL}, 0, DDWAF_OBJ_ARRAY \ + } #define DDWAF_OBJECT_SIGNED_FORCE(value) \ - { \ - NULL, 0, {(const char *)value}, 0, DDWAF_OBJ_SIGNED \ - } + { \ + NULL, 0, {(const char *)value}, 0, DDWAF_OBJ_SIGNED \ + } #define DDWAF_OBJECT_UNSIGNED_FORCE(value) \ - { \ - NULL, 0, {(const char *)value}, 0, DDWAF_OBJ_UNSIGNED \ - } + { \ + NULL, 0, {(const char *)value}, 0, DDWAF_OBJ_UNSIGNED \ + } #define DDWAF_OBJECT_STRING_PTR(string, length) \ - { \ - NULL, 0, {string}, length, DDWAF_OBJ_STRING \ - } + { \ + NULL, 0, {string}, length, DDWAF_OBJ_STRING \ + } namespace ddwaf::test { diff --git a/tests/common/gtest_utils.hpp b/tests/common/gtest_utils.hpp index b0705813a..f78720ac2 100644 --- a/tests/common/gtest_utils.hpp +++ b/tests/common/gtest_utils.hpp @@ -170,31 +170,31 @@ std::list from_matches( // NOLINTBEGIN(cppcoreguidelines-macro-usage) #define EXPECT_EVENTS(result, ...) \ - { \ - auto data = ddwaf::test::object_to_json(result.events); \ - EXPECT_TRUE(ValidateSchema(data)); \ - YAML::Node doc = YAML::Load(data.c_str()); \ - auto events = doc.as>(); \ - EXPECT_THAT(events, WithEvents({__VA_ARGS__})); \ - } + { \ + auto data = ddwaf::test::object_to_json(result.events); \ + EXPECT_TRUE(ValidateSchema(data)); \ + YAML::Node doc = YAML::Load(data.c_str()); \ + auto events = doc.as>(); \ + EXPECT_THAT(events, WithEvents({__VA_ARGS__})); \ + } #define EXPECT_MATCHES(matches, ...) EXPECT_THAT(from_matches(matches), WithMatches({__VA_ARGS__})); #define EXPECT_SCHEMA_EQ(obtained, expected) \ - { \ - auto obtained_doc = test::object_to_rapidjson(obtained); \ - EXPECT_TRUE(ValidateSchemaSchema(obtained_doc)); \ - rapidjson::Document expected_doc; \ - expected_doc.Parse(expected); \ - EXPECT_FALSE(expected_doc.HasParseError()); \ - EXPECT_TRUE(json_equals(obtained_doc, expected_doc)) << test::object_to_json(obtained); \ - } + { \ + auto obtained_doc = test::object_to_rapidjson(obtained); \ + EXPECT_TRUE(ValidateSchemaSchema(obtained_doc)); \ + rapidjson::Document expected_doc; \ + expected_doc.Parse(expected); \ + EXPECT_FALSE(expected_doc.HasParseError()); \ + EXPECT_TRUE(json_equals(obtained_doc, expected_doc)) << test::object_to_json(obtained); \ + } #define EXPECT_ACTIONS(result, ...) \ - { \ - auto data = ddwaf::test::object_to_json(result.actions); \ - YAML::Node doc = YAML::Load(data.c_str()); \ - auto obtained = doc.as(); \ - EXPECT_THAT(obtained, WithActions(__VA_ARGS__)); \ - } + { \ + auto data = ddwaf::test::object_to_json(result.actions); \ + YAML::Node doc = YAML::Load(data.c_str()); \ + auto obtained = doc.as(); \ + EXPECT_THAT(obtained, WithActions(__VA_ARGS__)); \ + } // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/tests/unit/collection_test.cpp b/tests/unit/collection_test.cpp index c41fc2f6d..9d3192f82 100644 --- a/tests/unit/collection_test.cpp +++ b/tests/unit/collection_test.cpp @@ -32,7 +32,7 @@ TYPED_TEST(TestCollection, SingleRuleMatch) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); TypeParam rule_collection; rule_collection.insert(rule); @@ -72,7 +72,7 @@ TYPED_TEST(TestCollection, SingleRuleMatch) // Validate that once there's a match for a collection, a second match isn't possible TYPED_TEST(TestCollection, MultipleRuleCachedMatch) { - std::vector> rules; + std::vector> rules; TypeParam rule_collection; { test::expression_builder builder(1); @@ -84,7 +84,7 @@ TYPED_TEST(TestCollection, MultipleRuleCachedMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); rules.emplace_back(rule); rule_collection.insert(rule); @@ -100,7 +100,7 @@ TYPED_TEST(TestCollection, MultipleRuleCachedMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); + auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); rules.emplace_back(rule); rule_collection.insert(rule); @@ -141,7 +141,7 @@ TYPED_TEST(TestCollection, MultipleRuleCachedMatch) // Validate that after a failed match, the collection can still produce a match TYPED_TEST(TestCollection, MultipleRuleFailAndMatch) { - std::vector> rules; + std::vector> rules; TypeParam rule_collection; { test::expression_builder builder(1); @@ -153,7 +153,7 @@ TYPED_TEST(TestCollection, MultipleRuleFailAndMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); rules.emplace_back(rule); rule_collection.insert(rule); @@ -169,7 +169,7 @@ TYPED_TEST(TestCollection, MultipleRuleFailAndMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); + auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); rules.emplace_back(rule); rule_collection.insert(rule); @@ -223,7 +223,7 @@ TYPED_TEST(TestCollection, SingleRuleMultipleCalls) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); TypeParam rule_collection; rule_collection.insert(rule); @@ -265,7 +265,7 @@ TYPED_TEST(TestCollection, SingleRuleMultipleCalls) // Validate that a match in a priority collection prevents further regular matches TEST(TestPriorityCollection, NoRegularMatchAfterPriorityMatch) { - std::vector> rules; + std::vector> rules; collection regular; priority_collection priority; { @@ -278,7 +278,7 @@ TEST(TestPriorityCollection, NoRegularMatchAfterPriorityMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); rules.emplace_back(rule); regular.insert(rule); @@ -294,7 +294,7 @@ TEST(TestPriorityCollection, NoRegularMatchAfterPriorityMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); rules.emplace_back(rule); @@ -338,7 +338,7 @@ TEST(TestPriorityCollection, NoRegularMatchAfterPriorityMatch) // priority collection TEST(TestPriorityCollection, PriorityMatchAfterRegularMatch) { - std::vector> rules; + std::vector> rules; collection regular; priority_collection priority; { @@ -351,7 +351,7 @@ TEST(TestPriorityCollection, PriorityMatchAfterRegularMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); rules.emplace_back(rule); regular.insert(rule); @@ -367,7 +367,7 @@ TEST(TestPriorityCollection, PriorityMatchAfterRegularMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); rules.emplace_back(rule); @@ -412,7 +412,7 @@ TEST(TestPriorityCollection, PriorityMatchAfterRegularMatch) // Validate that a match in a priority collection prevents another match TEST(TestPriorityCollection, NoPriorityMatchAfterPriorityMatch) { - std::vector> rules; + std::vector> rules; priority_collection priority; { test::expression_builder builder(1); @@ -424,7 +424,7 @@ TEST(TestPriorityCollection, NoPriorityMatchAfterPriorityMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); rules.emplace_back(rule); @@ -441,7 +441,7 @@ TEST(TestPriorityCollection, NoPriorityMatchAfterPriorityMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); rules.emplace_back(rule); @@ -485,7 +485,7 @@ TEST(TestPriorityCollection, NoPriorityMatchAfterPriorityMatch) // Validate that an ephemeral match in a priority collection doesn't another match TEST(TestPriorityCollection, NoPriorityMatchAfterEphemeralPriorityMatch) { - std::vector> rules; + std::vector> rules; priority_collection priority; { test::expression_builder builder(1); @@ -497,7 +497,7 @@ TEST(TestPriorityCollection, NoPriorityMatchAfterEphemeralPriorityMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); rules.emplace_back(rule); @@ -514,7 +514,7 @@ TEST(TestPriorityCollection, NoPriorityMatchAfterEphemeralPriorityMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); rules.emplace_back(rule); @@ -563,7 +563,7 @@ TEST(TestPriorityCollection, NoPriorityMatchAfterEphemeralPriorityMatch) // within the same evaluation TEST(TestPriorityCollection, EphemeralPriorityMatchNoOtherMatches) { - std::vector> rules; + std::vector> rules; priority_collection priority; { test::expression_builder builder(1); @@ -575,7 +575,7 @@ TEST(TestPriorityCollection, EphemeralPriorityMatchNoOtherMatches) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); rules.emplace_back(rule); @@ -592,7 +592,7 @@ TEST(TestPriorityCollection, EphemeralPriorityMatchNoOtherMatches) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); rules.emplace_back(rule); diff --git a/tests/unit/context_test.cpp b/tests/unit/context_test.cpp index 2d893a8df..1fb2f9930 100644 --- a/tests/unit/context_test.cpp +++ b/tests/unit/context_test.cpp @@ -43,20 +43,20 @@ namespace { namespace mock { -class rule : public ddwaf::rule { +class rule : public core_rule { public: using ptr = std::shared_ptr; rule(std::string id, std::string name, std::unordered_map tags, std::shared_ptr expr, std::vector actions = {}, bool enabled = true, source_type source = source_type::base) - : ddwaf::rule(std::move(id), std::move(name), std::move(tags), std::move(expr), + : core_rule(std::move(id), std::move(name), std::move(tags), std::move(expr), std::move(actions), enabled, source) {} ~rule() override = default; MOCK_METHOD(std::optional, match, - (const object_store &, rule::cache_type &, (const exclusion::object_set_ref &objects), + (const object_store &, core_rule::cache_type &, (const exclusion::object_set_ref &objects), (const std::unordered_map> &), ddwaf::timer &), (const override)); @@ -67,7 +67,7 @@ class rule_filter : public ddwaf::exclusion::rule_filter { using ptr = std::shared_ptr; rule_filter(std::string id, std::shared_ptr expr, - std::set rule_targets, filter_mode mode = filter_mode::bypass) + std::set rule_targets, filter_mode mode = filter_mode::bypass) : exclusion::rule_filter(std::move(id), std::move(expr), std::move(rule_targets), mode) {} ~rule_filter() override = default; @@ -84,7 +84,7 @@ class input_filter : public ddwaf::exclusion::input_filter { using ptr = std::shared_ptr; input_filter(std::string id, std::shared_ptr expr, - std::set rule_targets, std::shared_ptr filter) + std::set rule_targets, std::shared_ptr filter) : exclusion::input_filter( std::move(id), std::move(expr), std::move(rule_targets), std::move(filter)) {} @@ -214,7 +214,7 @@ TEST(TestContext, MatchTimeout) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto ruleset = test::get_default_ruleset(); ruleset->insert_rule(rule); @@ -241,7 +241,7 @@ TEST(TestContext, NoMatch) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto ruleset = test::get_default_ruleset(); ruleset->insert_rule(rule); @@ -269,7 +269,7 @@ TEST(TestContext, Match) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto ruleset = test::get_default_ruleset(); ruleset->insert_rule(rule); @@ -300,7 +300,7 @@ TEST(TestContext, MatchMultipleRulesInCollectionSingleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -315,7 +315,7 @@ TEST(TestContext, MatchMultipleRulesInCollectionSingleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); + auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -334,8 +334,8 @@ TEST(TestContext, MatchMultipleRulesInCollectionSingleRun) EXPECT_EQ(events.size(), 1); auto &event = events[0]; - EXPECT_STREQ(event.rule->get_id().c_str(), "id1"); - EXPECT_STREQ(event.rule->get_name().c_str(), "name1"); + EXPECT_STREQ(event.rule->get_id().data(), "id1"); + EXPECT_STREQ(event.rule->get_name().data(), "name1"); EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); EXPECT_STREQ(event.rule->get_tag("category").data(), "category1"); std::vector expected_actions{}; @@ -364,7 +364,7 @@ TEST(TestContext, MatchMultipleRulesWithPrioritySingleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -379,7 +379,7 @@ TEST(TestContext, MatchMultipleRulesWithPrioritySingleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"block"}); ruleset->insert_rule(rule); @@ -400,7 +400,7 @@ TEST(TestContext, MatchMultipleRulesWithPrioritySingleRun) EXPECT_EQ(events.size(), 1); auto event = events[0]; - EXPECT_STREQ(event.rule->get_id().c_str(), "id2"); + EXPECT_STREQ(event.rule->get_id().data(), "id2"); EXPECT_EQ(event.rule->get_actions().size(), 1); EXPECT_STREQ(event.rule->get_actions()[0].data(), "block"); } @@ -420,7 +420,7 @@ TEST(TestContext, MatchMultipleRulesWithPrioritySingleRun) EXPECT_EQ(events.size(), 1); auto event = events[0]; - EXPECT_STREQ(event.rule->get_id().c_str(), "id2"); + EXPECT_STREQ(event.rule->get_id().data(), "id2"); EXPECT_EQ(event.rule->get_actions().size(), 1); EXPECT_STREQ(event.rule->get_actions()[0].data(), "block"); } @@ -439,7 +439,7 @@ TEST(TestContext, MatchMultipleRulesInCollectionDoubleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -454,7 +454,7 @@ TEST(TestContext, MatchMultipleRulesInCollectionDoubleRun) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); + auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -473,8 +473,8 @@ TEST(TestContext, MatchMultipleRulesInCollectionDoubleRun) EXPECT_EQ(events.size(), 1); auto &event = events[0]; - EXPECT_STREQ(event.rule->get_id().c_str(), "id1"); - EXPECT_STREQ(event.rule->get_name().c_str(), "name1"); + EXPECT_STREQ(event.rule->get_id().data(), "id1"); + EXPECT_STREQ(event.rule->get_name().data(), "name1"); EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); EXPECT_STREQ(event.rule->get_tag("category").data(), "category1"); std::vector expected_actions{}; @@ -515,7 +515,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityLast) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -530,7 +530,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityLast) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"block"}); ruleset->insert_rule(rule); @@ -550,8 +550,8 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityLast) EXPECT_EQ(events.size(), 1); auto &event = events[0]; - EXPECT_STREQ(event.rule->get_id().c_str(), "id1"); - EXPECT_STREQ(event.rule->get_name().c_str(), "name1"); + EXPECT_STREQ(event.rule->get_id().data(), "id1"); + EXPECT_STREQ(event.rule->get_name().data(), "name1"); EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); EXPECT_STREQ(event.rule->get_tag("category").data(), "category1"); std::vector expected_actions{}; @@ -581,8 +581,8 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityLast) auto &event = events[0]; EXPECT_EQ(events.size(), 1); - EXPECT_STREQ(event.rule->get_id().c_str(), "id2"); - EXPECT_STREQ(event.rule->get_name().c_str(), "name2"); + EXPECT_STREQ(event.rule->get_id().data(), "id2"); + EXPECT_STREQ(event.rule->get_name().data(), "name2"); EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); EXPECT_STREQ(event.rule->get_tag("category").data(), "category2"); std::vector expected_actions{"block"}; @@ -612,7 +612,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); ruleset->insert_rule(rule); @@ -628,7 +628,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); + auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -647,8 +647,8 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) EXPECT_EQ(events.size(), 1); auto &event = events[0]; - EXPECT_STREQ(event.rule->get_id().c_str(), "id1"); - EXPECT_STREQ(event.rule->get_name().c_str(), "name1"); + EXPECT_STREQ(event.rule->get_id().data(), "id1"); + EXPECT_STREQ(event.rule->get_name().data(), "name1"); EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); EXPECT_STREQ(event.rule->get_tag("category").data(), "category1"); std::vector expected_actions{"block"}; @@ -691,7 +691,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) std::unordered_map tags{ {"type", "type"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -706,7 +706,7 @@ TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) std::unordered_map tags{ {"type", "type"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); ruleset->insert_rule(rule); @@ -726,8 +726,8 @@ TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) EXPECT_EQ(events.size(), 1); auto &event = events[0]; - EXPECT_STREQ(event.rule->get_id().c_str(), "id1"); - EXPECT_STREQ(event.rule->get_name().c_str(), "name1"); + EXPECT_STREQ(event.rule->get_id().data(), "id1"); + EXPECT_STREQ(event.rule->get_name().data(), "name1"); EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); EXPECT_STREQ(event.rule->get_tag("category").data(), "category1"); EXPECT_TRUE(event.rule->get_actions().empty()); @@ -755,8 +755,8 @@ TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) auto &event = events[0]; EXPECT_EQ(events.size(), 1); - EXPECT_STREQ(event.rule->get_id().c_str(), "id2"); - EXPECT_STREQ(event.rule->get_name().c_str(), "name2"); + EXPECT_STREQ(event.rule->get_id().data(), "id2"); + EXPECT_STREQ(event.rule->get_name().data(), "name2"); EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); EXPECT_STREQ(event.rule->get_tag("category").data(), "category2"); std::vector expected_actions{"redirect"}; @@ -786,7 +786,7 @@ TEST(TestContext, MatchMultipleCollectionsSingleRun) std::unordered_map tags{ {"type", "type1"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -801,7 +801,7 @@ TEST(TestContext, MatchMultipleCollectionsSingleRun) std::unordered_map tags{ {"type", "type2"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); + auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -833,7 +833,7 @@ TEST(TestContext, MatchMultiplePriorityCollectionsSingleRun) std::unordered_map tags{ {"type", "type1"}, {"category", "category1"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); ruleset->insert_rule(rule); @@ -849,7 +849,7 @@ TEST(TestContext, MatchMultiplePriorityCollectionsSingleRun) std::unordered_map tags{ {"type", "type2"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); ruleset->insert_rule(rule); @@ -882,7 +882,7 @@ TEST(TestContext, MatchMultipleCollectionsDoubleRun) std::unordered_map tags{ {"type", "type1"}, {"category", "category1"}}; - auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); + auto rule = std::make_shared("id1", "name1", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -897,7 +897,7 @@ TEST(TestContext, MatchMultipleCollectionsDoubleRun) std::unordered_map tags{ {"type", "type2"}, {"category", "category2"}}; - auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); + auto rule = std::make_shared("id2", "name2", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -941,7 +941,7 @@ TEST(TestContext, MatchMultiplePriorityCollectionsDoubleRun) std::unordered_map tags{ {"type", "type1"}, {"category", "category1"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id1", "name1", std::move(tags), builder.build(), std::vector{"block"}); ruleset->insert_rule(rule); @@ -957,7 +957,7 @@ TEST(TestContext, MatchMultiplePriorityCollectionsDoubleRun) std::unordered_map tags{ {"type", "type2"}, {"category", "category2"}}; - auto rule = std::make_shared( + auto rule = std::make_shared( "id2", "name2", std::move(tags), builder.build(), std::vector{"redirect"}); ruleset->insert_rule(rule); @@ -1020,7 +1020,7 @@ TEST(TestContext, SkipRuleFilterNoTargets) builder.end_condition(std::vector{"192.168.0.1"}); filter = std::make_shared( - "1", builder.build(), std::set{rule.get()}); + "1", builder.build(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -1069,7 +1069,7 @@ TEST(TestContext, SkipRuleButNotRuleFilterNoTargets) builder.end_condition(std::vector{"192.168.0.1"}); filter = std::make_shared( - "1", builder.build(), std::set{rule.get()}); + "1", builder.build(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -1092,7 +1092,7 @@ TEST(TestContext, RuleFilterWithCondition) auto ruleset = test::get_default_ruleset(); // Generate rule - std::shared_ptr rule; + std::shared_ptr rule; { test::expression_builder builder(1); builder.start_condition(); @@ -1103,7 +1103,7 @@ TEST(TestContext, RuleFilterWithCondition) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rule = std::make_shared("id", "name", std::move(tags), builder.build()); + rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -1116,8 +1116,8 @@ TEST(TestContext, RuleFilterWithCondition) builder.add_target("http.client_ip"); builder.end_condition(std::vector{"192.168.0.1"}); - auto filter = std::make_shared( - "1", builder.build(), std::set{rule.get()}); + auto filter = + std::make_shared("1", builder.build(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -1144,7 +1144,7 @@ TEST(TestContext, RuleFilterWithEphemeralConditionMatch) auto ruleset = test::get_default_ruleset(); // Generate rule - std::shared_ptr rule; + std::shared_ptr rule; { test::expression_builder builder(1); builder.start_condition(); @@ -1155,7 +1155,7 @@ TEST(TestContext, RuleFilterWithEphemeralConditionMatch) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rule = std::make_shared("id", "name", std::move(tags), builder.build()); + rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -1168,8 +1168,8 @@ TEST(TestContext, RuleFilterWithEphemeralConditionMatch) builder.add_target("http.client_ip"); builder.end_condition(std::vector{"192.168.0.1"}); - auto filter = std::make_shared( - "1", builder.build(), std::set{rule.get()}); + auto filter = + std::make_shared("1", builder.build(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -1205,7 +1205,7 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralBypassPersistentMonitor) auto ruleset = test::get_default_ruleset(); // Generate rule - std::shared_ptr rule; + std::shared_ptr rule; { test::expression_builder builder(1); builder.start_condition(); @@ -1216,7 +1216,7 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralBypassPersistentMonitor) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rule = std::make_shared("id", "name", std::move(tags), builder.build()); + rule = std::make_shared("id", "name", std::move(tags), builder.build()); rule->set_actions({"block"}); ruleset->insert_rule(rule); } @@ -1229,8 +1229,8 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralBypassPersistentMonitor) builder.add_target("http.client_ip"); builder.end_condition(std::vector{"192.168.0.1"}); - auto filter = std::make_shared( - "1", builder.build(), std::set{rule.get()}); + auto filter = + std::make_shared("1", builder.build(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -1242,7 +1242,7 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralBypassPersistentMonitor) builder.end_condition(std::vector{"unrouted"}); auto filter = std::make_shared("2", builder.build(), - std::set{rule.get()}, exclusion::filter_mode::monitor); + std::set{rule.get()}, exclusion::filter_mode::monitor); ruleset->insert_filter(filter); } @@ -1282,7 +1282,7 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralMonitorPersistentBypass) auto ruleset = test::get_default_ruleset(); // Generate rule - std::shared_ptr rule; + std::shared_ptr rule; { test::expression_builder builder(1); builder.start_condition(); @@ -1293,7 +1293,7 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralMonitorPersistentBypass) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rule = std::make_shared("id", "name", std::move(tags), builder.build()); + rule = std::make_shared("id", "name", std::move(tags), builder.build()); rule->set_actions({"block"}); ruleset->insert_rule(rule); } @@ -1307,7 +1307,7 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralMonitorPersistentBypass) builder.end_condition(std::vector{"192.168.0.1"}); auto filter = std::make_shared("1", builder.build(), - std::set{rule.get()}, exclusion::filter_mode::monitor); + std::set{rule.get()}, exclusion::filter_mode::monitor); ruleset->insert_filter(filter); } @@ -1318,8 +1318,8 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralMonitorPersistentBypass) builder.add_target("http.route"); builder.end_condition(std::vector{"unrouted"}); - auto filter = std::make_shared( - "2", builder.build(), std::set{rule.get()}); + auto filter = + std::make_shared("2", builder.build(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -1356,7 +1356,7 @@ TEST(TestContext, RuleFilterTimeout) auto ruleset = test::get_default_ruleset(); // Generate rule - std::shared_ptr rule; + std::shared_ptr rule; { test::expression_builder builder(1); builder.start_condition(); @@ -1367,7 +1367,7 @@ TEST(TestContext, RuleFilterTimeout) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rule = std::make_shared("id", "name", std::move(tags), builder.build()); + rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -1380,8 +1380,8 @@ TEST(TestContext, RuleFilterTimeout) builder.add_target("http.client_ip"); builder.end_condition(std::vector{"192.168.0.1"}); - auto filter = std::make_shared( - "1", builder.build(), std::set{rule.get()}); + auto filter = + std::make_shared("1", builder.build(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -1403,7 +1403,7 @@ TEST(TestContext, NoRuleFilterWithCondition) auto ruleset = test::get_default_ruleset(); // Generate rule - std::shared_ptr rule; + std::shared_ptr rule; { test::expression_builder builder(1); builder.start_condition(); @@ -1414,7 +1414,7 @@ TEST(TestContext, NoRuleFilterWithCondition) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rule = std::make_shared("id", "name", std::move(tags), builder.build()); + rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -1427,8 +1427,8 @@ TEST(TestContext, NoRuleFilterWithCondition) builder.add_target("http.client_ip"); builder.end_condition(std::vector{"192.168.0.1"}); - auto filter = std::make_shared( - "1", builder.build(), std::set{rule.get()}); + auto filter = + std::make_shared("1", builder.build(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -1455,14 +1455,14 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRules) // Generate rule constexpr unsigned num_rules = 9; - std::vector> rules; + std::vector> rules; rules.reserve(num_rules); for (unsigned i = 0; i < num_rules; i++) { std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rules.emplace_back(std::make_shared("id" + std::to_string(i), "name", + 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()); @@ -1478,7 +1478,7 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRules) { auto filter = std::make_shared("1", std::make_shared(), - std::set{rules[0].get(), rules[1].get(), rules[2].get()}); + std::set{rules[0].get(), rules[1].get(), rules[2].get()}); ruleset->insert_filter(filter); auto rules_to_exclude = ctx.eval_filters(deadline); @@ -1490,7 +1490,7 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRules) { auto filter = std::make_shared("2", std::make_shared(), - std::set{rules[3].get(), rules[4].get(), rules[5].get()}); + std::set{rules[3].get(), rules[4].get(), rules[5].get()}); ruleset->insert_filter(filter); auto rules_to_exclude = ctx.eval_filters(deadline); @@ -1505,7 +1505,7 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRules) { auto filter = std::make_shared("3", std::make_shared(), - std::set{rules[6].get(), rules[7].get(), rules[8].get()}); + std::set{rules[6].get(), rules[7].get(), rules[8].get()}); ruleset->insert_filter(filter); auto rules_to_exclude = ctx.eval_filters(deadline); @@ -1528,7 +1528,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRules) // Generate rule constexpr unsigned num_rules = 9; - std::vector> rules; + std::vector> rules; rules.reserve(num_rules); for (unsigned i = 0; i < num_rules; i++) { std::string id = "id" + std::to_string(i); @@ -1536,7 +1536,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRules) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), + rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), std::make_shared(), std::vector{})); ruleset->insert_rule(rules.back()); @@ -1552,8 +1552,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRules) { auto filter = std::make_shared("1", std::make_shared(), - std::set{ - rules[0].get(), rules[1].get(), rules[2].get(), rules[3].get()}); + std::set{rules[0].get(), rules[1].get(), rules[2].get(), rules[3].get()}); ruleset->insert_filter(filter); auto rules_to_exclude = ctx.eval_filters(deadline); @@ -1566,7 +1565,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRules) { auto filter = std::make_shared("2", std::make_shared(), - std::set{rules[2].get(), rules[3].get(), rules[4].get()}); + std::set{rules[2].get(), rules[3].get(), rules[4].get()}); ruleset->insert_filter(filter); auto rules_to_exclude = ctx.eval_filters(deadline); @@ -1580,7 +1579,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRules) { auto filter = std::make_shared("3", std::make_shared(), - std::set{rules[0].get(), rules[5].get(), rules[6].get()}); + std::set{rules[0].get(), rules[5].get(), rules[6].get()}); ruleset->insert_filter(filter); auto rules_to_exclude = ctx.eval_filters(deadline); @@ -1596,7 +1595,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRules) { auto filter = std::make_shared("4", std::make_shared(), - std::set{rules[7].get(), rules[8].get(), rules[6].get()}); + std::set{rules[7].get(), rules[8].get(), rules[6].get()}); ruleset->insert_filter(filter); auto rules_to_exclude = ctx.eval_filters(deadline); @@ -1614,7 +1613,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRules) { auto filter = std::make_shared("5", std::make_shared(), - std::set{rules[0].get(), rules[1].get(), rules[2].get(), rules[3].get(), + std::set{rules[0].get(), rules[1].get(), rules[2].get(), rules[3].get(), rules[4].get(), rules[5].get(), rules[6].get(), rules[7].get(), rules[8].get()}); ruleset->insert_filter(filter); @@ -1638,7 +1637,7 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRulesWithConditions) // Generate rule constexpr unsigned num_rules = 10; - std::vector> rules; + std::vector> rules; rules.reserve(num_rules); for (unsigned i = 0; i < num_rules; i++) { std::string id = "id" + std::to_string(i); @@ -1646,7 +1645,7 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRulesWithConditions) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), + rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), std::make_shared(), std::vector{})); ruleset->insert_rule(rules.back()); @@ -1663,7 +1662,7 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRulesWithConditions) builder.end_condition(std::vector{"192.168.0.1"}); auto filter = std::make_shared("1", builder.build(), - std::set{ + std::set{ rules[0].get(), rules[1].get(), rules[2].get(), rules[3].get(), rules[4].get()}); ruleset->insert_filter(filter); } @@ -1676,7 +1675,7 @@ TEST(TestContext, MultipleRuleFiltersNonOverlappingRulesWithConditions) builder.end_condition(std::vector{"admin"}); auto filter = std::make_shared("2", builder.build(), - std::set{ + std::set{ rules[5].get(), rules[6].get(), rules[7].get(), rules[8].get(), rules[9].get()}); ruleset->insert_filter(filter); } @@ -1725,7 +1724,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRulesWithConditions) // Generate rule constexpr unsigned num_rules = 10; - std::vector> rules; + std::vector> rules; rules.reserve(num_rules); for (unsigned i = 0; i < num_rules; i++) { std::string id = "id" + std::to_string(i); @@ -1733,7 +1732,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRulesWithConditions) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), + rules.emplace_back(std::make_shared(std::string(id), "name", std::move(tags), std::make_shared(), std::vector{})); ruleset->insert_rule(rules.back()); @@ -1750,7 +1749,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRulesWithConditions) builder.end_condition(std::vector{"192.168.0.1"}); auto filter = std::make_shared("1", builder.build(), - std::set{rules[0].get(), rules[1].get(), rules[2].get(), rules[3].get(), + std::set{rules[0].get(), rules[1].get(), rules[2].get(), rules[3].get(), rules[4].get(), rules[5].get(), rules[6].get()}); ruleset->insert_filter(filter); } @@ -1763,7 +1762,7 @@ TEST(TestContext, MultipleRuleFiltersOverlappingRulesWithConditions) builder.end_condition(std::vector{"admin"}); auto filter = std::make_shared("2", builder.build(), - std::set{rules[3].get(), rules[4].get(), rules[5].get(), rules[6].get(), + std::set{rules[3].get(), rules[4].get(), rules[5].get(), rules[6].get(), rules[7].get(), rules[8].get(), rules[9].get()}); ruleset->insert_filter(filter); } @@ -1835,7 +1834,7 @@ TEST(TestContext, SkipInputFilterNoTargets) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{rule.get()}; + std::set eval_filters{rule.get()}; filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); ruleset->insert_filter(filter); @@ -1881,7 +1880,7 @@ TEST(TestContext, SkipRuleButNotInputFilterNoTargets) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{rule.get()}; + std::set eval_filters{rule.get()}; filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); ruleset->insert_filter(filter); @@ -1910,12 +1909,12 @@ TEST(TestContext, InputFilterExclude) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{rule.get()}; + std::set eval_filters{rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -1950,12 +1949,12 @@ TEST(TestContext, InputFilterExcludeEphemeral) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{rule.get()}; + std::set eval_filters{rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2001,12 +2000,12 @@ TEST(TestContext, InputFilterExcludeEphemeralReuseObject) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{rule.get()}; + std::set eval_filters{rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2045,14 +2044,14 @@ TEST(TestContext, InputFilterExcludeRule) auto ruleset = test::get_default_ruleset(); - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); { auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{rule.get()}; + std::set eval_filters{rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2061,7 +2060,7 @@ TEST(TestContext, InputFilterExcludeRule) { auto filter = std::make_shared( - "1", std::make_shared(), std::set{rule.get()}); + "1", std::make_shared(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -2100,14 +2099,14 @@ TEST(TestContext, InputFilterExcludeRuleEphemeral) auto ruleset = test::get_default_ruleset(); - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); { auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{rule.get()}; + std::set eval_filters{rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2116,7 +2115,7 @@ TEST(TestContext, InputFilterExcludeRuleEphemeral) { auto filter = std::make_shared( - "1", std::make_shared(), std::set{rule.get()}); + "1", std::make_shared(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -2150,14 +2149,14 @@ TEST(TestContext, InputFilterMonitorRuleEphemeral) auto ruleset = test::get_default_ruleset(); - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); { auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - std::set eval_filters{rule.get()}; + std::set eval_filters{rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2166,7 +2165,7 @@ TEST(TestContext, InputFilterMonitorRuleEphemeral) { auto filter = std::make_shared("1", std::make_shared(), - std::set{rule.get()}, filter_mode::monitor); + std::set{rule.get()}, filter_mode::monitor); ruleset->insert_filter(filter); } @@ -2205,7 +2204,7 @@ TEST(TestContext, InputFilterExcluderRuleEphemeralAndPersistent) auto ruleset = test::get_default_ruleset(); - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); { @@ -2213,7 +2212,7 @@ TEST(TestContext, InputFilterExcluderRuleEphemeralAndPersistent) 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{rule.get()}; + std::set eval_filters{rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2222,7 +2221,7 @@ TEST(TestContext, InputFilterExcluderRuleEphemeralAndPersistent) { auto filter = std::make_shared( - "1", std::make_shared(), std::set{rule.get()}); + "1", std::make_shared(), std::set{rule.get()}); ruleset->insert_filter(filter); } @@ -2266,7 +2265,7 @@ TEST(TestContext, InputFilterMonitorRuleEphemeralAndPersistent) auto ruleset = test::get_default_ruleset(); - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); { @@ -2274,7 +2273,7 @@ TEST(TestContext, InputFilterMonitorRuleEphemeralAndPersistent) 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{rule.get()}; + std::set eval_filters{rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2283,7 +2282,7 @@ TEST(TestContext, InputFilterMonitorRuleEphemeralAndPersistent) { auto filter = std::make_shared("1", std::make_shared(), - std::set{rule.get()}, filter_mode::monitor); + std::set{rule.get()}, filter_mode::monitor); ruleset->insert_filter(filter); } @@ -2333,7 +2332,7 @@ TEST(TestContext, InputFilterWithCondition) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2348,7 +2347,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].get()}; auto filter = std::make_shared( "1", builder.build(), std::move(eval_filters), std::move(obj_filter)); @@ -2422,7 +2421,7 @@ TEST(TestContext, InputFilterWithEphemeralCondition) std::unordered_map tags{ {"type", "type"}, {"category", "category"}}; - auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); + auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2437,7 +2436,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].get()}; auto filter = std::make_shared( "1", builder.build(), std::move(eval_filters), std::move(obj_filter)); @@ -2482,8 +2481,7 @@ 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()); + auto rule = std::make_shared("ip_id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2498,8 +2496,7 @@ 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()); + auto rule = std::make_shared("usr_id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2509,7 +2506,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].get(), ruleset->rules[1].get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2595,8 +2592,7 @@ 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()); + auto rule = std::make_shared("ip_id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2611,8 +2607,7 @@ 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()); + auto rule = std::make_shared("usr_id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2621,7 +2616,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].get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2632,7 +2627,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].get()}; auto filter = std::make_shared( "2", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2721,8 +2716,7 @@ 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()); + auto rule = std::make_shared("ip_id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2737,8 +2731,7 @@ 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()); + auto rule = std::make_shared("usr_id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2754,7 +2747,7 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) {"type", "cookie_type"}, {"category", "category"}}; auto rule = - std::make_shared("cookie_id", "name", std::move(tags), builder.build()); + std::make_shared("cookie_id", "name", std::move(tags), builder.build()); ruleset->insert_rule(rule); } @@ -2768,7 +2761,7 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) 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.get(), cookie_rule.get()}; auto filter = std::make_shared( "1", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2780,7 +2773,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.get(), ip_rule.get()}; auto filter = std::make_shared( "2", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); @@ -2792,7 +2785,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.get(), cookie_rule.get()}; auto filter = std::make_shared( "3", std::make_shared(), std::move(eval_filters), std::move(obj_filter)); diff --git a/tests/unit/event_serializer_test.cpp b/tests/unit/event_serializer_test.cpp index 43cdee345..5d644bdc5 100644 --- a/tests/unit/event_serializer_test.cpp +++ b/tests/unit/event_serializer_test.cpp @@ -41,7 +41,7 @@ TEST(TestEventSerializer, SerializeEmptyEvent) TEST(TestEventSerializer, SerializeSingleEventSingleMatch) { - ddwaf::rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}}, + core_rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}}, std::make_shared(), {"block", "monitor_request"}}; ddwaf::event event; @@ -78,7 +78,7 @@ TEST(TestEventSerializer, SerializeSingleEventSingleMatch) TEST(TestEventSerializer, SerializeSingleEventMultipleMatches) { - ddwaf::rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}}, + core_rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}}, std::make_shared(), {"block", "monitor_request"}}; ddwaf::event event; @@ -153,9 +153,9 @@ TEST(TestEventSerializer, SerializeMultipleEvents) ddwaf::obfuscator obfuscator; ddwaf::event_serializer serializer(obfuscator, actions); - ddwaf::rule rule1{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}}, + core_rule rule1{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}}, std::make_shared(), {"block", "monitor_request"}}; - ddwaf::rule rule2{"xasd1023", "pseudorandom rule", {{"type", "test"}, {"category", "none"}}, + core_rule rule2{"xasd1023", "pseudorandom rule", {{"type", "test"}, {"category", "none"}}, std::make_shared(), {"unblock"}}; std::vector events; { @@ -227,7 +227,7 @@ TEST(TestEventSerializer, SerializeMultipleEvents) TEST(TestEventSerializer, SerializeEventNoActions) { - ddwaf::rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}}, + core_rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}}, std::make_shared()}; ddwaf::event event; @@ -265,7 +265,7 @@ TEST(TestEventSerializer, SerializeEventNoActions) TEST(TestEventSerializer, SerializeAllTags) { - ddwaf::rule rule{"xasd1022", "random rule", + core_rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}, {"tag0", "value0"}, {"tag1", "value1"}, {"confidence", "none"}}, std::make_shared(), {"unblock"}}; @@ -307,7 +307,7 @@ TEST(TestEventSerializer, SerializeAllTags) TEST(TestEventSerializer, NoMonitorActions) { - ddwaf::rule rule{"xasd1022", "random rule", + core_rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}, {"tag0", "value0"}, {"tag1", "value1"}, {"confidence", "none"}}, std::make_shared(), {"monitor"}}; @@ -347,7 +347,7 @@ TEST(TestEventSerializer, NoMonitorActions) TEST(TestEventSerializer, UndefinedActions) { - ddwaf::rule rule{"xasd1022", "random rule", + core_rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}, {"tag0", "value0"}, {"tag1", "value1"}, {"confidence", "none"}}, std::make_shared(), {"unblock_request"}}; @@ -387,7 +387,7 @@ TEST(TestEventSerializer, UndefinedActions) TEST(TestEventSerializer, StackTraceAction) { - ddwaf::rule rule{"xasd1022", "random rule", + core_rule rule{"xasd1022", "random rule", {{"type", "test"}, {"category", "none"}, {"tag0", "value0"}, {"tag1", "value1"}, {"confidence", "none"}}, std::make_shared(), {"stack_trace"}}; diff --git a/tests/unit/exclusion/input_filter_test.cpp b/tests/unit/exclusion/input_filter_test.cpp index fa8310268..32ef2d060 100644 --- a/tests/unit/exclusion/input_filter_test.cpp +++ b/tests/unit/exclusion/input_filter_test.cpp @@ -26,8 +26,7 @@ TEST(TestInputFilter, InputExclusionNoConditions) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter( "filter", std::make_shared(), {rule.get()}, std::move(obj_filter)); @@ -55,8 +54,7 @@ TEST(TestInputFilter, EphemeralInputExclusionNoConditions) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter( "filter", std::make_shared(), {rule.get()}, std::move(obj_filter)); @@ -89,8 +87,7 @@ TEST(TestInputFilter, ObjectExclusionNoConditions) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter( "filter", std::make_shared(), {rule.get()}, std::move(obj_filter)); @@ -123,8 +120,7 @@ TEST(TestInputFilter, EphemeralObjectExclusionNoConditions) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter( "filter", std::make_shared(), {rule.get()}, std::move(obj_filter)); @@ -158,8 +154,7 @@ TEST(TestInputFilter, PersistentInputExclusionWithPersistentCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip", {}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); ddwaf::timer deadline{2s}; @@ -192,8 +187,7 @@ TEST(TestInputFilter, EphemeralInputExclusionWithEphemeralCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip", {}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); ddwaf::timer deadline{2s}; @@ -230,8 +224,7 @@ TEST(TestInputFilter, PersistentInputExclusionWithEphemeralCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip", {}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); ddwaf::timer deadline{2s}; @@ -268,8 +261,7 @@ TEST(TestInputFilter, EphemeralInputExclusionWithPersistentCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip", {}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); ddwaf::timer deadline{2s}; @@ -302,8 +294,7 @@ TEST(TestInputFilter, InputExclusionWithConditionAndTransformers) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("usr.id"), "usr.id", {}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); ddwaf::timer deadline{2s}; @@ -335,8 +326,7 @@ TEST(TestInputFilter, InputExclusionFailedCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip", {}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); ddwaf::timer deadline{2s}; @@ -370,8 +360,7 @@ TEST(TestInputFilter, ObjectExclusionWithCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); ddwaf::timer deadline{2s}; @@ -409,8 +398,7 @@ TEST(TestInputFilter, ObjectExclusionFailedCondition) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); ddwaf::timer deadline{2s}; @@ -435,8 +423,7 @@ TEST(TestInputFilter, InputValidateCachedMatch) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("usr.id"), "usr.id"); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // To validate that the cache works, we pass an object store containing @@ -485,8 +472,7 @@ TEST(TestInputFilter, InputValidateCachedEphemeralMatch) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("usr.id"), "usr.id"); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // To validate that the cache works, we pass an object store containing @@ -566,8 +552,7 @@ TEST(TestInputFilter, InputMatchWithoutCache) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // In this test we validate that when the cache is empty and only one @@ -616,8 +601,7 @@ TEST(TestInputFilter, InputNoMatchWithoutCache) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // In this instance we pass a complete store with both addresses but an @@ -674,8 +658,7 @@ TEST(TestInputFilter, InputCachedMatchSecondRun) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("http.client_ip"), "http.client_ip"); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // In this instance we pass a complete store with both addresses but an @@ -734,8 +717,7 @@ TEST(TestInputFilter, ObjectValidateCachedMatch) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // To validate that the cache works, we pass an object store containing @@ -798,8 +780,7 @@ TEST(TestInputFilter, ObjectMatchWithoutCache) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // In this test we validate that when the cache is empty and only one @@ -858,8 +839,7 @@ TEST(TestInputFilter, ObjectNoMatchWithoutCache) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // In this instance we pass a complete store with both addresses but an @@ -918,8 +898,7 @@ TEST(TestInputFilter, ObjectCachedMatchSecondRun) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); // In this instance we pass a complete store with both addresses but an @@ -982,8 +961,7 @@ TEST(TestInputFilter, MatchWithDynamicMatcher) auto obj_filter = std::make_shared(); obj_filter->insert(get_target_index("query"), "query", {"params"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); input_filter filter("filter", builder.build(), {rule.get()}, std::move(obj_filter)); { diff --git a/tests/unit/exclusion/rule_filter_test.cpp b/tests/unit/exclusion/rule_filter_test.cpp index 2b713f611..6efa34e76 100644 --- a/tests/unit/exclusion/rule_filter_test.cpp +++ b/tests/unit/exclusion/rule_filter_test.cpp @@ -23,8 +23,7 @@ TEST(TestRuleFilter, Match) builder.add_target("http.client_ip"); builder.end_condition(std::vector{"192.168.0.1"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; std::unordered_map addresses; @@ -59,8 +58,7 @@ TEST(TestRuleFilter, MatchWithDynamicMatcher) builder.add_target("http.client_ip"); builder.end_condition_with_data("ip_data"); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; std::unordered_map addresses; @@ -116,8 +114,7 @@ TEST(TestRuleFilter, EphemeralMatch) builder.add_target("http.client_ip"); builder.end_condition(std::vector{"192.168.0.1"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; std::unordered_map addresses; @@ -182,8 +179,7 @@ TEST(TestRuleFilter, ValidateCachedMatch) builder.add_target("usr.id"); builder.end_condition(std::vector{"admin"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; ddwaf::exclusion::rule_filter::cache_type cache; @@ -238,8 +234,7 @@ TEST(TestRuleFilter, CachedMatchAndEphemeralMatch) builder.add_target("usr.id"); builder.end_condition(std::vector{"admin"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; ddwaf::exclusion::rule_filter::cache_type cache; @@ -296,8 +291,7 @@ TEST(TestRuleFilter, ValidateEphemeralMatchCache) builder.add_target("usr.id"); builder.end_condition(std::vector{"admin"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; ddwaf::exclusion::rule_filter::cache_type cache; @@ -349,8 +343,7 @@ TEST(TestRuleFilter, MatchWithoutCache) builder.add_target("usr.id"); builder.end_condition(std::vector{"admin"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; // In this instance we pass a complete store with both addresses but an @@ -398,8 +391,7 @@ TEST(TestRuleFilter, NoMatchWithoutCache) builder.add_target("usr.id"); builder.end_condition(std::vector{"admin"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; // In this test we validate that when the cache is empty and only one @@ -447,8 +439,7 @@ TEST(TestRuleFilter, FullCachedMatchSecondRun) builder.add_target("usr.id"); builder.end_condition(std::vector{"admin"}); - auto rule = - std::make_shared(ddwaf::rule("", "", {}, std::make_shared())); + auto rule = std::make_shared(core_rule("", "", {}, std::make_shared())); ddwaf::exclusion::rule_filter filter{"filter", builder.build(), {rule.get()}}; ddwaf::object_store store; diff --git a/tests/unit/mkmap_test.cpp b/tests/unit/mkmap_test.cpp index 94f8cfbd1..950987a03 100644 --- a/tests/unit/mkmap_test.cpp +++ b/tests/unit/mkmap_test.cpp @@ -12,7 +12,7 @@ using namespace ddwaf; using namespace std::literals; -using rule_tag_map = ddwaf::multi_key_map; +using rule_tag_map = ddwaf::multi_key_map; namespace { @@ -36,13 +36,13 @@ TEST(TestMultiKeyMap, Find) {"id6", "type1", "category1", {{"key", "value0"}}}, {"id7", "type1", "category1", {{"key", "value1"}}}}; - std::vector> rules; + std::vector> rules; for (const auto &spec : specs) { std::unordered_map tags = spec.tags; tags.emplace("type", spec.type); tags.emplace("category", spec.category); - auto rule_ptr = std::make_shared( + auto rule_ptr = std::make_shared( std::string(spec.id), "name", decltype(tags)(tags), std::make_shared()); rules.emplace_back(rule_ptr); ruledb.insert(rule_ptr->get_tags(), rule_ptr.get()); @@ -101,13 +101,13 @@ TEST(TestMultiKeyMap, Multifind) {"id6", "type1", "category1", {{"key", "value0"}}}, {"id7", "type1", "category1", {{"key", "value1"}}}}; - std::vector> rules; + std::vector> rules; for (const auto &spec : specs) { std::unordered_map tags = spec.tags; tags.emplace("type", spec.type); tags.emplace("category", spec.category); - auto rule_ptr = std::make_shared( + auto rule_ptr = std::make_shared( std::string(spec.id), "name", decltype(tags)(tags), std::make_shared()); rules.emplace_back(rule_ptr); ruledb.insert(rule_ptr->get_tags(), rule_ptr.get()); @@ -171,13 +171,13 @@ TEST(TestMultiKeyMap, Erase) {"id6", "type1", "category1", {{"key", "value0"}}}, {"id7", "type1", "category1", {{"key", "value1"}}}}; - std::vector> rules; + std::vector> rules; for (const auto &spec : specs) { std::unordered_map tags = spec.tags; tags.emplace("type", spec.type); tags.emplace("category", spec.category); - auto rule_ptr = std::make_shared( + auto rule_ptr = std::make_shared( std::string(spec.id), "name", decltype(tags)(tags), std::make_shared()); rules.emplace_back(rule_ptr); ruledb.insert(rule_ptr->get_tags(), rule_ptr.get()); diff --git a/tests/unit/parser_transformers_test.cpp b/tests/unit/parser_transformers_test.cpp index 4b27e509d..1d56ca414 100644 --- a/tests/unit/parser_transformers_test.cpp +++ b/tests/unit/parser_transformers_test.cpp @@ -13,8 +13,8 @@ using namespace ddwaf::parser; // NOLINTBEGIN(cppcoreguidelines-macro-usage,bugprone-unchecked-optional-access) #define EXPECT_OPTEQ(opt, expected) \ - ASSERT_TRUE((opt)); \ - EXPECT_EQ((opt), expected); + ASSERT_TRUE((opt)); \ + EXPECT_EQ((opt), expected); // NOLINTEND(cppcoreguidelines-macro-usage,bugprone-unchecked-optional-access) namespace { diff --git a/tests/unit/rule_test.cpp b/tests/unit/rule_test.cpp index 49d789e40..ec8e7280d 100644 --- a/tests/unit/rule_test.cpp +++ b/tests/unit/rule_test.cpp @@ -25,8 +25,7 @@ TEST(TestRule, Match) builder.end_condition(std::vector{"192.168.0.1"}); std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - ddwaf::rule rule( - "id", "name", std::move(tags), builder.build(), {"update", "block", "passlist"}); + core_rule rule("id", "name", std::move(tags), builder.build(), {"update", "block", "passlist"}); ddwaf_object root; ddwaf_object tmp; @@ -37,7 +36,7 @@ TEST(TestRule, Match) ddwaf::timer deadline{2s}; - rule::cache_type cache; + core_rule::cache_type cache; { auto scope = store.get_eval_scope(); store.insert(root, object_store::attribute::none, nullptr); @@ -45,8 +44,8 @@ TEST(TestRule, Match) auto event = rule.match(store, cache, {}, {}, deadline); EXPECT_TRUE(event.has_value()); - EXPECT_STREQ(event->rule->get_id().c_str(), "id"); - EXPECT_STREQ(event->rule->get_name().c_str(), "name"); + EXPECT_STREQ(event->rule->get_id().data(), "id"); + EXPECT_STREQ(event->rule->get_name().data(), "name"); EXPECT_STREQ(event->rule->get_tag("type").data(), "type"); EXPECT_STREQ(event->rule->get_tag("category").data(), "category"); std::vector expected_actions{"update", "block", "passlist"}; @@ -86,8 +85,7 @@ TEST(TestRule, EphemeralMatch) builder.end_condition(std::vector{"192.168.0.1"}); std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - ddwaf::rule rule( - "id", "name", std::move(tags), builder.build(), {"update", "block", "passlist"}); + core_rule rule("id", "name", std::move(tags), builder.build(), {"update", "block", "passlist"}); ddwaf::object_store store; @@ -98,7 +96,7 @@ TEST(TestRule, EphemeralMatch) ddwaf::timer deadline{2s}; - rule::cache_type cache; + core_rule::cache_type cache; { auto scope = store.get_eval_scope(); store.insert(root, object_store::attribute::ephemeral, nullptr); @@ -131,7 +129,7 @@ TEST(TestRule, NoMatch) builder.end_condition(std::vector{}); std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - ddwaf::rule rule("id", "name", std::move(tags), builder.build()); + core_rule rule("id", "name", std::move(tags), builder.build()); ddwaf_object root; ddwaf_object tmp; @@ -143,7 +141,7 @@ TEST(TestRule, NoMatch) ddwaf::timer deadline{2s}; - rule::cache_type cache; + core_rule::cache_type cache; auto match = rule.match(store, cache, {}, {}, deadline); EXPECT_FALSE(match.has_value()); } @@ -163,8 +161,8 @@ TEST(TestRule, ValidateCachedMatch) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - ddwaf::rule rule("id", "name", std::move(tags), builder.build()); - ddwaf::rule::cache_type cache; + core_rule rule("id", "name", std::move(tags), builder.build()); + core_rule::cache_type cache; // To validate that the cache works, we pass an object store containing // only the latest address. This ensures that the IP condition can't be @@ -195,8 +193,8 @@ TEST(TestRule, ValidateCachedMatch) ddwaf::timer deadline{2s}; auto event = rule.match(store, cache, {}, {}, deadline); EXPECT_TRUE(event.has_value()); - EXPECT_STREQ(event->rule->get_id().c_str(), "id"); - EXPECT_STREQ(event->rule->get_name().c_str(), "name"); + EXPECT_STREQ(event->rule->get_id().data(), "id"); + EXPECT_STREQ(event->rule->get_name().data(), "name"); EXPECT_STREQ(event->rule->get_tag("type").data(), "type"); EXPECT_STREQ(event->rule->get_tag("category").data(), "category"); EXPECT_TRUE(event->rule->get_actions().empty()); @@ -238,7 +236,7 @@ TEST(TestRule, MatchWithoutCache) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - ddwaf::rule rule("id", "name", std::move(tags), builder.build()); + core_rule rule("id", "name", std::move(tags), builder.build()); // In this instance we pass a complete store with both addresses but an // empty cache on every run to ensure that both conditions are matched on @@ -252,7 +250,7 @@ TEST(TestRule, MatchWithoutCache) store.insert(root); ddwaf::timer deadline{2s}; - ddwaf::rule::cache_type cache; + core_rule::cache_type cache; auto event = rule.match(store, cache, {}, {}, deadline); EXPECT_FALSE(event.has_value()); } @@ -265,7 +263,7 @@ TEST(TestRule, MatchWithoutCache) store.insert(root); ddwaf::timer deadline{2s}; - ddwaf::rule::cache_type cache; + core_rule::cache_type cache; auto event = rule.match(store, cache, {}, {}, deadline); EXPECT_TRUE(event.has_value()); @@ -305,7 +303,7 @@ TEST(TestRule, NoMatchWithoutCache) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - ddwaf::rule rule("id", "name", std::move(tags), builder.build()); + core_rule rule("id", "name", std::move(tags), builder.build()); // In this test we validate that when the cache is empty and only one // address is passed, the filter doesn't match (as it should be). @@ -318,7 +316,7 @@ TEST(TestRule, NoMatchWithoutCache) store.insert(root); ddwaf::timer deadline{2s}; - ddwaf::rule::cache_type cache; + core_rule::cache_type cache; auto event = rule.match(store, cache, {}, {}, deadline); EXPECT_FALSE(event.has_value()); } @@ -332,7 +330,7 @@ TEST(TestRule, NoMatchWithoutCache) store.insert(root); ddwaf::timer deadline{2s}; - ddwaf::rule::cache_type cache; + core_rule::cache_type cache; auto event = rule.match(store, cache, {}, {}, deadline); EXPECT_FALSE(event.has_value()); } @@ -353,12 +351,12 @@ TEST(TestRule, FullCachedMatchSecondRun) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - ddwaf::rule rule("id", "name", std::move(tags), builder.build()); + core_rule rule("id", "name", std::move(tags), builder.build()); // In this test we validate that when a match has already occurred, the // second run for the same rule returns no events regardless of input. - ddwaf::rule::cache_type cache; + core_rule::cache_type cache; { ddwaf_object root, tmp; ddwaf_object_map(&root); @@ -398,8 +396,7 @@ TEST(TestRule, ExcludeObject) std::unordered_map tags{{"type", "type"}, {"category", "category"}}; - ddwaf::rule rule( - "id", "name", std::move(tags), builder.build(), {"update", "block", "passlist"}); + core_rule rule("id", "name", std::move(tags), builder.build(), {"update", "block", "passlist"}); ddwaf_object root, tmp; ddwaf_object_map(&root); @@ -411,7 +408,7 @@ TEST(TestRule, ExcludeObject) ddwaf::timer deadline{2s}; std::unordered_set excluded_set{&root.array[0]}; - rule::cache_type cache; + core_rule::cache_type cache; auto event = rule.match(store, cache, {excluded_set, {}}, {}, deadline); EXPECT_FALSE(event.has_value()); } diff --git a/tests/unit/ruleset_test.cpp b/tests/unit/ruleset_test.cpp index 4af95953f..b96aea7db 100644 --- a/tests/unit/ruleset_test.cpp +++ b/tests/unit/ruleset_test.cpp @@ -11,17 +11,17 @@ using namespace ddwaf; namespace { -std::shared_ptr make_rule(std::string id, std::string name, +std::shared_ptr make_rule(std::string id, std::string name, std::unordered_map tags, std::vector actions, - rule::source_type source = rule::source_type::base) + core_rule::source_type source = core_rule::source_type::base) { - return std::make_shared(std::move(id), std::move(name), std::move(tags), + return std::make_shared(std::move(id), std::move(name), std::move(tags), std::make_shared(), std::move(actions), true, source); } TEST(TestRuleset, InsertSingleRegularBaseRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {}), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {}), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {}), @@ -55,7 +55,7 @@ TEST(TestRuleset, InsertSingleRegularBaseRules) TEST(TestRuleset, InsertSinglePriorityBaseRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {"block"}), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}), @@ -89,7 +89,7 @@ TEST(TestRuleset, InsertSinglePriorityBaseRules) TEST(TestRuleset, InsertSingleMixedBaseRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {}), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {}), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}), @@ -123,19 +123,19 @@ TEST(TestRuleset, InsertSingleMixedBaseRules) TEST(TestRuleset, InsertSingleRegularUserRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id3", "name", {{"type", "type2"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id4", "name", {{"type", "type2"}, {"category", "category1"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id5", "name", {{"type", "type2"}, {"category", "category1"}}, {}, - rule::source_type::user), + core_rule::source_type::user), }; { @@ -164,19 +164,19 @@ TEST(TestRuleset, InsertSingleRegularUserRules) TEST(TestRuleset, InsertSinglePriorityUserRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id3", "name", {{"type", "type2"}, {"category", "category0"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id4", "name", {{"type", "type2"}, {"category", "category1"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id5", "name", {{"type", "type2"}, {"category", "category1"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), }; { ddwaf::ruleset ruleset; @@ -203,19 +203,19 @@ TEST(TestRuleset, InsertSinglePriorityUserRules) TEST(TestRuleset, InsertSingleMixedUserRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id3", "name", {{"type", "type2"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id4", "name", {{"type", "type2"}, {"category", "category1"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id5", "name", {{"type", "type2"}, {"category", "category1"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), }; { @@ -243,19 +243,19 @@ TEST(TestRuleset, InsertSingleMixedUserRules) TEST(TestRuleset, InsertSingleRegularMixedRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {}, - rule::source_type::base), + core_rule::source_type::base), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {}, - rule::source_type::base), + core_rule::source_type::base), make_rule("id3", "name", {{"type", "type2"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id4", "name", {{"type", "type2"}, {"category", "category1"}}, {}, - rule::source_type::base), + core_rule::source_type::base), make_rule("id5", "name", {{"type", "type2"}, {"category", "category1"}}, {}, - rule::source_type::user), + core_rule::source_type::user), }; { @@ -283,19 +283,19 @@ TEST(TestRuleset, InsertSingleRegularMixedRules) TEST(TestRuleset, InsertSinglePriorityMixedRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {"block"}, - rule::source_type::base), + core_rule::source_type::base), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}, - rule::source_type::base), + core_rule::source_type::base), make_rule("id3", "name", {{"type", "type2"}, {"category", "category0"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id4", "name", {{"type", "type2"}, {"category", "category1"}}, {"block"}, - rule::source_type::base), + core_rule::source_type::base), make_rule("id5", "name", {{"type", "type2"}, {"category", "category1"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), }; { ddwaf::ruleset ruleset; @@ -322,19 +322,19 @@ TEST(TestRuleset, InsertSinglePriorityMixedRules) TEST(TestRuleset, InsertSingleMixedMixedRules) { - std::vector> rules{ + std::vector> rules{ make_rule("id0", "name", {{"type", "type0"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id1", "name", {{"type", "type1"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id2", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id3", "name", {{"type", "type2"}, {"category", "category0"}}, {}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id4", "name", {{"type", "type2"}, {"category", "category1"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id5", "name", {{"type", "type2"}, {"category", "category1"}}, {"block"}, - rule::source_type::user), + core_rule::source_type::user), make_rule("id6", "name", {{"type", "type0"}, {"category", "category0"}}, {}), make_rule("id7", "name", {{"type", "type1"}, {"category", "category0"}}, {}), make_rule("id8", "name", {{"type", "type1"}, {"category", "category0"}}, {"block"}), diff --git a/tests/unit/transformer/manager_test.cpp b/tests/unit/transformer/manager_test.cpp index a53a99769..082bdd738 100644 --- a/tests/unit/transformer/manager_test.cpp +++ b/tests/unit/transformer/manager_test.cpp @@ -15,20 +15,20 @@ namespace { // NOLINTNEXTLINE #define EXPECT_TRANSFORM(src, dst, ...) \ - { \ - auto res = transform({src, sizeof(src) - 1}, {__VA_ARGS__}); \ - EXPECT_TRUE(res); \ - if (res) { \ - EXPECT_STR(res.value(), dst); \ - } \ - } + { \ + auto res = transform({src, sizeof(src) - 1}, {__VA_ARGS__}); \ + EXPECT_TRUE(res); \ + if (res) { \ + EXPECT_STR(res.value(), dst); \ + } \ + } // NOLINTNEXTLINE #define EXPECT_NO_TRANSFORM(src, ...) \ - { \ - auto res = transform({src, sizeof(src) - 1}, {__VA_ARGS__}); \ - EXPECT_FALSE(res); \ - } + { \ + auto res = transform({src, sizeof(src) - 1}, {__VA_ARGS__}); \ + EXPECT_FALSE(res); \ + } std::optional transform(std::string_view input, const std::vector &ids) { diff --git a/tests/unit/transformer/transformer_utils.hpp b/tests/unit/transformer/transformer_utils.hpp index 1ba2048de..985dbc33a 100644 --- a/tests/unit/transformer/transformer_utils.hpp +++ b/tests/unit/transformer/transformer_utils.hpp @@ -29,41 +29,41 @@ template constexpr std::array literal_to_array(cons // NOLINTBEGIN(cppcoreguidelines-macro-usage) #define EXPECT_EVENTS(result, ...) \ - { \ - auto data = ddwaf::test::object_to_json(result.events); \ - EXPECT_TRUE(ValidateSchema(data)); \ - YAML::Node doc = YAML::Load(data.c_str()); \ - auto events = doc.as>(); \ - EXPECT_ACTIONS(result, __VA_ARGS__) \ - } + { \ + auto data = ddwaf::test::object_to_json(result.events); \ + EXPECT_TRUE(ValidateSchema(data)); \ + YAML::Node doc = YAML::Load(data.c_str()); \ + auto events = doc.as>(); \ + EXPECT_ACTIONS(result, __VA_ARGS__) \ + } #define EXPECT_TRANSFORM(name, source, expected) \ + { \ { \ - { \ - cow_string str(std::string_view{source, sizeof(source) - 1}); \ - EXPECT_TRUE(transformer::name::transform(str)); \ - EXPECT_STREQ(str.data(), expected); \ - } \ - if constexpr (sizeof(source) > 1) { \ - std::array copy{literal_to_array(source)}; \ - cow_string str(std::string_view{copy.data(), copy.size()}); \ - EXPECT_TRUE(transformer::name::transform(str)) << "Non nul-terminated string"; \ - EXPECT_STREQ(str.data(), expected) << "Non nul-terminated string"; \ - } \ - } + cow_string str(std::string_view{source, sizeof(source) - 1}); \ + EXPECT_TRUE(transformer::name::transform(str)); \ + EXPECT_STREQ(str.data(), expected); \ + } \ + if constexpr (sizeof(source) > 1) { \ + std::array copy{literal_to_array(source)}; \ + cow_string str(std::string_view{copy.data(), copy.size()}); \ + EXPECT_TRUE(transformer::name::transform(str)) << "Non nul-terminated string"; \ + EXPECT_STREQ(str.data(), expected) << "Non nul-terminated string"; \ + } \ + } #define EXPECT_NO_TRANSFORM(name, source) \ + { \ { \ - { \ - cow_string str(std::string_view{source, sizeof(source) - 1}); \ - EXPECT_FALSE(transformer::name::transform(str)); \ - EXPECT_FALSE(str.modified()); \ - } \ - if constexpr (sizeof(source) > 1) { \ - std::array copy{literal_to_array(source)}; \ - cow_string str(std::string_view{copy.data(), copy.size()}); \ - EXPECT_FALSE(transformer::name::transform(str)) << "Non nul-terminated string"; \ - EXPECT_FALSE(str.modified()); \ - } \ - } + cow_string str(std::string_view{source, sizeof(source) - 1}); \ + EXPECT_FALSE(transformer::name::transform(str)); \ + EXPECT_FALSE(str.modified()); \ + } \ + if constexpr (sizeof(source) > 1) { \ + std::array copy{literal_to_array(source)}; \ + cow_string str(std::string_view{copy.data(), copy.size()}); \ + EXPECT_FALSE(transformer::name::transform(str)) << "Non nul-terminated string"; \ + EXPECT_FALSE(str.modified()); \ + } \ + } // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/tests/unit/utils_test.cpp b/tests/unit/utils_test.cpp index aa1d0b77e..8fc09d0b3 100644 --- a/tests/unit/utils_test.cpp +++ b/tests/unit/utils_test.cpp @@ -351,10 +351,10 @@ TEST(TestUtils, CloneMap) } #define EXPECT_VEC(expected, ...) \ - { \ - std::vector vec{__VA_ARGS__}; \ - EXPECT_EQ(expected, vec); \ - } + { \ + std::vector vec{__VA_ARGS__}; \ + EXPECT_EQ(expected, vec); \ + } TEST(TestUtils, Split) { diff --git a/validator/assert.hpp b/validator/assert.hpp index d2e22cf00..5cf5479b0 100644 --- a/validator/assert.hpp +++ b/validator/assert.hpp @@ -15,13 +15,13 @@ #include "utils.hpp" #define expect(lhs, rhs) \ - try { \ - assert(lhs, rhs, __LINE__, __func__); \ - } catch (const assert_exception &e) { \ - throw; \ - } catch (const std::exception &e) { \ - throw assert_exception(e.what(), __LINE__, __func__); \ - } + try { \ + assert(lhs, rhs, __LINE__, __func__); \ + } catch (const assert_exception &e) { \ + throw; \ + } catch (const std::exception &e) { \ + throw assert_exception(e.what(), __LINE__, __func__); \ + } class assert_exception : public std::exception { public: From ae1c1e9df655861c00a58ecf25383faf7502318e Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:38:53 +0000 Subject: [PATCH 02/32] Add target_address abstraction for addresses --- src/object_store.cpp | 1 + src/object_store.hpp | 1 + src/target_address.hpp | 64 ++++++++++++++++++++++++++++++++++++++++++ src/utils.hpp | 7 ----- 4 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 src/target_address.hpp diff --git a/src/object_store.cpp b/src/object_store.cpp index 381237c20..c5a2fbcbb 100644 --- a/src/object_store.cpp +++ b/src/object_store.cpp @@ -10,6 +10,7 @@ #include "ddwaf.h" #include "log.hpp" #include "object_store.hpp" +#include "target_address.hpp" #include "utils.hpp" namespace ddwaf { diff --git a/src/object_store.hpp b/src/object_store.hpp index e783d0ad4..de80f79d2 100644 --- a/src/object_store.hpp +++ b/src/object_store.hpp @@ -13,6 +13,7 @@ #include "context_allocator.hpp" #include "ddwaf.h" +#include "target_address.hpp" #include "utils.hpp" namespace ddwaf { diff --git a/src/target_address.hpp b/src/target_address.hpp new file mode 100644 index 000000000..bf33d62d1 --- /dev/null +++ b/src/target_address.hpp @@ -0,0 +1,64 @@ +// 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 2024 Datadog, Inc. + +#pragma once + +#include "utils.hpp" + +#include +#include +#include + +namespace ddwaf { + +using target_index = std::size_t; + +inline target_index get_target_index(std::string_view address) +{ + return std::hash{}(address); +} + +struct target_address { + explicit target_address(std::string name_) + : name(std::move(name_)), index(get_target_index(name)) + {} + + auto operator<=>(const target_address &o) const { return name <=> o.name; } + bool operator==(const target_address &o) const { return index == o.index && name == o.name; } + + std::string name; + target_index index; +}; + +struct target_address_view { + // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) + target_address_view(const target_address &address) : name(address.name), index(address.index) {} + + auto operator<=>(const target_address_view &o) const { return name <=> o.name; } + auto operator<=>(const target_address &o) const { return name <=> o.name; } + + template + bool operator==(const T &o) const + requires std::is_same_v || std::is_same_v + { + return index == o.index && name == o.name; + } + + std::string_view name; + target_index index; +}; + +} // namespace ddwaf + +namespace std { +template <> struct hash { + size_t operator()(const ddwaf::target_address &addr) const { return addr.index; } +}; + +template <> struct hash { + size_t operator()(const ddwaf::target_address_view &addr) const { return addr.index; } +}; +} // namespace std diff --git a/src/utils.hpp b/src/utils.hpp index 0c53cdad0..a6f7860dd 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -56,13 +56,6 @@ struct object_limits { uint32_t max_transformers_per_address{10}; // can't be overridden for now }; -using target_index = std::size_t; - -inline target_index get_target_index(std::string_view address) -{ - return std::hash{}(address); -} - inline size_t find_string_cutoff(const char *str, size_t length, object_limits limits = {}) { // If the string is shorter than our cap, then fine From 8aa27264fd5d0a421758cec00dbc8996ca79e81c Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:56:19 +0000 Subject: [PATCH 03/32] Add more headers --- src/target_address.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target_address.hpp b/src/target_address.hpp index bf33d62d1..fdd163556 100644 --- a/src/target_address.hpp +++ b/src/target_address.hpp @@ -8,7 +8,9 @@ #include "utils.hpp" +#include #include +#include #include #include From 32c3af4f3903b4183ca2b6173cd521fa841bb2f8 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:09:36 +0000 Subject: [PATCH 04/32] Avoid spaceship --- src/target_address.hpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/target_address.hpp b/src/target_address.hpp index fdd163556..354c47088 100644 --- a/src/target_address.hpp +++ b/src/target_address.hpp @@ -28,8 +28,9 @@ struct target_address { : name(std::move(name_)), index(get_target_index(name)) {} - auto operator<=>(const target_address &o) const { return name <=> o.name; } - bool operator==(const target_address &o) const { return index == o.index && name == o.name; } + bool operator<(const target_address &o) const { return name < o.name; } + bool operator>(const target_address &o) const { return name > o.name; } + bool operator==(const target_address &o) const { return name == o.name; } std::string name; target_index index; @@ -39,15 +40,9 @@ struct target_address_view { // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) target_address_view(const target_address &address) : name(address.name), index(address.index) {} - auto operator<=>(const target_address_view &o) const { return name <=> o.name; } - auto operator<=>(const target_address &o) const { return name <=> o.name; } - - template - bool operator==(const T &o) const - requires std::is_same_v || std::is_same_v - { - return index == o.index && name == o.name; - } + bool operator<(const target_address_view &o) const { return name < o.name; } + bool operator>(const target_address_view &o) const { return name > o.name; } + bool operator==(const target_address_view &o) const { return name == o.name; } std::string_view name; target_index index; From e0b5e3728833c9e510e465054a3d1fe2623c877f Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:45:30 +0000 Subject: [PATCH 05/32] Fixes --- src/builder/processor_builder.cpp | 2 +- src/context.cpp | 1 + src/log.hpp | 17 +++--- src/object_store.cpp | 1 - src/parser/exclusion_parser.cpp | 1 + src/parser/expression_parser.cpp | 1 + src/parser/parser_v1.cpp | 1 + src/parser/processor_parser.cpp | 1 + src/sha256.cpp | 42 ++++++------- tests/common/base_utils.hpp | 36 ++++++------ tests/common/gtest_utils.hpp | 42 ++++++------- tests/unit/parser_transformers_test.cpp | 4 +- tests/unit/transformer/manager_test.cpp | 22 +++---- tests/unit/transformer/transformer_utils.hpp | 62 ++++++++++---------- tests/unit/utils_test.cpp | 8 +-- validator/assert.hpp | 14 ++--- 16 files changed, 130 insertions(+), 125 deletions(-) diff --git a/src/builder/processor_builder.cpp b/src/builder/processor_builder.cpp index de6e71ae4..c5f61a026 100644 --- a/src/builder/processor_builder.cpp +++ b/src/builder/processor_builder.cpp @@ -86,7 +86,7 @@ concept has_build_with_scanners = requires(typed_processor_builder b, Spec spec, Scanners scanners) { { b.build(spec, scanners) - } -> std::same_as>; + } -> std::same_as>; }; template diff --git a/src/context.cpp b/src/context.cpp index f66c57bde..a4f0ddb52 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -18,6 +18,7 @@ #include "log.hpp" #include "object_store.hpp" #include "processor/base.hpp" +#include "target_address.hpp" #include "utils.hpp" namespace ddwaf { diff --git a/src/log.hpp b/src/log.hpp index 133a9e586..2543c8f33 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -49,16 +49,17 @@ constexpr const char *base_name(const char *path) } # define DDWAF_LOG_HELPER(level, function, file, line, fmt_str, ...) \ - { \ - if (ddwaf::logger::valid(level)) { \ - constexpr const char *filename = base_name(file); \ - auto message = ddwaf::fmt::format(fmt_str, ##__VA_ARGS__); \ - ddwaf::logger::log(level, function, filename, line, message.c_str(), message.size()); \ - } \ - } + { \ + if (ddwaf::logger::valid(level)) { \ + constexpr const char *filename = base_name(file); \ + auto message = ddwaf::fmt::format(fmt_str, ##__VA_ARGS__); \ + ddwaf::logger::log( \ + level, function, filename, line, message.c_str(), message.size()); \ + } \ + } # define DDWAF_LOG(level, fmt, ...) \ - DDWAF_LOG_HELPER(level, __func__, __FILE__, __LINE__, fmt, ##__VA_ARGS__) + DDWAF_LOG_HELPER(level, __func__, __FILE__, __LINE__, fmt, ##__VA_ARGS__) #endif #if DDWAF_COMPILE_LOG_LEVEL <= DDWAF_COMPILE_LOG_TRACE diff --git a/src/object_store.cpp b/src/object_store.cpp index c5a2fbcbb..af759f50d 100644 --- a/src/object_store.cpp +++ b/src/object_store.cpp @@ -11,7 +11,6 @@ #include "log.hpp" #include "object_store.hpp" #include "target_address.hpp" -#include "utils.hpp" namespace ddwaf { diff --git a/src/parser/exclusion_parser.cpp b/src/parser/exclusion_parser.cpp index 7b6db9e2e..c634a2858 100644 --- a/src/parser/exclusion_parser.cpp +++ b/src/parser/exclusion_parser.cpp @@ -21,6 +21,7 @@ #include "parser/parser.hpp" #include "parser/specification.hpp" #include "semver.hpp" +#include "target_address.hpp" #include "utils.hpp" #include "version.hpp" diff --git a/src/parser/expression_parser.cpp b/src/parser/expression_parser.cpp index 1d0faef5e..249989602 100644 --- a/src/parser/expression_parser.cpp +++ b/src/parser/expression_parser.cpp @@ -33,6 +33,7 @@ #include "parser/common.hpp" #include "parser/matcher_parser.hpp" #include "parser/parser.hpp" +#include "target_address.hpp" #include "transformer/base.hpp" #include "utils.hpp" diff --git a/src/parser/parser_v1.cpp b/src/parser/parser_v1.cpp index a7b85f2da..f5264b7b8 100644 --- a/src/parser/parser_v1.cpp +++ b/src/parser/parser_v1.cpp @@ -32,6 +32,7 @@ #include "rule.hpp" #include "ruleset.hpp" #include "ruleset_info.hpp" +#include "target_address.hpp" #include "transformer/base.hpp" #include "utils.hpp" diff --git a/src/parser/processor_parser.cpp b/src/parser/processor_parser.cpp index a720624a8..d76688119 100644 --- a/src/parser/processor_parser.cpp +++ b/src/parser/processor_parser.cpp @@ -21,6 +21,7 @@ #include "processor/extract_schema.hpp" #include "processor/fingerprint.hpp" #include "semver.hpp" +#include "target_address.hpp" #include "utils.hpp" #include "version.hpp" diff --git a/src/sha256.cpp b/src/sha256.cpp index 63e0e1346..128cba194 100644 --- a/src/sha256.cpp +++ b/src/sha256.cpp @@ -36,15 +36,15 @@ namespace { * Idea behind separate cases for pre-defined lengths is to let the * compiler decide if it's appropriate to unroll small loops. */ -#define ROTATE(a, n) (((a) << (n)) | (((a)&0xffffffff) >> (32 - (n)))) +#define ROTATE(a, n) (((a) << (n)) | (((a) & 0xffffffff) >> (32 - (n)))) #define CHAR_TO_UINT32(c, l) \ - { \ - (l) = ((static_cast(*((c)++))) << 24); \ - (l) |= ((static_cast(*((c)++))) << 16); \ - (l) |= ((static_cast(*((c)++))) << 8); \ - (l) |= ((static_cast(*((c)++)))); \ - } + { \ + (l) = ((static_cast(*((c)++))) << 24); \ + (l) |= ((static_cast(*((c)++))) << 16); \ + (l) |= ((static_cast(*((c)++))) << 8); \ + (l) |= ((static_cast(*((c)++)))); \ + } #define UINT8_TO_HEX_CHAR(u) static_cast((u) < 10 ? (u) + '0' : (u)-10 + 'a') @@ -61,22 +61,22 @@ namespace { #define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define ROUND_00_15(i, a, b, c, d, e, f, g, h) \ - { \ - T1 += (h) + Sigma1(e) + Ch(e, f, g) + K256[i]; \ - (h) = Sigma0(a) + Maj(a, b, c); \ - (d) += T1; \ - (h) += T1; \ - } + { \ + T1 += (h) + Sigma1(e) + Ch(e, f, g) + K256[i]; \ + (h) = Sigma0(a) + Maj(a, b, c); \ + (d) += T1; \ + (h) += T1; \ + } #define ROUND_16_63(i, a, b, c, d, e, f, g, h, X) \ - { \ - s0 = (X)[((i) + 1) & 0x0f]; \ - s0 = sigma0(s0); \ - s1 = (X)[((i) + 14) & 0x0f]; \ - s1 = sigma1(s1); \ - T1 = (X)[(i)&0x0f] += s0 + s1 + (X)[((i) + 9) & 0x0f]; \ - ROUND_00_15(i, a, b, c, d, e, f, g, h); \ - } + { \ + s0 = (X)[((i) + 1) & 0x0f]; \ + s0 = sigma0(s0); \ + s1 = (X)[((i) + 14) & 0x0f]; \ + s1 = sigma1(s1); \ + T1 = (X)[(i) & 0x0f] += s0 + s1 + (X)[((i) + 9) & 0x0f]; \ + ROUND_00_15(i, a, b, c, d, e, f, g, h); \ + } // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/tests/common/base_utils.hpp b/tests/common/base_utils.hpp index 4ad0c6444..4dbaaa8bb 100644 --- a/tests/common/base_utils.hpp +++ b/tests/common/base_utils.hpp @@ -15,29 +15,29 @@ #define SHORT_TIME 1 #define DDWAF_OBJECT_INVALID \ - { \ - NULL, 0, {NULL}, 0, DDWAF_OBJ_INVALID \ - } + { \ + NULL, 0, {NULL}, 0, DDWAF_OBJ_INVALID \ + } #define DDWAF_OBJECT_MAP \ - { \ - NULL, 0, {NULL}, 0, DDWAF_OBJ_MAP \ - } + { \ + NULL, 0, {NULL}, 0, DDWAF_OBJ_MAP \ + } #define DDWAF_OBJECT_ARRAY \ - { \ - NULL, 0, {NULL}, 0, DDWAF_OBJ_ARRAY \ - } + { \ + NULL, 0, {NULL}, 0, DDWAF_OBJ_ARRAY \ + } #define DDWAF_OBJECT_SIGNED_FORCE(value) \ - { \ - NULL, 0, {(const char *)value}, 0, DDWAF_OBJ_SIGNED \ - } + { \ + NULL, 0, {(const char *)value}, 0, DDWAF_OBJ_SIGNED \ + } #define DDWAF_OBJECT_UNSIGNED_FORCE(value) \ - { \ - NULL, 0, {(const char *)value}, 0, DDWAF_OBJ_UNSIGNED \ - } + { \ + NULL, 0, {(const char *)value}, 0, DDWAF_OBJ_UNSIGNED \ + } #define DDWAF_OBJECT_STRING_PTR(string, length) \ - { \ - NULL, 0, {string}, length, DDWAF_OBJ_STRING \ - } + { \ + NULL, 0, {string}, length, DDWAF_OBJ_STRING \ + } namespace ddwaf::test { diff --git a/tests/common/gtest_utils.hpp b/tests/common/gtest_utils.hpp index f78720ac2..b0705813a 100644 --- a/tests/common/gtest_utils.hpp +++ b/tests/common/gtest_utils.hpp @@ -170,31 +170,31 @@ std::list from_matches( // NOLINTBEGIN(cppcoreguidelines-macro-usage) #define EXPECT_EVENTS(result, ...) \ - { \ - auto data = ddwaf::test::object_to_json(result.events); \ - EXPECT_TRUE(ValidateSchema(data)); \ - YAML::Node doc = YAML::Load(data.c_str()); \ - auto events = doc.as>(); \ - EXPECT_THAT(events, WithEvents({__VA_ARGS__})); \ - } + { \ + auto data = ddwaf::test::object_to_json(result.events); \ + EXPECT_TRUE(ValidateSchema(data)); \ + YAML::Node doc = YAML::Load(data.c_str()); \ + auto events = doc.as>(); \ + EXPECT_THAT(events, WithEvents({__VA_ARGS__})); \ + } #define EXPECT_MATCHES(matches, ...) EXPECT_THAT(from_matches(matches), WithMatches({__VA_ARGS__})); #define EXPECT_SCHEMA_EQ(obtained, expected) \ - { \ - auto obtained_doc = test::object_to_rapidjson(obtained); \ - EXPECT_TRUE(ValidateSchemaSchema(obtained_doc)); \ - rapidjson::Document expected_doc; \ - expected_doc.Parse(expected); \ - EXPECT_FALSE(expected_doc.HasParseError()); \ - EXPECT_TRUE(json_equals(obtained_doc, expected_doc)) << test::object_to_json(obtained); \ - } + { \ + auto obtained_doc = test::object_to_rapidjson(obtained); \ + EXPECT_TRUE(ValidateSchemaSchema(obtained_doc)); \ + rapidjson::Document expected_doc; \ + expected_doc.Parse(expected); \ + EXPECT_FALSE(expected_doc.HasParseError()); \ + EXPECT_TRUE(json_equals(obtained_doc, expected_doc)) << test::object_to_json(obtained); \ + } #define EXPECT_ACTIONS(result, ...) \ - { \ - auto data = ddwaf::test::object_to_json(result.actions); \ - YAML::Node doc = YAML::Load(data.c_str()); \ - auto obtained = doc.as(); \ - EXPECT_THAT(obtained, WithActions(__VA_ARGS__)); \ - } + { \ + auto data = ddwaf::test::object_to_json(result.actions); \ + YAML::Node doc = YAML::Load(data.c_str()); \ + auto obtained = doc.as(); \ + EXPECT_THAT(obtained, WithActions(__VA_ARGS__)); \ + } // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/tests/unit/parser_transformers_test.cpp b/tests/unit/parser_transformers_test.cpp index 1d56ca414..4b27e509d 100644 --- a/tests/unit/parser_transformers_test.cpp +++ b/tests/unit/parser_transformers_test.cpp @@ -13,8 +13,8 @@ using namespace ddwaf::parser; // NOLINTBEGIN(cppcoreguidelines-macro-usage,bugprone-unchecked-optional-access) #define EXPECT_OPTEQ(opt, expected) \ - ASSERT_TRUE((opt)); \ - EXPECT_EQ((opt), expected); + ASSERT_TRUE((opt)); \ + EXPECT_EQ((opt), expected); // NOLINTEND(cppcoreguidelines-macro-usage,bugprone-unchecked-optional-access) namespace { diff --git a/tests/unit/transformer/manager_test.cpp b/tests/unit/transformer/manager_test.cpp index 082bdd738..a53a99769 100644 --- a/tests/unit/transformer/manager_test.cpp +++ b/tests/unit/transformer/manager_test.cpp @@ -15,20 +15,20 @@ namespace { // NOLINTNEXTLINE #define EXPECT_TRANSFORM(src, dst, ...) \ - { \ - auto res = transform({src, sizeof(src) - 1}, {__VA_ARGS__}); \ - EXPECT_TRUE(res); \ - if (res) { \ - EXPECT_STR(res.value(), dst); \ - } \ - } + { \ + auto res = transform({src, sizeof(src) - 1}, {__VA_ARGS__}); \ + EXPECT_TRUE(res); \ + if (res) { \ + EXPECT_STR(res.value(), dst); \ + } \ + } // NOLINTNEXTLINE #define EXPECT_NO_TRANSFORM(src, ...) \ - { \ - auto res = transform({src, sizeof(src) - 1}, {__VA_ARGS__}); \ - EXPECT_FALSE(res); \ - } + { \ + auto res = transform({src, sizeof(src) - 1}, {__VA_ARGS__}); \ + EXPECT_FALSE(res); \ + } std::optional transform(std::string_view input, const std::vector &ids) { diff --git a/tests/unit/transformer/transformer_utils.hpp b/tests/unit/transformer/transformer_utils.hpp index 985dbc33a..1ba2048de 100644 --- a/tests/unit/transformer/transformer_utils.hpp +++ b/tests/unit/transformer/transformer_utils.hpp @@ -29,41 +29,41 @@ template constexpr std::array literal_to_array(cons // NOLINTBEGIN(cppcoreguidelines-macro-usage) #define EXPECT_EVENTS(result, ...) \ - { \ - auto data = ddwaf::test::object_to_json(result.events); \ - EXPECT_TRUE(ValidateSchema(data)); \ - YAML::Node doc = YAML::Load(data.c_str()); \ - auto events = doc.as>(); \ - EXPECT_ACTIONS(result, __VA_ARGS__) \ - } + { \ + auto data = ddwaf::test::object_to_json(result.events); \ + EXPECT_TRUE(ValidateSchema(data)); \ + YAML::Node doc = YAML::Load(data.c_str()); \ + auto events = doc.as>(); \ + EXPECT_ACTIONS(result, __VA_ARGS__) \ + } #define EXPECT_TRANSFORM(name, source, expected) \ - { \ { \ - cow_string str(std::string_view{source, sizeof(source) - 1}); \ - EXPECT_TRUE(transformer::name::transform(str)); \ - EXPECT_STREQ(str.data(), expected); \ - } \ - if constexpr (sizeof(source) > 1) { \ - std::array copy{literal_to_array(source)}; \ - cow_string str(std::string_view{copy.data(), copy.size()}); \ - EXPECT_TRUE(transformer::name::transform(str)) << "Non nul-terminated string"; \ - EXPECT_STREQ(str.data(), expected) << "Non nul-terminated string"; \ - } \ - } + { \ + cow_string str(std::string_view{source, sizeof(source) - 1}); \ + EXPECT_TRUE(transformer::name::transform(str)); \ + EXPECT_STREQ(str.data(), expected); \ + } \ + if constexpr (sizeof(source) > 1) { \ + std::array copy{literal_to_array(source)}; \ + cow_string str(std::string_view{copy.data(), copy.size()}); \ + EXPECT_TRUE(transformer::name::transform(str)) << "Non nul-terminated string"; \ + EXPECT_STREQ(str.data(), expected) << "Non nul-terminated string"; \ + } \ + } #define EXPECT_NO_TRANSFORM(name, source) \ - { \ { \ - cow_string str(std::string_view{source, sizeof(source) - 1}); \ - EXPECT_FALSE(transformer::name::transform(str)); \ - EXPECT_FALSE(str.modified()); \ - } \ - if constexpr (sizeof(source) > 1) { \ - std::array copy{literal_to_array(source)}; \ - cow_string str(std::string_view{copy.data(), copy.size()}); \ - EXPECT_FALSE(transformer::name::transform(str)) << "Non nul-terminated string"; \ - EXPECT_FALSE(str.modified()); \ - } \ - } + { \ + cow_string str(std::string_view{source, sizeof(source) - 1}); \ + EXPECT_FALSE(transformer::name::transform(str)); \ + EXPECT_FALSE(str.modified()); \ + } \ + if constexpr (sizeof(source) > 1) { \ + std::array copy{literal_to_array(source)}; \ + cow_string str(std::string_view{copy.data(), copy.size()}); \ + EXPECT_FALSE(transformer::name::transform(str)) << "Non nul-terminated string"; \ + EXPECT_FALSE(str.modified()); \ + } \ + } // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/tests/unit/utils_test.cpp b/tests/unit/utils_test.cpp index 8fc09d0b3..aa1d0b77e 100644 --- a/tests/unit/utils_test.cpp +++ b/tests/unit/utils_test.cpp @@ -351,10 +351,10 @@ TEST(TestUtils, CloneMap) } #define EXPECT_VEC(expected, ...) \ - { \ - std::vector vec{__VA_ARGS__}; \ - EXPECT_EQ(expected, vec); \ - } + { \ + std::vector vec{__VA_ARGS__}; \ + EXPECT_EQ(expected, vec); \ + } TEST(TestUtils, Split) { diff --git a/validator/assert.hpp b/validator/assert.hpp index 5cf5479b0..d2e22cf00 100644 --- a/validator/assert.hpp +++ b/validator/assert.hpp @@ -15,13 +15,13 @@ #include "utils.hpp" #define expect(lhs, rhs) \ - try { \ - assert(lhs, rhs, __LINE__, __func__); \ - } catch (const assert_exception &e) { \ - throw; \ - } catch (const std::exception &e) { \ - throw assert_exception(e.what(), __LINE__, __func__); \ - } + try { \ + assert(lhs, rhs, __LINE__, __func__); \ + } catch (const assert_exception &e) { \ + throw; \ + } catch (const std::exception &e) { \ + throw assert_exception(e.what(), __LINE__, __func__); \ + } class assert_exception : public std::exception { public: From bb22c566d398cf66c70541a45dab6f155b31e0b1 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:49:16 +0000 Subject: [PATCH 06/32] Minor change --- src/rule.hpp | 12 +++++++++--- src/ruleset_builder.cpp | 42 +++++++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/rule.hpp b/src/rule.hpp index 712adc87a..8c900588b 100644 --- a/src/rule.hpp +++ b/src/rule.hpp @@ -12,6 +12,7 @@ #include #include +#include "action_mapper.hpp" #include "clock.hpp" #include "event.hpp" #include "exclusion/common.hpp" @@ -34,9 +35,11 @@ class core_rule { core_rule(std::string id, std::string name, std::unordered_map tags, std::shared_ptr expr, std::vector actions = {}, - bool enabled = true, source_type source = source_type::base) - : enabled_(enabled), source_(source), id_(std::move(id)), name_(std::move(name)), - tags_(std::move(tags)), actions_(std::move(actions)), expr_(std::move(expr)) + bool enabled = true, source_type source = source_type::base, + action_type blocking_mode = action_type::monitor) + : enabled_(enabled), source_(source), blocking_mode_(blocking_mode), id_(std::move(id)), + name_(std::move(name)), tags_(std::move(tags)), actions_(std::move(actions)), + expr_(std::move(expr)) { if (!expr_) { throw std::invalid_argument("rule constructed with null expression"); @@ -114,6 +117,8 @@ class core_rule { const std::vector &get_actions() const { return actions_; } void set_actions(std::vector new_actions) { actions_ = std::move(new_actions); } + void set_blocking_mode(action_type blocking_mode) { blocking_mode_ = blocking_mode; } + action_type get_blocking_mode() const { return blocking_mode_; } void get_addresses(std::unordered_map &addresses) const { return expr_->get_addresses(addresses); @@ -123,6 +128,7 @@ class core_rule { // General metadata bool enabled_{true}; source_type source_; + action_type blocking_mode_{action_type::monitor}; std::string id_; std::string name_; std::unordered_map tags_; diff --git a/src/ruleset_builder.cpp b/src/ruleset_builder.cpp index a17e70318..a8e291560 100644 --- a/src/ruleset_builder.cpp +++ b/src/ruleset_builder.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,24 @@ std::set references_to_rules( return rule_refs; } +action_type obtain_blocking_mode( + const action_mapper &mapper, const std::vector &rule_actions) +{ + action_type mode = action_type::monitor; + for (const auto &action : rule_actions) { + auto it = mapper.find(action); + if (it == mapper.end()) { + continue; + } + + auto action_mode = it->second.type; + if (is_blocking_action(action_mode) && mode < action_mode) { + mode = action_mode; + } + } + return mode; +} + } // namespace std::shared_ptr ruleset_builder::build(parameter::map &root, base_ruleset_info &info) @@ -82,9 +101,12 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules return {}; } - constexpr static change_state base_rule_update = change_state::rules | change_state::overrides; + constexpr static change_state base_rule_update = + change_state::rules | change_state::overrides | change_state::actions; + constexpr static change_state custom_rule_update = + change_state::custom_rules | change_state::actions; constexpr static change_state filters_update = - base_rule_update | change_state::custom_rules | change_state::filters; + base_rule_update | custom_rule_update | change_state::filters; constexpr static change_state processors_update = change_state::processors | change_state::scanners; // When a configuration with 'rules' or 'rules_override' is received, we @@ -135,23 +157,27 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules } } - // Remove any disabled rules + // Update blocking mode and remove any disabled rules for (auto it = final_base_rules_.begin(); it != final_base_rules_.end();) { if (!(*it)->is_enabled()) { it = final_base_rules_.erase(it); - } else { - ++it; + continue; } + + auto mode = obtain_blocking_mode(*actions_, (*it)->get_actions()); + (*it)->set_blocking_mode(mode); + + ++it; } } - if ((state & change_state::custom_rules) != change_state::none) { + if ((state & custom_rule_update) != change_state::none) { final_user_rules_.clear(); - // Initially, new rules are generated from their spec for (const auto &[id, spec] : user_rules_) { + auto mode = obtain_blocking_mode(*actions_, spec.actions); auto rule_ptr = std::make_shared( - id, spec.name, spec.tags, spec.expr, spec.actions, spec.enabled, spec.source); + id, spec.name, spec.tags, spec.expr, spec.actions, spec.enabled, spec.source, mode); if (!rule_ptr->is_enabled()) { // Skip disabled rules continue; From ba4907ae71a7e793286dda1c2acfc0f94b6eb53a Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Tue, 19 Nov 2024 22:14:52 +0000 Subject: [PATCH 07/32] Add modules and builder --- cmake/objects.cmake | 1 + src/builder/module_builder.hpp | 72 +++++++++++++++ src/clock.hpp | 3 +- src/module.cpp | 126 ++++++++++++++++++++++++++ src/module.hpp | 161 +++++++++++++++++++++++++++++++++ src/ruleset_builder.cpp | 2 + 6 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 src/builder/module_builder.hpp create mode 100644 src/module.cpp create mode 100644 src/module.hpp diff --git a/cmake/objects.cmake b/cmake/objects.cmake index 30674a7cf..40321ea9d 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/builder/module_builder.hpp b/src/builder/module_builder.hpp new file mode 100644 index 000000000..4bc7af084 --- /dev/null +++ b/src/builder/module_builder.hpp @@ -0,0 +1,72 @@ +// 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 "module.hpp" +#include "rule.hpp" + +namespace ddwaf { + +struct user_rule_precedence { + bool operator()(const core_rule *left, const core_rule *right) const + { + const auto left_mode = left->get_blocking_mode(); + const auto right_mode = right->get_blocking_mode(); + + const auto left_source = left->get_source(); + const auto right_source = right->get_source(); + + return (left_mode > right_mode) || (left_mode == right_mode && left_source > right_source); + } +}; + +struct base_rule_precedence { + bool operator()(const core_rule *left, const core_rule *right) const + { + const auto left_mode = left->get_blocking_mode(); + const auto right_mode = right->get_blocking_mode(); + + const auto left_source = left->get_source(); + const auto right_source = right->get_source(); + + return (left_mode > right_mode) || (left_mode == right_mode && left_source < right_source); + } +}; + +struct rule_collection_precedence { + bool operator()(const core_rule *left, const core_rule *right) const + { + const auto left_type = left->get_type(); + const auto right_type = right->get_type(); + + const auto type_cmp = left_type.compare(right_type); + + const auto left_mode = left->get_blocking_mode(); + const auto right_mode = right->get_blocking_mode(); + + return type_cmp < 0 || + (type_cmp == 0 && + (left_mode > right_mode || + (left_mode == right_mode && left->get_source() > right->get_source()))); + } +}; + +template class module_builder { +public: + void insert(core_rule *rule) { rules_.emplace_back(rule); } + + T build() + { + std::sort(rules_.begin(), rules_.end(), PrecedenceOrder{}); + return T{std::move(rules_)}; + } + +protected: + std::vector rules_; +}; + +} // namespace ddwaf diff --git a/src/clock.hpp b/src/clock.hpp index 4fcaa21d1..3c9bbf528 100644 --- a/src/clock.hpp +++ b/src/clock.hpp @@ -62,8 +62,9 @@ class timer { monotonic_clock::time_point start_; monotonic_clock::time_point end_; - const uint32_t syscall_period_; + uint32_t syscall_period_; uint32_t calls_{1}; bool expired_{false}; }; + } // namespace ddwaf diff --git a/src/module.cpp b/src/module.cpp new file mode 100644 index 000000000..4fdfbd078 --- /dev/null +++ b/src/module.cpp @@ -0,0 +1,126 @@ +// 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 "exception.hpp" +#include + +namespace ddwaf { + +namespace { +std::optional eval_rule(const core_rule &rule, const object_store &store, + core_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 rule_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 +{ + for (std::size_t i = 0; i < rules_.size(); ++i) { + const auto &rule = *rules_[i]; + auto &rule_cache = cache.rules[i]; + + auto event = eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, deadline); + if (event.has_value()) { + events.emplace_back(std::move(*event)); + DDWAF_DEBUG("Found event on rule {}", rule.get_id()); + break; + } + } +} + +void rule_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 +{ + // TODO Optimize by separating rule collections + for (std::size_t i = 0; i < rules_.size(); ++i) { + auto *rule = rules_[i]; + const auto collection_name = rule->get_type(); + auto &collection_cache = cache.collections[collection_name]; + if (collection_cache.type >= rule->get_blocking_mode()) { + // 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 == rule->get_blocking_mode() && collection_cache.ephemeral) { + collection_cache.type = action_type::none; + collection_cache.ephemeral = false; + } else { + // Skip all rules with the same type + while (++i < rules_.size() && collection_name == rules_[i]->get_type()) {} + continue; + } + } + + for (; i < rules_.size() && collection_name == rules_[i]->get_type(); ++i) { + rule = rules_[i]; + auto &rule_cache = cache.rules[i]; + auto event = + eval_rule(*rules_[i], store, rule_cache, exclusion, dynamic_matchers, deadline); + if (event.has_value()) { + collection_cache.type = rule->get_blocking_mode(); + 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..1d7f348e2 --- /dev/null +++ b/src/module.hpp @@ -0,0 +1,161 @@ +// 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 "rule.hpp" + +#include + +// Modules: +// - Network-ACL: +// - Order: +// blocking +// non-blocking +// - Short-circuit: rule match +// - No timing +// - Authentication-ACL: +// - Order: +// blocking +// non-blocking +// - Short-circuit: rule match +// - No timing +// - Custom-ACL: +// - Order: +// blocking user rules +// blocking datadog rules +// non-blocking user +// non-blocking datadog rules +// - Short-circuit: rule match +// - Timed +// - Configuration: +// - Order: +// blocking user rules +// blocking datadog rules +// non-blocking user +// non-blocking datadog rules +// - Short-circuit: rule match +// - Timed +// - Business logic: +// - Order: +// blocking user rules +// blocking datadog rules +// non-blocking user +// non-blocking datadog rules +// - Short-circuit: rule match +// - Timed +// - RASP: +// - Order: +// blocking datadog rules +// blocking user rules +// non-blocking datadog rules +// non-blocking user +// - Short-circuit: rule match +// - Timed +// - WAF: +// - Order: +// blocking datadog rules +// blocking user rules +// non-blocking datadog rules +// non-blocking user +// - Short-circuit: rule match, but only rules of the same type (collections) +// - Timed + +namespace ddwaf { + +struct rule_collection_cache { + action_type type{action_type::none}; + bool ephemeral{false}; +}; + +struct module_cache { + memory::unordered_map collections; + memory::vector rules; + + //[[nodiscard]] bool empty() const { return collections.empty(); } +}; + +class base_module { +public: + base_module() = default; + virtual ~base_module() = default; + base_module(const base_module &) = default; + base_module(base_module &&) noexcept = default; + base_module &operator=(const base_module &) = default; + base_module &operator=(base_module &&) noexcept = default; + + virtual void init_cache(module_cache &cache) const = 0; + + virtual 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 = 0; +}; + +class rule_module : public base_module { +public: + using cache_type = module_cache; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + + rule_module() = default; + ~rule_module() override = default; + rule_module(const rule_module &) = default; + rule_module(rule_module &&) noexcept = default; + rule_module &operator=(const rule_module &) = default; + rule_module &operator=(rule_module &&) noexcept = default; + + void init_cache(module_cache &cache) const override { cache.rules.resize(rules_.size()); } + + 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 override; + +protected: + explicit rule_module(std::vector &&rules) : rules_(std::move(rules)) {} + + std::vector rules_; + + template friend class module_builder; +}; + +class rule_collection_module : public base_module { +public: + using cache_type = module_cache; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + + rule_collection_module() = default; + ~rule_collection_module() override = default; + rule_collection_module(const rule_collection_module &) = default; + rule_collection_module(rule_collection_module &&) noexcept = default; + rule_collection_module &operator=(const rule_collection_module &) = default; + rule_collection_module &operator=(rule_collection_module &&) noexcept = default; + + void init_cache(module_cache &cache) const override { cache.rules.resize(rules_.size()); } + + 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 override; + +protected: + struct rule_collection { + std::string_view name; + std::vector rules; + }; + + explicit rule_collection_module(std::vector &&rules) : rules_(std::move(rules)) {} + + std::vector rules_; + + template friend class module_builder; +}; + +} // namespace ddwaf diff --git a/src/ruleset_builder.cpp b/src/ruleset_builder.cpp index a8e291560..e144d7588 100644 --- a/src/ruleset_builder.cpp +++ b/src/ruleset_builder.cpp @@ -14,11 +14,13 @@ #include #include "action_mapper.hpp" +#include "builder/module_builder.hpp" #include "exception.hpp" #include "exclusion/input_filter.hpp" #include "exclusion/rule_filter.hpp" #include "indexer.hpp" #include "log.hpp" +#include "module.hpp" #include "parameter.hpp" #include "parser/common.hpp" #include "parser/parser.hpp" From 792265a072396d31516016cb93f0a67cb905da74 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:10:22 +0000 Subject: [PATCH 08/32] Builder and other related changes --- cmake/objects.cmake | 1 + src/builder/module_builder.cpp | 82 ++++++++++++++++++ src/builder/module_builder.hpp | 154 +++++++++++++++++++++++---------- src/clock.hpp | 11 ++- src/module.cpp | 71 ++++++++------- src/module.hpp | 134 +++++----------------------- src/module_category.hpp | 49 +++++++++++ src/rule.hpp | 21 +++-- src/ruleset_builder.cpp | 15 ++-- 9 files changed, 327 insertions(+), 211 deletions(-) create mode 100644 src/builder/module_builder.cpp create mode 100644 src/module_category.hpp diff --git a/cmake/objects.cmake b/cmake/objects.cmake index 40321ea9d..52ac02f47 100644 --- a/cmake/objects.cmake +++ b/cmake/objects.cmake @@ -23,6 +23,7 @@ set(LIBDDWAF_SOURCE ${libddwaf_SOURCE_DIR}/src/sha256.cpp ${libddwaf_SOURCE_DIR}/src/uuid.cpp ${libddwaf_SOURCE_DIR}/src/action_mapper.cpp + ${libddwaf_SOURCE_DIR}/src/builder/module_builder.cpp ${libddwaf_SOURCE_DIR}/src/builder/processor_builder.cpp ${libddwaf_SOURCE_DIR}/src/tokenizer/sql_base.cpp ${libddwaf_SOURCE_DIR}/src/tokenizer/pgsql.cpp diff --git a/src/builder/module_builder.cpp b/src/builder/module_builder.cpp new file mode 100644 index 000000000..7b83dfd9e --- /dev/null +++ b/src/builder/module_builder.cpp @@ -0,0 +1,82 @@ +// 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 "builder/module_builder.hpp" +#include "module.hpp" +#include "module_category.hpp" +#include "rule.hpp" + +namespace ddwaf { + +rule_module rule_module_builder::build() +{ + const auto &sort_fn = [this](const auto *left, const auto *right) { + const auto lverdict = left->get_verdict(); + const auto rverdict = right->get_verdict(); + const auto lsource = left->get_source(); + const auto rsource = right->get_source(); + std::string_view const lkey = grouping_key_fn_(left); + std::string_view const rkey = grouping_key_fn_(right); + return lverdict > rverdict || + (lverdict == rverdict && (source_precedence_fn_(lsource, rsource) || + (lsource == rsource && lkey < rkey))); + }; + + // Sort first + std::sort(rules_.begin(), rules_.end(), sort_fn); + + // Generate collections by grouping based on the grouping key + std::string_view prev_key; + for (std::size_t i = 0; i < rules_.size(); ++i) { + const auto *rule = rules_[i]; + auto cur_key = grouping_key_fn_(rule); + if (cur_key != prev_key) { + if (!collections_.empty()) { + collections_.back().end = i; + } + collections_.emplace_back(cur_key, rule->get_verdict(), i, i + 1); + prev_key = cur_key; + } + } + + if (!collections_.empty()) { + collections_.back().end = rules_.size(); + } + + return rule_module{std::move(rules_), std::move(collections_)}; +} + +std::array rule_module_set_builder::build( + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + const std::vector> &base, + const std::vector> &user) +{ + std::array all_modules; + + const auto inserter = [this](const auto &rules) { + for (const auto &rule : rules) { + auto &builder = builders_[static_cast(rule->get_module())]; + builder.insert(rule.get()); + } + }; + + inserter(base); + inserter(user); + + for (std::size_t i = 0; i < builders_.size(); ++i) { all_modules[i] = builders_[i].build(); } + + return all_modules; +} + +} // namespace ddwaf diff --git a/src/builder/module_builder.hpp b/src/builder/module_builder.hpp index 4bc7af084..3255ec2ac 100644 --- a/src/builder/module_builder.hpp +++ b/src/builder/module_builder.hpp @@ -7,66 +7,132 @@ #pragma once #include "module.hpp" +#include "module_category.hpp" #include "rule.hpp" namespace ddwaf { -struct user_rule_precedence { - bool operator()(const core_rule *left, const core_rule *right) const - { - const auto left_mode = left->get_blocking_mode(); - const auto right_mode = right->get_blocking_mode(); - - const auto left_source = left->get_source(); - const auto right_source = right->get_source(); - - return (left_mode > right_mode) || (left_mode == right_mode && left_source > right_source); - } -}; +// The module builder can be used to build a single module +class rule_module_builder { +public: + using source_type = core_rule::source_type; + using source_precedence_fn_type = bool (*)(source_type left, source_type right); + using grouping_key_fn_type = std::string_view (*)(const core_rule *rule); + + rule_module_builder( + source_precedence_fn_type source_precedence, grouping_key_fn_type grouping_key) + : source_precedence_fn_(source_precedence), grouping_key_fn_(grouping_key) + {} + ~rule_module_builder() = default; + rule_module_builder(rule_module_builder &&) = delete; + rule_module_builder(const rule_module_builder &) = delete; + rule_module_builder &operator=(rule_module_builder &&) = delete; + rule_module_builder &operator=(const rule_module_builder &) = delete; -struct base_rule_precedence { - bool operator()(const core_rule *left, const core_rule *right) const - { - const auto left_mode = left->get_blocking_mode(); - const auto right_mode = right->get_blocking_mode(); + void insert(core_rule *rule) { rules_.emplace_back(rule); } - const auto left_source = left->get_source(); - const auto right_source = right->get_source(); + rule_module build(); - return (left_mode > right_mode) || (left_mode == right_mode && left_source < right_source); - } +protected: + source_precedence_fn_type source_precedence_fn_; + grouping_key_fn_type grouping_key_fn_; + std::vector rules_; + std::vector collections_; }; -struct rule_collection_precedence { - bool operator()(const core_rule *left, const core_rule *right) const - { - const auto left_type = left->get_type(); - const auto right_type = right->get_type(); - - const auto type_cmp = left_type.compare(right_type); +// Modules: +// - Network-ACL: +// - Order: +// blocking +// non-blocking +// - Short-circuit: rule match +// - No timing +// - Authentication-ACL: +// - Order: +// blocking +// non-blocking +// - Short-circuit: rule match +// - No timing +// - Custom-ACL: +// - Order: +// blocking user rules +// blocking datadog rules +// non-blocking user +// non-blocking datadog rules +// - Short-circuit: rule match +// - Timed +// - Configuration: +// - Order: +// blocking user rules +// blocking datadog rules +// non-blocking user +// non-blocking datadog rules +// - Short-circuit: rule match +// - Timed +// - Business logic: +// - Order: +// blocking user rules +// blocking datadog rules +// non-blocking user +// non-blocking datadog rules +// - Short-circuit: rule match +// - Timed +// - RASP: +// - Order: +// blocking datadog rules +// blocking user rules +// non-blocking datadog rules +// non-blocking user +// - Short-circuit: rule match +// - Timed +// - WAF: +// - Order: +// blocking datadog rules +// blocking user rules +// non-blocking datadog rules +// non-blocking user +// - Short-circuit: rule match, but only rules of the same type (collections) +// - Timed + +class rule_module_set_builder { +public: + rule_module_set_builder() = default; + ~rule_module_set_builder() = default; + rule_module_set_builder(rule_module_set_builder &&) = delete; + rule_module_set_builder(const rule_module_set_builder &) = delete; + rule_module_set_builder &operator=(rule_module_set_builder &&) = delete; + rule_module_set_builder &operator=(const rule_module_set_builder &) = delete; - const auto left_mode = left->get_blocking_mode(); - const auto right_mode = right->get_blocking_mode(); + std::array build( + const std::vector> &base, + const std::vector> &user); - return type_cmp < 0 || - (type_cmp == 0 && - (left_mode > right_mode || - (left_mode == right_mode && left->get_source() > right->get_source()))); +protected: + // Helpers + static bool user_rule_precedence( + const core_rule::source_type left, const core_rule::source_type right) + { + return left > right; } -}; -template class module_builder { -public: - void insert(core_rule *rule) { rules_.emplace_back(rule); } - - T build() + static bool base_rule_precedence( + const core_rule::source_type left, const core_rule::source_type right) { - std::sort(rules_.begin(), rules_.end(), PrecedenceOrder{}); - return T{std::move(rules_)}; + return left < right; } -protected: - std::vector rules_; + static std::string_view type_grouping_key(const core_rule *rule) { return rule->get_type(); } + static constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; } + + std::array builders_{{ + {base_rule_precedence, null_grouping_key}, // Network-ACL + {base_rule_precedence, null_grouping_key}, // Authentication-ACL + {user_rule_precedence, null_grouping_key}, // Custom-ACL + {user_rule_precedence, null_grouping_key}, // Configuration + {user_rule_precedence, null_grouping_key}, // Business logic + {base_rule_precedence, null_grouping_key}, // RASP + {user_rule_precedence, type_grouping_key}, // WAF + }}; }; } // namespace ddwaf diff --git a/src/clock.hpp b/src/clock.hpp index 3c9bbf528..39e598c0e 100644 --- a/src/clock.hpp +++ b/src/clock.hpp @@ -35,7 +35,8 @@ class timer { // WAF calls expired() quite often, otherwise another solution would be // required to minimise syscalls. explicit timer(std::chrono::microseconds exp, uint32_t syscall_period = default_syscall_period) - : start_(monotonic_clock::now()), end_(start_ + exp), syscall_period_(syscall_period) + : start_(monotonic_clock::now()), end_(add_saturated(start_, exp)), + syscall_period_(syscall_period) {} bool expired() @@ -58,6 +59,14 @@ class timer { } protected: + static monotonic_clock::time_point add_saturated( + monotonic_clock::time_point augend, std::chrono::microseconds addend) + { + return (addend > (monotonic_clock::time_point::max() - augend)) + ? monotonic_clock::time_point::max() + : augend + addend; + } + constexpr static uint32_t default_syscall_period{16}; monotonic_clock::time_point start_; diff --git a/src/module.cpp b/src/module.cpp index 4fdfbd078..d00056b10 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -63,64 +63,61 @@ std::optional eval_rule(const core_rule &rule, const object_store &store, } // namespace -void rule_module::eval(std::vector &events, object_store &store, module_cache &cache, - const exclusion::context_policy &exclusion, +void rule_module::eval_with_collections(std::vector &events, object_store &store, + cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) const { - for (std::size_t i = 0; i < rules_.size(); ++i) { - const auto &rule = *rules_[i]; - auto &rule_cache = cache.rules[i]; - - auto event = eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, deadline); - if (event.has_value()) { - events.emplace_back(std::move(*event)); - DDWAF_DEBUG("Found event on rule {}", rule.get_id()); - break; - } - } -} - -void rule_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 -{ - // TODO Optimize by separating rule collections - for (std::size_t i = 0; i < rules_.size(); ++i) { - auto *rule = rules_[i]; - const auto collection_name = rule->get_type(); - auto &collection_cache = cache.collections[collection_name]; - if (collection_cache.type >= rule->get_blocking_mode()) { + 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 // just a workaround taking advantage of the order of evaluation of // collections. Collections might be removed in the future altogether. - if (collection_cache.type == rule->get_blocking_mode() && collection_cache.ephemeral) { - collection_cache.type = action_type::none; + if (collection_cache.type == collection.type && collection_cache.ephemeral) { + collection_cache.type = core_rule::verdict_type::none; collection_cache.ephemeral = false; } else { - // Skip all rules with the same type - while (++i < rules_.size() && collection_name == rules_[i]->get_type()) {} continue; } } - for (; i < rules_.size() && collection_name == rules_[i]->get_type(); ++i) { - rule = rules_[i]; - auto &rule_cache = cache.rules[i]; + for (std::size_t i = collection.begin; i < collection.end; ++i) { + auto &rule = *rules_[i]; auto event = - eval_rule(*rules_[i], store, rule_cache, exclusion, dynamic_matchers, deadline); + eval_rule(rule, store, cache.rules[i], exclusion, dynamic_matchers, deadline); if (event.has_value()) { - collection_cache.type = rule->get_blocking_mode(); + 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()); - + DDWAF_DEBUG("Found event on rule {}", rule.get_id()); break; } } } } +void rule_module::eval(std::vector &events, object_store &store, cache_type &cache, + const exclusion::context_policy &exclusion, + const std::unordered_map> &dynamic_matchers, + ddwaf::timer &deadline) const +{ + if (collections_.empty()) { + for (std::size_t i = 0; i < rules_.size(); ++i) { + const auto &rule = *rules_[i]; + auto &rule_cache = cache.rules[i]; + + auto event = eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, deadline); + if (event.has_value()) { + events.emplace_back(std::move(*event)); + DDWAF_DEBUG("Found event on rule {}", rule.get_id()); + break; + } + } + } else { + eval_with_collections(events, store, cache, exclusion, dynamic_matchers, deadline); + } +} } // namespace ddwaf diff --git a/src/module.hpp b/src/module.hpp index 1d7f348e2..feeba5f56 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -12,150 +12,64 @@ #include -// Modules: -// - Network-ACL: -// - Order: -// blocking -// non-blocking -// - Short-circuit: rule match -// - No timing -// - Authentication-ACL: -// - Order: -// blocking -// non-blocking -// - Short-circuit: rule match -// - No timing -// - Custom-ACL: -// - Order: -// blocking user rules -// blocking datadog rules -// non-blocking user -// non-blocking datadog rules -// - Short-circuit: rule match -// - Timed -// - Configuration: -// - Order: -// blocking user rules -// blocking datadog rules -// non-blocking user -// non-blocking datadog rules -// - Short-circuit: rule match -// - Timed -// - Business logic: -// - Order: -// blocking user rules -// blocking datadog rules -// non-blocking user -// non-blocking datadog rules -// - Short-circuit: rule match -// - Timed -// - RASP: -// - Order: -// blocking datadog rules -// blocking user rules -// non-blocking datadog rules -// non-blocking user -// - Short-circuit: rule match -// - Timed -// - WAF: -// - Order: -// blocking datadog rules -// blocking user rules -// non-blocking datadog rules -// non-blocking user -// - Short-circuit: rule match, but only rules of the same type (collections) -// - Timed - namespace ddwaf { struct rule_collection_cache { - action_type type{action_type::none}; + core_rule::verdict_type type{core_rule::verdict_type::none}; bool ephemeral{false}; }; -struct module_cache { - memory::unordered_map collections; +struct rule_module_cache { memory::vector rules; - - //[[nodiscard]] bool empty() const { return collections.empty(); } -}; - -class base_module { -public: - base_module() = default; - virtual ~base_module() = default; - base_module(const base_module &) = default; - base_module(base_module &&) noexcept = default; - base_module &operator=(const base_module &) = default; - base_module &operator=(base_module &&) noexcept = default; - - virtual void init_cache(module_cache &cache) const = 0; - - virtual 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 = 0; + memory::unordered_map collections; }; -class rule_module : public base_module { +class rule_module { public: - using cache_type = module_cache; + using cache_type = rule_module_cache; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; rule_module() = default; - ~rule_module() override = default; + ~rule_module() = default; rule_module(const rule_module &) = default; rule_module(rule_module &&) noexcept = default; rule_module &operator=(const rule_module &) = default; rule_module &operator=(rule_module &&) noexcept = default; - void init_cache(module_cache &cache) const override { cache.rules.resize(rules_.size()); } + void init_cache(cache_type &cache) const + { + cache.rules.resize(rules_.size()); + cache.collections.reserve(collections_.size()); + } - void eval(std::vector &events, object_store &store, module_cache &cache, + void eval(std::vector &events, object_store &store, cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, - ddwaf::timer &deadline) const override; + ddwaf::timer &deadline) const; protected: - explicit rule_module(std::vector &&rules) : rules_(std::move(rules)) {} - - std::vector rules_; - - template friend class module_builder; -}; - -class rule_collection_module : public base_module { -public: - using cache_type = module_cache; - using iterator = std::vector::iterator; - using const_iterator = std::vector::const_iterator; - - rule_collection_module() = default; - ~rule_collection_module() override = default; - rule_collection_module(const rule_collection_module &) = default; - rule_collection_module(rule_collection_module &&) noexcept = default; - rule_collection_module &operator=(const rule_collection_module &) = default; - rule_collection_module &operator=(rule_collection_module &&) noexcept = default; - - void init_cache(module_cache &cache) const override { cache.rules.resize(rules_.size()); } - - void eval(std::vector &events, object_store &store, module_cache &cache, + void eval_with_collections(std::vector &events, object_store &store, cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, - ddwaf::timer &deadline) const override; + ddwaf::timer &deadline) const; -protected: struct rule_collection { std::string_view name; - std::vector rules; + core_rule::verdict_type type; + std::size_t begin; + std::size_t end; }; - explicit rule_collection_module(std::vector &&rules) : rules_(std::move(rules)) {} + explicit rule_module( + std::vector &&rules, std::vector &&collections) + : rules_(std::move(rules)), collections_(std::move(collections)) + {} std::vector rules_; + std::vector collections_; - template friend class module_builder; + friend class rule_module_builder; }; } // namespace ddwaf diff --git a/src/module_category.hpp b/src/module_category.hpp new file mode 100644 index 000000000..b26d109c6 --- /dev/null +++ b/src/module_category.hpp @@ -0,0 +1,49 @@ +// 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 +#include + +namespace ddwaf { +enum class rule_module_category : uint8_t { + network_acl, + authentication_acl, + custom_acl, + configuration, + business_logic, + rasp, + waf, + count +}; + +constexpr std::size_t rule_module_count = static_cast(rule_module_category::count); + +inline rule_module_category string_to_rule_module_category(std::string_view name) +{ + if (name == "network-acl") { + return rule_module_category::network_acl; + } + if (name == "authentication-acl") { + return rule_module_category::authentication_acl; + } + if (name == "custom-acl") { + return rule_module_category::custom_acl; + } + if (name == "configuration") { + return rule_module_category::configuration; + } + if (name == "business-logic") { + return rule_module_category::business_logic; + } + if (name == "rasp") { + return rule_module_category::rasp; + } + return rule_module_category::waf; +} + +} // namespace ddwaf diff --git a/src/rule.hpp b/src/rule.hpp index 8c900588b..8e9e92c04 100644 --- a/src/rule.hpp +++ b/src/rule.hpp @@ -6,19 +6,17 @@ #pragma once -#include #include #include #include #include -#include "action_mapper.hpp" #include "clock.hpp" #include "event.hpp" #include "exclusion/common.hpp" #include "expression.hpp" -#include "iterator.hpp" #include "matcher/base.hpp" +#include "module_category.hpp" #include "object_store.hpp" namespace ddwaf { @@ -30,14 +28,15 @@ namespace ddwaf { class core_rule { public: enum class source_type : uint8_t { base = 1, user = 2 }; + enum class verdict_type : uint8_t { none = 0, monitor = 1, block = 2 }; using cache_type = expression::cache_type; core_rule(std::string id, std::string name, std::unordered_map tags, std::shared_ptr expr, std::vector actions = {}, bool enabled = true, source_type source = source_type::base, - action_type blocking_mode = action_type::monitor) - : enabled_(enabled), source_(source), blocking_mode_(blocking_mode), id_(std::move(id)), + verdict_type verdict = verdict_type::monitor) + : enabled_(enabled), source_(source), verdict_(verdict), id_(std::move(id)), name_(std::move(name)), tags_(std::move(tags)), actions_(std::move(actions)), expr_(std::move(expr)) { @@ -46,7 +45,7 @@ class core_rule { } // If the tag is not present, the default is `waf` - mod_ = get_tag_or("module", "waf"); + mod_ = string_to_rule_module_category(get_tag_or("module", "waf")); // Type is guaranteed to be present type_ = get_tag("type"); } @@ -85,7 +84,7 @@ class core_rule { std::string_view get_id() const { return id_; } std::string_view get_name() const { return name_; } std::string_view get_type() const { return type_; } - std::string_view get_module() const { return mod_; } + rule_module_category get_module() const { return mod_; } std::string_view get_tag(const std::string &tag) const { @@ -117,8 +116,8 @@ class core_rule { const std::vector &get_actions() const { return actions_; } void set_actions(std::vector new_actions) { actions_ = std::move(new_actions); } - void set_blocking_mode(action_type blocking_mode) { blocking_mode_ = blocking_mode; } - action_type get_blocking_mode() const { return blocking_mode_; } + void set_verdict(verdict_type verdict) { verdict_ = verdict; } + verdict_type get_verdict() const { return verdict_; } void get_addresses(std::unordered_map &addresses) const { return expr_->get_addresses(addresses); @@ -128,7 +127,7 @@ class core_rule { // General metadata bool enabled_{true}; source_type source_; - action_type blocking_mode_{action_type::monitor}; + verdict_type verdict_{verdict_type::monitor}; std::string id_; std::string name_; std::unordered_map tags_; @@ -136,7 +135,7 @@ class core_rule { // Frequently accessed tags std::string type_; - std::string mod_; + rule_module_category mod_; // Tags provided through rules_override std::unordered_map ancillary_tags_; diff --git a/src/ruleset_builder.cpp b/src/ruleset_builder.cpp index e144d7588..e9e956661 100644 --- a/src/ruleset_builder.cpp +++ b/src/ruleset_builder.cpp @@ -74,10 +74,9 @@ std::set references_to_rules( return rule_refs; } -action_type obtain_blocking_mode( +core_rule::verdict_type obtain_rule_verdict( const action_mapper &mapper, const std::vector &rule_actions) { - action_type mode = action_type::monitor; for (const auto &action : rule_actions) { auto it = mapper.find(action); if (it == mapper.end()) { @@ -85,11 +84,11 @@ action_type obtain_blocking_mode( } auto action_mode = it->second.type; - if (is_blocking_action(action_mode) && mode < action_mode) { - mode = action_mode; + if (is_blocking_action(action_mode)) { + return core_rule::verdict_type::block; } } - return mode; + return core_rule::verdict_type::monitor; } } // namespace @@ -166,8 +165,8 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules continue; } - auto mode = obtain_blocking_mode(*actions_, (*it)->get_actions()); - (*it)->set_blocking_mode(mode); + auto mode = obtain_rule_verdict(*actions_, (*it)->get_actions()); + (*it)->set_verdict(mode); ++it; } @@ -177,7 +176,7 @@ std::shared_ptr ruleset_builder::build(parameter::map &root, base_rules final_user_rules_.clear(); // Initially, new rules are generated from their spec for (const auto &[id, spec] : user_rules_) { - auto mode = obtain_blocking_mode(*actions_, spec.actions); + auto mode = obtain_rule_verdict(*actions_, spec.actions); auto rule_ptr = std::make_shared( id, spec.name, spec.tags, spec.expr, spec.actions, spec.enabled, spec.source, mode); if (!rule_ptr->is_enabled()) { From ec2ffa09cd01a79e58934d08ab18254d47e640c2 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:36:41 +0000 Subject: [PATCH 09/32] Fix tests, remove collections, other changes... --- cmake/objects.cmake | 1 - src/collection.cpp | 121 ----- src/collection.hpp | 66 --- src/context.cpp | 37 +- src/context.hpp | 10 +- src/module.cpp | 20 +- src/module.hpp | 3 +- src/parser/parser.hpp | 4 +- src/parser/parser_v1.cpp | 66 +-- src/ruleset.hpp | 48 +- src/ruleset_builder.cpp | 5 +- src/waf.cpp | 4 +- tests/integration/diagnostics/v1/test.cpp | 22 +- tests/unit/collection_test.cpp | 630 ---------------------- tests/unit/context_test.cpp | 417 +++++++------- tests/unit/ruleset_test.cpp | 180 +++---- 16 files changed, 397 insertions(+), 1237 deletions(-) delete mode 100644 src/collection.cpp delete mode 100644 src/collection.hpp delete mode 100644 tests/unit/collection_test.cpp diff --git a/cmake/objects.cmake b/cmake/objects.cmake index 52ac02f47..7916ed66a 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 6b89fc4c7..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(core_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, core_rule::cache_type{}); - it = new_it; - } - - core_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 08044d656..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/context.cpp b/src/context.cpp index a4f0ddb52..978bf6fab 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -4,12 +4,12 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. #include +#include #include #include #include #include "clock.hpp" -#include "collection.hpp" #include "context.hpp" #include "ddwaf.h" #include "event.hpp" @@ -236,37 +236,10 @@ 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); + for (std::size_t i = 0; i < ruleset_->rule_modules.size(); ++i) { + const auto &mod = ruleset_->rule_modules[i]; + auto &cache = rule_module_cache_[i]; + mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline); } return events; diff --git a/src/context.hpp b/src/context.hpp index df6dcc2bf..68fc0e796 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -31,9 +31,13 @@ class context { explicit context(std::shared_ptr ruleset) : ruleset_(std::move(ruleset)) { + processor_cache_.reserve(ruleset_->preprocessors.size() + ruleset_->postprocessors.size()); rule_filter_cache_.reserve(ruleset_->rule_filters.size()); input_filter_cache_.reserve(ruleset_->input_filters.size()); - collection_cache_.reserve(ruleset_->collection_types.size()); + + for (std::size_t i = 0; i < ruleset_->rule_modules.size(); ++i) { + ruleset_->rule_modules[i].init_cache(rule_module_cache_[i]); + } } context(const context &) = delete; @@ -53,7 +57,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 is_first_run() const { return store_.empty(); } bool check_new_rule_targets() const { // NOLINTNEXTLINE(readability-use-anyofallof) @@ -88,7 +92,7 @@ class context { exclusion::context_policy exclusion_policy_; // Cache of collections to avoid processing once a result has been obtained - memory::unordered_map collection_cache_{}; + std::array rule_module_cache_; }; class context_wrapper { diff --git a/src/module.cpp b/src/module.cpp index d00056b10..6c5db16f6 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -4,8 +4,24 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. -#include "exception.hpp" +#include +#include #include +#include +#include +#include +#include +#include +#include + +#include "clock.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 { @@ -88,7 +104,7 @@ void rule_module::eval_with_collections(std::vector &events, object_store auto event = eval_rule(rule, store, cache.rules[i], exclusion, dynamic_matchers, deadline); if (event.has_value()) { - collection_cache.type = collection.type; + collection_cache.type = rule.get_verdict(); collection_cache.ephemeral = event->ephemeral; events.emplace_back(std::move(*event)); diff --git a/src/module.hpp b/src/module.hpp index feeba5f56..a6af41856 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -26,6 +26,7 @@ struct rule_module_cache { class rule_module { public: + using verdict_type = core_rule::verdict_type; using cache_type = rule_module_cache; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; @@ -56,7 +57,7 @@ class rule_module { struct rule_collection { std::string_view name; - core_rule::verdict_type type; + verdict_type type; std::size_t begin; std::size_t end; }; diff --git a/src/parser/parser.hpp b/src/parser/parser.hpp index ac251362e..e4ffa2f49 100644 --- a/src/parser/parser.hpp +++ b/src/parser/parser.hpp @@ -22,12 +22,12 @@ namespace ddwaf::parser { unsigned parse_schema_version(parameter::map &ruleset); - +#ifdef LIBDDWAF_ENABLE_PARSER_V1 namespace v1 { void parse( parameter::map &ruleset, base_ruleset_info &info, ddwaf::ruleset &rs, object_limits limits); } // namespace v1 - +#endif namespace v2 { rule_spec_container parse_rules(parameter::vector &rule_array, base_section_info &info, diff --git a/src/parser/parser_v1.cpp b/src/parser/parser_v1.cpp index f5264b7b8..694d55808 100644 --- a/src/parser/parser_v1.cpp +++ b/src/parser/parser_v1.cpp @@ -4,37 +4,39 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "condition/base.hpp" -#include "condition/scalar_condition.hpp" -#include "ddwaf.h" -#include "exception.hpp" -#include "expression.hpp" -#include "log.hpp" -#include "matcher/base.hpp" -#include "matcher/is_sqli.hpp" -#include "matcher/is_xss.hpp" -#include "matcher/phrase_match.hpp" -#include "matcher/regex_match.hpp" -#include "parameter.hpp" -#include "parser/common.hpp" -#include "parser/parser.hpp" -#include "rule.hpp" -#include "ruleset.hpp" -#include "ruleset_info.hpp" -#include "target_address.hpp" -#include "transformer/base.hpp" -#include "utils.hpp" +#ifdef LIBDDWAF_ENABLE_PARSER_V1 + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# include "condition/base.hpp" +# include "condition/scalar_condition.hpp" +# include "ddwaf.h" +# include "exception.hpp" +# include "expression.hpp" +# include "log.hpp" +# include "matcher/base.hpp" +# include "matcher/is_sqli.hpp" +# include "matcher/is_xss.hpp" +# include "matcher/phrase_match.hpp" +# include "matcher/regex_match.hpp" +# include "parameter.hpp" +# include "parser/common.hpp" +# include "parser/parser.hpp" +# include "rule.hpp" +# include "ruleset.hpp" +# include "ruleset_info.hpp" +# include "target_address.hpp" +# include "transformer/base.hpp" +# include "utils.hpp" namespace ddwaf::parser::v1 { @@ -208,3 +210,5 @@ void parse( } } // namespace ddwaf::parser::v1 + +#endif diff --git a/src/ruleset.hpp b/src/ruleset.hpp index 143c97392..4b2f1f026 100644 --- a/src/ruleset.hpp +++ b/src/ruleset.hpp @@ -11,9 +11,10 @@ #include #include "action_mapper.hpp" -#include "collection.hpp" +#include "builder/module_builder.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 +23,22 @@ namespace ddwaf { struct ruleset { - void insert_rule(const std::shared_ptr &rule) + // OLINTNEXTLINE(bugprone-easily-swappable-parameters) + void insert_rules(const std::vector> &base, + const std::vector> &user) { - 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() == core_rule::source_type::user) { - user_collections[collection].insert(rule); - } else { - base_collections[collection].insert(rule); - } - } else { - if (rule->get_source() == core_rule::source_type::user) { - user_priority_collections[collection].insert(rule); - } else { - base_priority_collections[collection].insert(rule); - } + for (const auto &rule : base) { + rule->get_addresses(rule_addresses); + rules.emplace_back(rule); + } + for (const auto &rule : user) { + rule->get_addresses(rule_addresses); + rules.emplace_back(rule); } - rule->get_addresses(rule_addresses); - } - void insert_rules(const std::vector> &rules_) - { - for (const auto &rule : rules_) { insert_rule(rule); } + // TODO this could be done with rules vector + rule_module_set_builder builder; + rule_modules = builder.build(base, user); } template @@ -171,12 +161,8 @@ struct ruleset { 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; + // Rule modules + std::array rule_modules; std::unordered_map rule_addresses; std::unordered_map filter_addresses; diff --git a/src/ruleset_builder.cpp b/src/ruleset_builder.cpp index e9e956661..c2239a9a8 100644 --- a/src/ruleset_builder.cpp +++ b/src/ruleset_builder.cpp @@ -14,13 +14,11 @@ #include #include "action_mapper.hpp" -#include "builder/module_builder.hpp" #include "exception.hpp" #include "exclusion/input_filter.hpp" #include "exclusion/rule_filter.hpp" #include "indexer.hpp" #include "log.hpp" -#include "module.hpp" #include "parameter.hpp" #include "parser/common.hpp" #include "parser/parser.hpp" @@ -230,8 +228,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/waf.cpp b/src/waf.cpp index a66afd686..d800cd35c 100644 --- a/src/waf.cpp +++ b/src/waf.cpp @@ -8,14 +8,12 @@ #include #include -#include "action_mapper.hpp" #include "ddwaf.h" #include "exception.hpp" #include "log.hpp" #include "obfuscator.hpp" #include "parameter.hpp" #include "parser/parser.hpp" -#include "ruleset.hpp" #include "ruleset_builder.hpp" #include "ruleset_info.hpp" #include "utils.hpp" @@ -39,6 +37,7 @@ waf::waf(ddwaf::parameter input, ddwaf::base_ruleset_info &info, ddwaf::object_l } } +#ifdef LIBDDWAF_ENABLE_PARSER_V1 // Prevent combining version 1 of the ruleset and the builder if (version == 1) { ddwaf::ruleset rs; @@ -50,6 +49,7 @@ waf::waf(ddwaf::parameter input, ddwaf::base_ruleset_info &info, ddwaf::object_l ruleset_ = std::make_shared(std::move(rs)); return; } +#endif if (version == 2) { DDWAF_DEBUG("Parsing ruleset with schema version 2.x"); diff --git a/tests/integration/diagnostics/v1/test.cpp b/tests/integration/diagnostics/v1/test.cpp index 241e40333..adafdc9d4 100644 --- a/tests/integration/diagnostics/v1/test.cpp +++ b/tests/integration/diagnostics/v1/test.cpp @@ -4,9 +4,10 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. -#include "common/gtest_utils.hpp" -#include "parser/common.hpp" -#include "parser/parser.hpp" +#ifdef LIBDDWAF_ENABLE_PARSER_V1 +# include "common/gtest_utils.hpp" +# include "parser/common.hpp" +# include "parser/parser.hpp" using namespace ddwaf; @@ -66,7 +67,7 @@ void run_test(ddwaf_handle handle) ddwaf_context_destroy(context); } -TEST(TestTestDiagnosticsV1Integration, Basic) +TEST(TestDiagnosticsV1Integration, Basic) { auto rule = yaml_to_object( R"({version: '1.1', events: [{id: 1, name: rule1, tags: {type: flow1, category: category1}, conditions: [{operation: match_regex, parameters: {inputs: [arg1], regex: .*}}, {operation: match_regex, parameters: {inputs: [arg2:x], regex: .*}},{operation: match_regex, parameters: {inputs: [arg2:y], regex: .*}}]}]})"); @@ -101,7 +102,7 @@ TEST(TestTestDiagnosticsV1Integration, Basic) ddwaf_destroy(handle); } -TEST(TestTestDiagnosticsV1Integration, TestInvalidRule) +TEST(TestDiagnosticsV1Integration, TestInvalidRule) { auto rule = read_file("invalid_single_v1.yaml", base_dir); ASSERT_TRUE(rule.type != DDWAF_OBJ_INVALID); @@ -139,7 +140,7 @@ TEST(TestTestDiagnosticsV1Integration, TestInvalidRule) ddwaf_object_free(&diagnostics); } -TEST(TestTestDiagnosticsV1Integration, TestMultipleSameInvalidRules) +TEST(TestDiagnosticsV1Integration, TestMultipleSameInvalidRules) { auto rule = read_file("invalid_multiple_same_v1.yaml", base_dir); ASSERT_TRUE(rule.type != DDWAF_OBJ_INVALID); @@ -178,7 +179,7 @@ TEST(TestTestDiagnosticsV1Integration, TestMultipleSameInvalidRules) ddwaf_object_free(&diagnostics); } -TEST(TestTestDiagnosticsV1Integration, TestMultipleDiffInvalidRules) +TEST(TestDiagnosticsV1Integration, TestMultipleDiffInvalidRules) { auto rule = read_file("invalid_multiple_diff_v1.yaml", base_dir); ASSERT_TRUE(rule.type != DDWAF_OBJ_INVALID); @@ -227,7 +228,7 @@ TEST(TestTestDiagnosticsV1Integration, TestMultipleDiffInvalidRules) ddwaf_object_free(&diagnostics); } -TEST(TestTestDiagnosticsV1Integration, TestMultipleMixInvalidRules) +TEST(TestDiagnosticsV1Integration, TestMultipleMixInvalidRules) { auto rule = read_file("invalid_multiple_mix_v1.yaml", base_dir); ASSERT_TRUE(rule.type != DDWAF_OBJ_INVALID); @@ -287,7 +288,7 @@ TEST(TestTestDiagnosticsV1Integration, TestMultipleMixInvalidRules) ddwaf_destroy(handle); } -TEST(TestTestDiagnosticsV1Integration, TestInvalidDuplicate) +TEST(TestDiagnosticsV1Integration, TestInvalidDuplicate) { auto rule = read_file("invalid_duplicate_v1.yaml", base_dir); ASSERT_TRUE(rule.type != DDWAF_OBJ_INVALID); @@ -326,7 +327,7 @@ TEST(TestTestDiagnosticsV1Integration, TestInvalidDuplicate) ddwaf_destroy(handle); } -TEST(TestTestDiagnosticsV1Integration, TestInvalidTooManyTransformers) +TEST(TestDiagnosticsV1Integration, TestInvalidTooManyTransformers) { auto rule = read_file("invalid_too_many_transformers_v1.yaml", base_dir); ASSERT_TRUE(rule.type != DDWAF_OBJ_INVALID); @@ -366,3 +367,4 @@ TEST(TestTestDiagnosticsV1Integration, TestInvalidTooManyTransformers) } } // namespace +#endif diff --git a/tests/unit/collection_test.cpp b/tests/unit/collection_test.cpp deleted file mode 100644 index 9d3192f82..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 diff --git a/tests/unit/context_test.cpp b/tests/unit/context_test.cpp index 1fb2f9930..bc8ed97bb 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,11 @@ 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"})); + rules.back()->set_verdict(core_rule::verdict_type::block); } + ruleset->insert_rules(rules, {}); { ddwaf::test::context ctx(ruleset); @@ -429,6 +430,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 +441,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 +455,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 +506,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 +517,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 +531,11 @@ 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"})); + rules.back()->set_verdict(core_rule::verdict_type::block); } + ruleset->insert_rules(rules, {}); ddwaf::timer deadline{2s}; ddwaf::test::context ctx(ruleset); @@ -602,6 +603,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 +614,9 @@ 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"})); + rules.back()->set_verdict(core_rule::verdict_type::block); } { @@ -628,10 +629,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); @@ -678,104 +679,105 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) } } -TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet) -{ - auto ruleset = test::get_default_ruleset(); - { - 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()); - - ruleset->insert_rule(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"}); - - ruleset->insert_rule(rule); - } - - ddwaf::timer deadline{2s}; - ddwaf::test::context ctx(ruleset); - - { - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); - ctx.insert(root); - - auto events = ctx.eval_rules({}, deadline); - EXPECT_EQ(events.size(), 1); - - auto &event = events[0]; - EXPECT_STREQ(event.rule->get_id().data(), "id1"); - EXPECT_STREQ(event.rule->get_name().data(), "name1"); - EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); - EXPECT_STREQ(event.rule->get_tag("category").data(), "category1"); - EXPECT_TRUE(event.rule->get_actions().empty()); - - auto &match = event.matches[0]; - EXPECT_STREQ(match.args[0].resolved.c_str(), "192.168.0.1"); - EXPECT_STREQ(match.highlights[0].c_str(), "192.168.0.1"); - EXPECT_STREQ(match.operator_name.data(), "ip_match"); - EXPECT_STREQ(match.operator_value.data(), ""); - EXPECT_STREQ(match.args[0].address.data(), "http.client_ip"); - EXPECT_TRUE(match.args[0].key_path.empty()); - } - - { - // An existing match in a collection will not inhibit a match in a - // priority collection. - ddwaf_object root; - ddwaf_object tmp; - ddwaf_object_map(&root); - ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin")); - ctx.insert(root); - - auto events = ctx.eval_rules({}, deadline); - EXPECT_EQ(events.size(), 1); - - auto &event = events[0]; - EXPECT_EQ(events.size(), 1); - EXPECT_STREQ(event.rule->get_id().data(), "id2"); - EXPECT_STREQ(event.rule->get_name().data(), "name2"); - EXPECT_STREQ(event.rule->get_tag("type").data(), "type"); - EXPECT_STREQ(event.rule->get_tag("category").data(), "category2"); - std::vector expected_actions{"redirect"}; - EXPECT_EQ(event.rule->get_actions(), expected_actions); - EXPECT_EQ(event.matches.size(), 1); - - auto &match = event.matches[0]; - EXPECT_STREQ(match.args[0].resolved.c_str(), "admin"); - EXPECT_STREQ(match.highlights[0].c_str(), "admin"); - EXPECT_STREQ(match.operator_name.data(), "exact_match"); - EXPECT_STREQ(match.operator_value.data(), ""); - EXPECT_STREQ(match.args[0].address.data(), "usr.id"); - EXPECT_TRUE(match.args[0].key_path.empty()); - } -} +// TODO: collections don't work like that any longer +/*TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet)*/ +/*{*/ +/*auto ruleset = test::get_default_ruleset();*/ +/*std::vector> rules;*/ +/*{*/ +/*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"}};*/ + +/*rules.emplace_back(*/ +/*std::make_shared("id1", "name1", std::move(tags), builder.build()));*/ +/*}*/ + +/*{*/ +/*test::expression_builder builder(1);*/ +/*builder.start_condition();*/ +/*builder.add_argument();*/ +/*builder.add_target("usr.id");*/ +/*builder.end_condition(std::vector{"admin"});*/ + +/*std::unordered_map tags{*/ +/*{"type", "type"}, {"category", "category2"}};*/ + +/*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);*/ + +/*{*/ +/*ddwaf_object root;*/ +/*ddwaf_object tmp;*/ +/*ddwaf_object_map(&root);*/ +/*ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1"));*/ +/*ctx.insert(root);*/ + +/*auto events = ctx.eval_rules({}, deadline);*/ +/*EXPECT_EQ(events.size(), 1);*/ + +/*auto &event = events[0];*/ +/*EXPECT_STREQ(event.rule->get_id().data(), "id1");*/ +/*EXPECT_STREQ(event.rule->get_name().data(), "name1");*/ +/*EXPECT_STREQ(event.rule->get_tag("type").data(), "type");*/ +/*EXPECT_STREQ(event.rule->get_tag("category").data(), "category1");*/ +/*EXPECT_TRUE(event.rule->get_actions().empty());*/ + +/*auto &match = event.matches[0];*/ +/*EXPECT_STREQ(match.args[0].resolved.c_str(), "192.168.0.1");*/ +/*EXPECT_STREQ(match.highlights[0].c_str(), "192.168.0.1");*/ +/*EXPECT_STREQ(match.operator_name.data(), "ip_match");*/ +/*EXPECT_STREQ(match.operator_value.data(), "");*/ +/*EXPECT_STREQ(match.args[0].address.data(), "http.client_ip");*/ +/*EXPECT_TRUE(match.args[0].key_path.empty());*/ +/*}*/ + +/*{*/ +/*// An existing match in a collection will not inhibit a match in a*/ +/*// priority collection.*/ +/*ddwaf_object root;*/ +/*ddwaf_object tmp;*/ +/*ddwaf_object_map(&root);*/ +/*ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin"));*/ +/*ctx.insert(root);*/ + +/*auto events = ctx.eval_rules({}, deadline);*/ +/*EXPECT_EQ(events.size(), 1);*/ + +/*auto &event = events[0];*/ +/*EXPECT_EQ(events.size(), 1);*/ +/*EXPECT_STREQ(event.rule->get_id().data(), "id2");*/ +/*EXPECT_STREQ(event.rule->get_name().data(), "name2");*/ +/*EXPECT_STREQ(event.rule->get_tag("type").data(), "type");*/ +/*EXPECT_STREQ(event.rule->get_tag("category").data(), "category2");*/ +/*std::vector expected_actions{"redirect"};*/ +/*EXPECT_EQ(event.rule->get_actions(), expected_actions);*/ +/*EXPECT_EQ(event.matches.size(), 1);*/ + +/*auto &match = event.matches[0];*/ +/*EXPECT_STREQ(match.args[0].resolved.c_str(), "admin");*/ +/*EXPECT_STREQ(match.highlights[0].c_str(), "admin");*/ +/*EXPECT_STREQ(match.operator_name.data(), "exact_match");*/ +/*EXPECT_STREQ(match.operator_value.data(), "");*/ +/*EXPECT_STREQ(match.args[0].address.data(), "usr.id");*/ +/*EXPECT_TRUE(match.args[0].key_path.empty());*/ +/*}*/ +/*}*/ TEST(TestContext, MatchMultipleCollectionsSingleRun) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -786,9 +788,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 +802,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 +824,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 +835,9 @@ 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"})); + rules.back()->set_verdict(core_rule::verdict_type::block); } { @@ -849,11 +850,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 +872,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 +883,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 +897,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 +931,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 +942,9 @@ 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"})); + rules.back()->set_verdict(core_rule::verdict_type::block); } { @@ -957,11 +957,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 +1007,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 +1056,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 +1104,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 +1156,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 +1217,8 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralBypassPersistentMonitor) rule = std::make_shared("id", "name", std::move(tags), builder.build()); rule->set_actions({"block"}); - ruleset->insert_rule(rule); + rule->set_verdict(core_rule::verdict_type::block); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1295,7 +1295,8 @@ TEST(TestContext, OverlappingRuleFiltersEphemeralMonitorPersistentBypass) rule = std::make_shared("id", "name", std::move(tags), builder.build()); rule->set_actions({"block"}); - ruleset->insert_rule(rule); + rule->set_verdict(core_rule::verdict_type::block); + ruleset->insert_rules({rule}, {}); } // Generate filter @@ -1369,7 +1370,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 +1417,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 +1465,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 +1538,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); @@ -1647,9 +1646,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); @@ -1734,9 +1732,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); @@ -1826,7 +1823,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 @@ -1872,7 +1869,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 @@ -1919,7 +1916,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}; @@ -1959,7 +1956,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); @@ -2010,7 +2007,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; @@ -2045,7 +2042,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(); @@ -2100,7 +2097,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(); @@ -2150,7 +2147,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(); @@ -2205,7 +2202,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(); @@ -2266,7 +2263,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(); @@ -2334,7 +2331,7 @@ TEST(TestContext, InputFilterWithCondition) auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } { @@ -2423,7 +2420,7 @@ TEST(TestContext, InputFilterWithEphemeralCondition) auto rule = std::make_shared("id", "name", std::move(tags), builder.build()); - ruleset->insert_rule(rule); + ruleset->insert_rules({rule}, {}); } { @@ -2471,6 +2468,7 @@ TEST(TestContext, InputFilterWithEphemeralCondition) TEST(TestContext, InputFilterMultipleRules) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -2481,9 +2479,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())); } { @@ -2496,10 +2493,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(); @@ -2582,6 +2579,7 @@ TEST(TestContext, InputFilterMultipleRules) TEST(TestContext, InputFilterMultipleRulesMultipleFilters) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -2592,9 +2590,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())); } { @@ -2607,10 +2604,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(); @@ -2706,6 +2703,7 @@ TEST(TestContext, InputFilterMultipleRulesMultipleFilters) TEST(TestContext, InputFilterMultipleRulesMultipleFiltersMultipleObjects) { auto ruleset = test::get_default_ruleset(); + std::vector> rules; { test::expression_builder builder(1); builder.start_condition(); @@ -2716,9 +2714,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())); } { @@ -2731,9 +2728,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())); } { @@ -2746,11 +2742,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 b96aea7db..f53cd79d0 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 472c898d158b330c9e5f13d7118d45d17e8c22dc Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:42:19 +0000 Subject: [PATCH 10/32] Fix compilation in clang --- src/builder/module_builder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/builder/module_builder.cpp b/src/builder/module_builder.cpp index 7b83dfd9e..f1df1ce9d 100644 --- a/src/builder/module_builder.cpp +++ b/src/builder/module_builder.cpp @@ -45,7 +45,8 @@ rule_module rule_module_builder::build() if (!collections_.empty()) { collections_.back().end = i; } - collections_.emplace_back(cur_key, rule->get_verdict(), i, i + 1); + using rule_collection = rule_module::rule_collection; + collections_.emplace_back(rule_collection{cur_key, rule->get_verdict(), i, i + 1}); prev_key = cur_key; } } From 74980d074f2824814bc56d4fdba3c3c2bb7673be Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sat, 23 Nov 2024 22:39:48 +0000 Subject: [PATCH 11/32] Short-circuit modules --- src/context.cpp | 7 +++++- src/exclusion/rule_filter.hpp | 2 +- src/module.cpp | 44 +++++++++++++++++++++++------------ src/module.hpp | 6 ++--- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/context.cpp b/src/context.cpp index 978bf6fab..ff6359dea 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -16,8 +16,10 @@ #include "exception.hpp" #include "exclusion/common.hpp" #include "log.hpp" +#include "module.hpp" #include "object_store.hpp" #include "processor/base.hpp" +#include "rule.hpp" #include "target_address.hpp" #include "utils.hpp" @@ -239,7 +241,10 @@ std::vector context::eval_rules( for (std::size_t i = 0; i < ruleset_->rule_modules.size(); ++i) { const auto &mod = ruleset_->rule_modules[i]; auto &cache = rule_module_cache_[i]; - mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline); + auto verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline); + if (verdict == rule_module::verdict_type::block) { + break; + } } return events; diff --git a/src/exclusion/rule_filter.hpp b/src/exclusion/rule_filter.hpp index 5eacf53b6..12952ae7d 100644 --- a/src/exclusion/rule_filter.hpp +++ b/src/exclusion/rule_filter.hpp @@ -55,7 +55,7 @@ class rule_filter { std::shared_ptr expr_; std::unordered_set rule_targets_; filter_mode mode_; - std::string action_{}; + std::string action_; }; } // namespace ddwaf::exclusion diff --git a/src/module.cpp b/src/module.cpp index 6c5db16f6..e770e46d7 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -26,12 +26,16 @@ namespace ddwaf { namespace { -std::optional eval_rule(const core_rule &rule, const object_store &store, - core_rule::cache_type &cache, const exclusion::context_policy &policy, +using verdict_type = rule_module::verdict_type; + +std::pair, verdict_type> eval_rule(const core_rule &rule, + const object_store &store, core_rule::cache_type &cache, + const exclusion::context_policy &policy, const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) { const auto &id = rule.get_id(); + auto verdict = rule.get_verdict(); if (deadline.expired()) { DDWAF_INFO("Ran out of time while evaluating rule '{}'", id); @@ -40,21 +44,23 @@ std::optional eval_rule(const core_rule &rule, const object_store &store, if (!rule.is_enabled()) { DDWAF_DEBUG("Rule '{}' is disabled", id); - return std::nullopt; + return {std::nullopt, verdict_type::none}; } 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; + return {std::nullopt, verdict_type::none}; } if (exclusion.mode == exclusion::filter_mode::monitor) { - DDWAF_DEBUG("Monitoring rule '{}'", id); action_override = "monitor"; + verdict = verdict_type::monitor; + DDWAF_DEBUG("Monitoring rule '{}'", id); } else if (exclusion.mode == exclusion::filter_mode::custom) { action_override = exclusion.action_override; + verdict = verdict_type::block; DDWAF_DEBUG("Evaluating rule '{}' with custom action '{}'", id, action_override); } else { DDWAF_DEBUG("Evaluating rule '{}'", id); @@ -68,22 +74,23 @@ std::optional eval_rule(const core_rule &rule, const object_store &store, event->action_override = action_override; } - return event; + return {event, verdict}; } catch (const ddwaf::timeout_exception &) { DDWAF_INFO("Ran out of time while evaluating rule '{}'", id); throw; } - return std::nullopt; + return {std::nullopt, verdict_type::none}; } } // namespace -void rule_module::eval_with_collections(std::vector &events, object_store &store, +verdict_type rule_module::eval_with_collections(std::vector &events, object_store &store, cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) const { + verdict_type final_verdict = verdict_type::none; for (const auto &collection : collections_) { DDWAF_DEBUG("Evaluating collection: {}", collection.name); auto &collection_cache = cache.collections[collection.name]; @@ -101,21 +108,26 @@ void rule_module::eval_with_collections(std::vector &events, object_store for (std::size_t i = collection.begin; i < collection.end; ++i) { auto &rule = *rules_[i]; - auto event = + auto [event, verdict] = eval_rule(rule, store, cache.rules[i], exclusion, dynamic_matchers, deadline); if (event.has_value()) { - collection_cache.type = rule.get_verdict(); + collection_cache.type = verdict; collection_cache.ephemeral = event->ephemeral; events.emplace_back(std::move(*event)); DDWAF_DEBUG("Found event on rule {}", rule.get_id()); + + if (verdict > final_verdict) { + final_verdict = verdict; + } break; } } } + return final_verdict; } -void rule_module::eval(std::vector &events, object_store &store, cache_type &cache, +verdict_type rule_module::eval(std::vector &events, object_store &store, cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) const @@ -125,15 +137,17 @@ void rule_module::eval(std::vector &events, object_store &store, cache_ty const auto &rule = *rules_[i]; auto &rule_cache = cache.rules[i]; - auto event = eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, deadline); + auto [event, verdict] = + eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, deadline); if (event.has_value()) { events.emplace_back(std::move(*event)); DDWAF_DEBUG("Found event on rule {}", rule.get_id()); - break; + return verdict; } } - } else { - eval_with_collections(events, store, cache, exclusion, dynamic_matchers, deadline); + return verdict_type::none; } + + return eval_with_collections(events, store, cache, exclusion, dynamic_matchers, deadline); } } // namespace ddwaf diff --git a/src/module.hpp b/src/module.hpp index a6af41856..c2b33eba3 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -44,14 +44,14 @@ class rule_module { cache.collections.reserve(collections_.size()); } - void eval(std::vector &events, object_store &store, cache_type &cache, + verdict_type eval(std::vector &events, object_store &store, cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) const; protected: - void eval_with_collections(std::vector &events, object_store &store, cache_type &cache, - const exclusion::context_policy &exclusion, + verdict_type eval_with_collections(std::vector &events, object_store &store, + cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) const; From 017c76d3a394c11ce043d828074e80d737521829 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sat, 23 Nov 2024 23:08:05 +0000 Subject: [PATCH 12/32] Prevent network and authentication ACLs from expiration --- src/builder/module_builder.cpp | 2 +- src/builder/module_builder.hpp | 83 ++++++++-------------------------- src/clock.hpp | 5 ++ src/context.cpp | 11 ++++- src/module.hpp | 15 ++++-- 5 files changed, 47 insertions(+), 69 deletions(-) diff --git a/src/builder/module_builder.cpp b/src/builder/module_builder.cpp index f1df1ce9d..7803bf81b 100644 --- a/src/builder/module_builder.cpp +++ b/src/builder/module_builder.cpp @@ -55,7 +55,7 @@ rule_module rule_module_builder::build() collections_.back().end = rules_.size(); } - return rule_module{std::move(rules_), std::move(collections_)}; + return rule_module{std::move(rules_), std::move(collections_), policy_}; } std::array rule_module_set_builder::build( diff --git a/src/builder/module_builder.hpp b/src/builder/module_builder.hpp index 3255ec2ac..4513e13c3 100644 --- a/src/builder/module_builder.hpp +++ b/src/builder/module_builder.hpp @@ -18,10 +18,11 @@ class rule_module_builder { using source_type = core_rule::source_type; using source_precedence_fn_type = bool (*)(source_type left, source_type right); using grouping_key_fn_type = std::string_view (*)(const core_rule *rule); + using expiration_policy = rule_module::expiration_policy; - rule_module_builder( - source_precedence_fn_type source_precedence, grouping_key_fn_type grouping_key) - : source_precedence_fn_(source_precedence), grouping_key_fn_(grouping_key) + rule_module_builder(source_precedence_fn_type source_precedence, + grouping_key_fn_type grouping_key, expiration_policy policy = expiration_policy::expiring) + : source_precedence_fn_(source_precedence), grouping_key_fn_(grouping_key), policy_(policy) {} ~rule_module_builder() = default; rule_module_builder(rule_module_builder &&) = delete; @@ -38,62 +39,9 @@ class rule_module_builder { grouping_key_fn_type grouping_key_fn_; std::vector rules_; std::vector collections_; + expiration_policy policy_; }; -// Modules: -// - Network-ACL: -// - Order: -// blocking -// non-blocking -// - Short-circuit: rule match -// - No timing -// - Authentication-ACL: -// - Order: -// blocking -// non-blocking -// - Short-circuit: rule match -// - No timing -// - Custom-ACL: -// - Order: -// blocking user rules -// blocking datadog rules -// non-blocking user -// non-blocking datadog rules -// - Short-circuit: rule match -// - Timed -// - Configuration: -// - Order: -// blocking user rules -// blocking datadog rules -// non-blocking user -// non-blocking datadog rules -// - Short-circuit: rule match -// - Timed -// - Business logic: -// - Order: -// blocking user rules -// blocking datadog rules -// non-blocking user -// non-blocking datadog rules -// - Short-circuit: rule match -// - Timed -// - RASP: -// - Order: -// blocking datadog rules -// blocking user rules -// non-blocking datadog rules -// non-blocking user -// - Short-circuit: rule match -// - Timed -// - WAF: -// - Order: -// blocking datadog rules -// blocking user rules -// non-blocking datadog rules -// non-blocking user -// - Short-circuit: rule match, but only rules of the same type (collections) -// - Timed - class rule_module_set_builder { public: rule_module_set_builder() = default; @@ -125,13 +73,20 @@ class rule_module_set_builder { static constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; } std::array builders_{{ - {base_rule_precedence, null_grouping_key}, // Network-ACL - {base_rule_precedence, null_grouping_key}, // Authentication-ACL - {user_rule_precedence, null_grouping_key}, // Custom-ACL - {user_rule_precedence, null_grouping_key}, // Configuration - {user_rule_precedence, null_grouping_key}, // Business logic - {base_rule_precedence, null_grouping_key}, // RASP - {user_rule_precedence, type_grouping_key}, // WAF + // Network-ACL + {base_rule_precedence, null_grouping_key, rule_module::expiration_policy::non_expiring}, + // Authentication-ACL + {base_rule_precedence, null_grouping_key, rule_module::expiration_policy::non_expiring}, + // Custom-ACL + {user_rule_precedence, null_grouping_key}, + // Configuration + {user_rule_precedence, null_grouping_key}, + // Business logic + {user_rule_precedence, null_grouping_key}, + // RASP + {base_rule_precedence, null_grouping_key}, + // WAF + {user_rule_precedence, type_grouping_key}, }}; }; diff --git a/src/clock.hpp b/src/clock.hpp index 39e598c0e..aeb5efeaf 100644 --- a/src/clock.hpp +++ b/src/clock.hpp @@ -76,4 +76,9 @@ class timer { bool expired_{false}; }; +inline timer endless_timer() +{ + return timer(std::chrono::microseconds{std::numeric_limits::max()}); +} + } // namespace ddwaf diff --git a/src/context.cpp b/src/context.cpp index ff6359dea..2c6ff88a8 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -236,12 +236,21 @@ exclusion::context_policy &context::eval_filters(ddwaf::timer &deadline) std::vector context::eval_rules( const exclusion::context_policy &policy, ddwaf::timer &deadline) { + static auto no_deadline = endless_timer(); + std::vector events; for (std::size_t i = 0; i < ruleset_->rule_modules.size(); ++i) { const auto &mod = ruleset_->rule_modules[i]; auto &cache = rule_module_cache_[i]; - auto verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline); + + rule_module::verdict_type verdict = rule_module::verdict_type::none; + if (mod.may_expire()) { + verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline); + } else { + verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, no_deadline); + } + if (verdict == rule_module::verdict_type::block) { break; } diff --git a/src/module.hpp b/src/module.hpp index c2b33eba3..b632fa1e0 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -31,6 +31,11 @@ class rule_module { using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; + enum class expiration_policy : uint8_t { + expiring, + non_expiring, + }; + rule_module() = default; ~rule_module() = default; rule_module(const rule_module &) = default; @@ -44,6 +49,8 @@ class rule_module { cache.collections.reserve(collections_.size()); } + [[nodiscard]] bool may_expire() const { return policy_ == expiration_policy::expiring; } + verdict_type eval(std::vector &events, object_store &store, cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, @@ -62,13 +69,15 @@ class rule_module { std::size_t end; }; - explicit rule_module( - std::vector &&rules, std::vector &&collections) - : rules_(std::move(rules)), collections_(std::move(collections)) + explicit rule_module(std::vector &&rules, + std::vector &&collections, + expiration_policy policy = expiration_policy::expiring) + : rules_(std::move(rules)), collections_(std::move(collections)), policy_(policy) {} std::vector rules_; std::vector collections_; + expiration_policy policy_{expiration_policy::expiring}; friend class rule_module_builder; }; From ce3ee1f874371bbcb8bf73034b3c77e0d3f938f5 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 24 Nov 2024 14:09:49 +0000 Subject: [PATCH 13/32] Allow non-expiring rules to be evaluated --- src/context.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/context.cpp b/src/context.cpp index 2c6ff88a8..0f9da9c68 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -75,17 +75,6 @@ DDWAF_RET_CODE context::run(optional_ref persistent, return DDWAF_ERR_INVALID_OBJECT; } - // If the timeout provided is 0, we need to ensure the parameters are owned - // by the additive to ensure that the semantics of DDWAF_ERR_TIMEOUT are - // consistent across all possible timeout scenarios. - if (timeout == 0) { - if (res.has_value()) { - ddwaf_result &output = *res; - output.timeout = true; - } - return DDWAF_OK; - } - 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 From 37f9bb44dc479c2a45df87a1ad05644b55ffb0de Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:27:14 +0000 Subject: [PATCH 14/32] Update v1 parser, change clock, other small changes --- src/builder/module_builder.cpp | 17 ++--- src/builder/module_builder.hpp | 3 +- src/clock.hpp | 24 ++++--- src/parser/parser.hpp | 4 +- src/parser/parser_v1.cpp | 77 +++++++++++------------ src/ruleset.hpp | 2 +- src/waf.cpp | 4 +- tests/integration/diagnostics/v1/test.cpp | 8 +-- tests/unit/clock_test.cpp | 4 +- 9 files changed, 65 insertions(+), 78 deletions(-) diff --git a/src/builder/module_builder.cpp b/src/builder/module_builder.cpp index 7803bf81b..998367b66 100644 --- a/src/builder/module_builder.cpp +++ b/src/builder/module_builder.cpp @@ -59,21 +59,14 @@ rule_module rule_module_builder::build() } std::array rule_module_set_builder::build( - // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - const std::vector> &base, - const std::vector> &user) + const std::vector> &rules) { std::array all_modules; - const auto inserter = [this](const auto &rules) { - for (const auto &rule : rules) { - auto &builder = builders_[static_cast(rule->get_module())]; - builder.insert(rule.get()); - } - }; - - inserter(base); - inserter(user); + for (const auto &rule : rules) { + auto &builder = builders_[static_cast(rule->get_module())]; + builder.insert(rule.get()); + } for (std::size_t i = 0; i < builders_.size(); ++i) { all_modules[i] = builders_[i].build(); } diff --git a/src/builder/module_builder.hpp b/src/builder/module_builder.hpp index 4513e13c3..d26c70069 100644 --- a/src/builder/module_builder.hpp +++ b/src/builder/module_builder.hpp @@ -52,8 +52,7 @@ class rule_module_set_builder { rule_module_set_builder &operator=(const rule_module_set_builder &) = delete; std::array build( - const std::vector> &base, - const std::vector> &user); + const std::vector> &rules); protected: // Helpers diff --git a/src/clock.hpp b/src/clock.hpp index aeb5efeaf..4ca1e8ff2 100644 --- a/src/clock.hpp +++ b/src/clock.hpp @@ -28,15 +28,14 @@ struct monotonic_clock { }; #endif // __linux__ -class timer { +// Syscall period refers to the number of calls to expired() before +// clock_gettime is called. This approach is only feasible because the +// WAF calls expired() quite often, otherwise another solution would be +// required to minimise syscalls. +template class base_timer { public: - // Syscall period refers to the number of calls to expired() before - // clock_gettime is called. This approach is only feasible because the - // WAF calls expired() quite often, otherwise another solution would be - // required to minimise syscalls. - explicit timer(std::chrono::microseconds exp, uint32_t syscall_period = default_syscall_period) - : start_(monotonic_clock::now()), end_(add_saturated(start_, exp)), - syscall_period_(syscall_period) + explicit base_timer(std::chrono::microseconds exp) + : start_(monotonic_clock::now()), end_(add_saturated(start_, exp)) {} bool expired() @@ -45,7 +44,7 @@ class timer { if (end_ <= monotonic_clock::now()) { expired_ = true; } else { - calls_ = syscall_period_; + calls_ = SyscallPeriod; } } return expired_; @@ -67,18 +66,17 @@ class timer { : augend + addend; } - constexpr static uint32_t default_syscall_period{16}; - monotonic_clock::time_point start_; monotonic_clock::time_point end_; - uint32_t syscall_period_; uint32_t calls_{1}; bool expired_{false}; }; +using timer = base_timer<16>; + inline timer endless_timer() { - return timer(std::chrono::microseconds{std::numeric_limits::max()}); + return timer{std::chrono::microseconds{std::numeric_limits::max()}}; } } // namespace ddwaf diff --git a/src/parser/parser.hpp b/src/parser/parser.hpp index e4ffa2f49..ac251362e 100644 --- a/src/parser/parser.hpp +++ b/src/parser/parser.hpp @@ -22,12 +22,12 @@ namespace ddwaf::parser { unsigned parse_schema_version(parameter::map &ruleset); -#ifdef LIBDDWAF_ENABLE_PARSER_V1 + namespace v1 { void parse( parameter::map &ruleset, base_ruleset_info &info, ddwaf::ruleset &rs, object_limits limits); } // namespace v1 -#endif + namespace v2 { rule_spec_container parse_rules(parameter::vector &rule_array, base_section_info &info, diff --git a/src/parser/parser_v1.cpp b/src/parser/parser_v1.cpp index 694d55808..e4c96938a 100644 --- a/src/parser/parser_v1.cpp +++ b/src/parser/parser_v1.cpp @@ -4,39 +4,37 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. -#ifdef LIBDDWAF_ENABLE_PARSER_V1 - -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include - -# include "condition/base.hpp" -# include "condition/scalar_condition.hpp" -# include "ddwaf.h" -# include "exception.hpp" -# include "expression.hpp" -# include "log.hpp" -# include "matcher/base.hpp" -# include "matcher/is_sqli.hpp" -# include "matcher/is_xss.hpp" -# include "matcher/phrase_match.hpp" -# include "matcher/regex_match.hpp" -# include "parameter.hpp" -# include "parser/common.hpp" -# include "parser/parser.hpp" -# include "rule.hpp" -# include "ruleset.hpp" -# include "ruleset_info.hpp" -# include "target_address.hpp" -# include "transformer/base.hpp" -# include "utils.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "condition/base.hpp" +#include "condition/scalar_condition.hpp" +#include "ddwaf.h" +#include "exception.hpp" +#include "expression.hpp" +#include "log.hpp" +#include "matcher/base.hpp" +#include "matcher/is_sqli.hpp" +#include "matcher/is_xss.hpp" +#include "matcher/phrase_match.hpp" +#include "matcher/regex_match.hpp" +#include "parameter.hpp" +#include "parser/common.hpp" +#include "parser/parser.hpp" +#include "rule.hpp" +#include "ruleset.hpp" +#include "ruleset_info.hpp" +#include "target_address.hpp" +#include "transformer/base.hpp" +#include "utils.hpp" namespace ddwaf::parser::v1 { @@ -125,8 +123,9 @@ std::shared_ptr parse_expression(parameter::vector &conditions_array return std::make_shared(std::move(conditions)); } -void parseRule(parameter::map &rule, base_section_info &info, - std::unordered_set &rule_ids, ddwaf::ruleset &rs, ddwaf::object_limits limits) +void parse_rule(parameter::map &rule, base_section_info &info, + 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()) { @@ -171,7 +170,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()); @@ -190,11 +189,13 @@ void parse( auto §ion = info.add_section("rules"); std::unordered_set rule_ids; + std::vector> rules; for (unsigned i = 0; i < rules_array.size(); ++i) { const auto &rule_param = rules_array[i]; try { auto rule = static_cast(rule_param); - parseRule(rule, section, rule_ids, rs, limits); + parse_rule(rule, section, rule_ids, rules, limits); + rs.insert_rules(rules, {}); } catch (const std::exception &e) { DDWAF_WARN("{}", e.what()); section.add_failed("index:" + to_string(i), e.what()); @@ -210,5 +211,3 @@ void parse( } } // namespace ddwaf::parser::v1 - -#endif diff --git a/src/ruleset.hpp b/src/ruleset.hpp index 4b2f1f026..53f34f1e0 100644 --- a/src/ruleset.hpp +++ b/src/ruleset.hpp @@ -38,7 +38,7 @@ struct ruleset { // TODO this could be done with rules vector rule_module_set_builder builder; - rule_modules = builder.build(base, user); + rule_modules = builder.build(rules); } template diff --git a/src/waf.cpp b/src/waf.cpp index d800cd35c..a66afd686 100644 --- a/src/waf.cpp +++ b/src/waf.cpp @@ -8,12 +8,14 @@ #include #include +#include "action_mapper.hpp" #include "ddwaf.h" #include "exception.hpp" #include "log.hpp" #include "obfuscator.hpp" #include "parameter.hpp" #include "parser/parser.hpp" +#include "ruleset.hpp" #include "ruleset_builder.hpp" #include "ruleset_info.hpp" #include "utils.hpp" @@ -37,7 +39,6 @@ waf::waf(ddwaf::parameter input, ddwaf::base_ruleset_info &info, ddwaf::object_l } } -#ifdef LIBDDWAF_ENABLE_PARSER_V1 // Prevent combining version 1 of the ruleset and the builder if (version == 1) { ddwaf::ruleset rs; @@ -49,7 +50,6 @@ waf::waf(ddwaf::parameter input, ddwaf::base_ruleset_info &info, ddwaf::object_l ruleset_ = std::make_shared(std::move(rs)); return; } -#endif if (version == 2) { DDWAF_DEBUG("Parsing ruleset with schema version 2.x"); diff --git a/tests/integration/diagnostics/v1/test.cpp b/tests/integration/diagnostics/v1/test.cpp index adafdc9d4..a2e504efc 100644 --- a/tests/integration/diagnostics/v1/test.cpp +++ b/tests/integration/diagnostics/v1/test.cpp @@ -4,10 +4,9 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. -#ifdef LIBDDWAF_ENABLE_PARSER_V1 -# include "common/gtest_utils.hpp" -# include "parser/common.hpp" -# include "parser/parser.hpp" +#include "common/gtest_utils.hpp" +#include "parser/common.hpp" +#include "parser/parser.hpp" using namespace ddwaf; @@ -367,4 +366,3 @@ TEST(TestDiagnosticsV1Integration, TestInvalidTooManyTransformers) } } // namespace -#endif diff --git a/tests/unit/clock_test.cpp b/tests/unit/clock_test.cpp index 7fba1ccc8..51b7776e8 100644 --- a/tests/unit/clock_test.cpp +++ b/tests/unit/clock_test.cpp @@ -17,7 +17,7 @@ namespace { TEST(TestTimer, Basic) { - ddwaf::timer deadline{2ms, 1}; + ddwaf::base_timer<1> deadline{2ms}; EXPECT_FALSE(deadline.expired()); std::this_thread::sleep_for(2ms); @@ -34,7 +34,7 @@ TEST(TestTimer, ExpiredFromConstruction) TEST(TestTimer, ValidatePeriod) { - ddwaf::timer deadline{1ms, 5}; + ddwaf::base_timer<5> deadline{1ms}; EXPECT_FALSE(deadline.expired()); std::this_thread::sleep_for(1ms); From 216e0450ead81173524c482ed32c2c4cfa6a07f6 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:37:38 +0000 Subject: [PATCH 15/32] Fix endless timer --- src/clock.hpp | 9 +++------ src/interface.cpp | 8 ++++++++ tests/unit/clock_test.cpp | 10 ++++++++++ tools/waf_runner.cpp | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/clock.hpp b/src/clock.hpp index 4ca1e8ff2..2342496bd 100644 --- a/src/clock.hpp +++ b/src/clock.hpp @@ -34,7 +34,7 @@ struct monotonic_clock { // required to minimise syscalls. template class base_timer { public: - explicit base_timer(std::chrono::microseconds exp) + explicit base_timer(std::chrono::nanoseconds exp) : start_(monotonic_clock::now()), end_(add_saturated(start_, exp)) {} @@ -59,7 +59,7 @@ template class base_timer { protected: static monotonic_clock::time_point add_saturated( - monotonic_clock::time_point augend, std::chrono::microseconds addend) + monotonic_clock::time_point augend, std::chrono::nanoseconds addend) { return (addend > (monotonic_clock::time_point::max() - augend)) ? monotonic_clock::time_point::max() @@ -74,9 +74,6 @@ template class base_timer { using timer = base_timer<16>; -inline timer endless_timer() -{ - return timer{std::chrono::microseconds{std::numeric_limits::max()}}; -} +inline timer endless_timer() { return timer{std::chrono::nanoseconds::max()}; } } // namespace ddwaf diff --git a/src/interface.cpp b/src/interface.cpp index 90467821e..db84024ca 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -4,6 +4,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. +#include #include #include #include @@ -230,6 +231,13 @@ DDWAF_RET_CODE ddwaf_run(ddwaf_context context, ddwaf_object *persistent_data, ephemeral = *ephemeral_data; } + // The timers will actually count nanoseconds, std::chrono doesn't + // deal well with durations being beyond range. + constexpr uint64_t max_timeout_ms = std::chrono::nanoseconds::max().count() / 1000; + if (timeout > max_timeout_ms) { + timeout = max_timeout_ms; + } + return context->run(persistent, ephemeral, res, timeout); } catch (const std::exception &e) { // catch-all to avoid std::terminate diff --git a/tests/unit/clock_test.cpp b/tests/unit/clock_test.cpp index 51b7776e8..d71816ed6 100644 --- a/tests/unit/clock_test.cpp +++ b/tests/unit/clock_test.cpp @@ -46,4 +46,14 @@ TEST(TestTimer, ValidatePeriod) EXPECT_TRUE(deadline.expired()); } +TEST(TestTimer, EndlessTimer) +{ + // Simple sanity check, we can't really test this + auto deadline = ddwaf::endless_timer(); + EXPECT_FALSE(deadline.expired()); + + std::this_thread::sleep_for(1ms); + EXPECT_FALSE(deadline.expired()); +} + } // namespace diff --git a/tools/waf_runner.cpp b/tools/waf_runner.cpp index c14136494..cae97e31a 100644 --- a/tools/waf_runner.cpp +++ b/tools/waf_runner.cpp @@ -126,7 +126,7 @@ int main(int argc, char *argv[]) ddwaf_result ret; auto code = - ddwaf_run(context, &persistent, &ephemeral, &ret, std::numeric_limits::max()); + ddwaf_run(context, &persistent, &ephemeral, &ret, std::numeric_limits::max()); if (code == DDWAF_MATCH && ddwaf_object_size(&ret.events) > 0) { std::stringstream ss; YAML::Emitter out(ss); From 6144b41a2bfcc5928334caa8c858e55852caddcd Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:24:20 +0000 Subject: [PATCH 16/32] Minor change --- src/module_category.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/module_category.hpp b/src/module_category.hpp index b26d109c6..d75d1cf01 100644 --- a/src/module_category.hpp +++ b/src/module_category.hpp @@ -11,17 +11,16 @@ namespace ddwaf { enum class rule_module_category : uint8_t { - network_acl, + network_acl = 0, authentication_acl, custom_acl, configuration, business_logic, rasp, waf, - count }; -constexpr std::size_t rule_module_count = static_cast(rule_module_category::count); +constexpr std::size_t rule_module_count = static_cast(rule_module_category::waf) + 1; inline rule_module_category string_to_rule_module_category(std::string_view name) { From c8a3ff124e563cc215273d39f4cfbb16076d4398 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:39:20 +0000 Subject: [PATCH 17/32] Minor fix --- .gitignore | 1 + src/processor/fingerprint.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index 699ea3a7e..cb82b5acf 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ perf/test_files/breakdown.numbers .vscode *.swp *.swo +*.swn diff --git a/src/processor/fingerprint.cpp b/src/processor/fingerprint.cpp index c6c8d9480..2a5d6dd60 100644 --- a/src/processor/fingerprint.cpp +++ b/src/processor/fingerprint.cpp @@ -586,6 +586,10 @@ std::pair http_header_fingerprint::eval_i const unary_argument &headers, processor_cache & /*cache*/, ddwaf::timer &deadline) const { + if (headers.value->type != DDWAF_OBJ_MAP) { + return {{}, object_store::attribute::none}; + } + std::string known_header_bitset; known_header_bitset.resize(standard_headers_length, '0'); @@ -632,6 +636,10 @@ std::pair http_network_fingerprint::eval_ const unary_argument &headers, processor_cache & /*cache*/, ddwaf::timer &deadline) const { + if (headers.value->type != DDWAF_OBJ_MAP) { + return {{}, object_store::attribute::none}; + } + std::string ip_origin_bitset; ip_origin_bitset.resize(ip_origin_headers_length, '0'); From 5815ee8a84f257d02cbb6733ad4893a5c76fa2cd Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:12:47 +0000 Subject: [PATCH 18/32] Add validator tests to verify rule precedence per module --- validator/runner.cpp | 14 +- validator/runner.hpp | 1 + .../001_rule1_base_rule_match.yaml | 21 +++ .../002_rule1_user_rule_match.yaml | 21 +++ .../003_rule2_blocking_user_rule_match.yaml | 21 +++ .../004_rule2_base_rule_match.yaml | 21 +++ .../005_rule3_blocking_base_rule_match.yaml | 21 +++ .../006_rule3_user_rule_match.yaml | 21 +++ .../007_rule4_blocking_base_rule_match.yaml | 21 +++ .../008_rule4_user_rule_match.yaml | 21 +++ .../modules/authentication-acl/ruleset.yaml | 123 ++++++++++++++++++ .../001_rule1_user_rule_match.yaml | 21 +++ .../002_rule1_base_rule_match.yaml | 21 +++ .../003_rule2_blocking_user_rule_match.yaml | 21 +++ .../004_rule2_base_rule_match.yaml | 21 +++ .../005_rule3_blocking_base_rule_match.yaml | 21 +++ .../006_rule3_user_rule_match.yaml | 21 +++ .../007_rule4_blocking_user_rule_match.yaml | 21 +++ .../008_rule4_base_rule_match.yaml | 21 +++ .../rules/modules/business-logic/ruleset.yaml | 123 ++++++++++++++++++ .../001_rule1_user_rule_match.yaml | 21 +++ .../002_rule1_base_rule_match.yaml | 21 +++ .../003_rule2_blocking_user_rule_match.yaml | 21 +++ .../004_rule2_base_rule_match.yaml | 21 +++ .../005_rule3_blocking_base_rule_match.yaml | 21 +++ .../006_rule3_user_rule_match.yaml | 21 +++ .../007_rule4_blocking_user_rule_match.yaml | 21 +++ .../008_rule4_base_rule_match.yaml | 21 +++ .../rules/modules/configuration/ruleset.yaml | 123 ++++++++++++++++++ .../custom-acl/001_rule1_user_rule_match.yaml | 21 +++ .../custom-acl/002_rule1_base_rule_match.yaml | 21 +++ .../003_rule2_blocking_user_rule_match.yaml | 21 +++ .../custom-acl/004_rule2_base_rule_match.yaml | 21 +++ .../005_rule3_blocking_base_rule_match.yaml | 21 +++ .../custom-acl/006_rule3_user_rule_match.yaml | 21 +++ .../007_rule4_blocking_user_rule_match.yaml | 21 +++ .../custom-acl/008_rule4_base_rule_match.yaml | 21 +++ .../rules/modules/custom-acl/ruleset.yaml | 123 ++++++++++++++++++ .../001_rule1_base_rule_match.yaml | 21 +++ .../002_rule1_user_rule_match.yaml | 21 +++ .../003_rule2_blocking_user_rule_match.yaml | 21 +++ .../004_rule2_base_rule_match.yaml | 21 +++ .../005_rule3_blocking_base_rule_match.yaml | 21 +++ .../006_rule3_user_rule_match.yaml | 21 +++ .../007_rule4_blocking_base_rule_match.yaml | 21 +++ .../008_rule4_user_rule_match.yaml | 21 +++ .../rules/modules/network-acl/ruleset.yaml | 123 ++++++++++++++++++ .../rasp/001_rule1_base_rule_match.yaml | 21 +++ .../rasp/002_rule1_user_rule_match.yaml | 21 +++ .../003_rule2_blocking_user_rule_match.yaml | 21 +++ .../rasp/004_rule2_base_rule_match.yaml | 21 +++ .../005_rule3_blocking_base_rule_match.yaml | 21 +++ .../rasp/006_rule3_user_rule_match.yaml | 21 +++ .../007_rule4_blocking_base_rule_match.yaml | 21 +++ .../rasp/008_rule4_user_rule_match.yaml | 21 +++ .../tests/rules/modules/rasp/ruleset.yaml | 123 ++++++++++++++++++ .../waf/001_rule1_user_rule_match.yaml | 21 +++ .../waf/002_rule1_base_rule_match.yaml | 21 +++ .../003_rule2_blocking_user_rule_match.yaml | 21 +++ .../waf/004_rule2_base_rule_match.yaml | 21 +++ .../005_rule3_blocking_base_rule_match.yaml | 21 +++ .../waf/006_rule3_user_rule_match.yaml | 21 +++ .../007_rule4_blocking_user_rule_match.yaml | 21 +++ .../waf/008_rule4_base_rule_match.yaml | 21 +++ .../tests/rules/modules/waf/ruleset.yaml | 123 ++++++++++++++++++ 65 files changed, 2051 insertions(+), 1 deletion(-) create mode 100644 validator/tests/rules/modules/authentication-acl/001_rule1_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/authentication-acl/002_rule1_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/authentication-acl/003_rule2_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/authentication-acl/004_rule2_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/authentication-acl/005_rule3_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/authentication-acl/006_rule3_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/authentication-acl/007_rule4_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/authentication-acl/008_rule4_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/authentication-acl/ruleset.yaml create mode 100644 validator/tests/rules/modules/business-logic/001_rule1_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/business-logic/002_rule1_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/business-logic/003_rule2_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/business-logic/004_rule2_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/business-logic/005_rule3_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/business-logic/006_rule3_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/business-logic/007_rule4_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/business-logic/008_rule4_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/business-logic/ruleset.yaml create mode 100644 validator/tests/rules/modules/configuration/001_rule1_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/configuration/002_rule1_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/configuration/003_rule2_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/configuration/004_rule2_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/configuration/005_rule3_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/configuration/006_rule3_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/configuration/007_rule4_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/configuration/008_rule4_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/configuration/ruleset.yaml create mode 100644 validator/tests/rules/modules/custom-acl/001_rule1_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/custom-acl/002_rule1_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/custom-acl/003_rule2_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/custom-acl/004_rule2_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/custom-acl/005_rule3_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/custom-acl/006_rule3_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/custom-acl/007_rule4_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/custom-acl/008_rule4_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/custom-acl/ruleset.yaml create mode 100644 validator/tests/rules/modules/network-acl/001_rule1_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/network-acl/002_rule1_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/network-acl/003_rule2_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/network-acl/004_rule2_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/network-acl/005_rule3_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/network-acl/006_rule3_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/network-acl/007_rule4_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/network-acl/008_rule4_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/network-acl/ruleset.yaml create mode 100644 validator/tests/rules/modules/rasp/001_rule1_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/rasp/002_rule1_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/rasp/003_rule2_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/rasp/004_rule2_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/rasp/005_rule3_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/rasp/006_rule3_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/rasp/007_rule4_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/rasp/008_rule4_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/rasp/ruleset.yaml create mode 100644 validator/tests/rules/modules/waf/001_rule1_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/waf/002_rule1_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/waf/003_rule2_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/waf/004_rule2_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/waf/005_rule3_blocking_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/waf/006_rule3_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/waf/007_rule4_blocking_user_rule_match.yaml create mode 100644 validator/tests/rules/modules/waf/008_rule4_base_rule_match.yaml create mode 100644 validator/tests/rules/modules/waf/ruleset.yaml diff --git a/validator/runner.cpp b/validator/runner.cpp index f9589253c..ce53fe951 100644 --- a/validator/runner.cpp +++ b/validator/runner.cpp @@ -25,6 +25,13 @@ test_runner::test_runner(const std::string &rule_file) auto id = rule_node["id"].as(); rules_[id] = rule_node; } + + auto custom_rules_node = doc["custom_rules"]; + for (auto it = custom_rules_node.begin(); it != custom_rules_node.end(); ++it) { + YAML::Node rule_node = *it; + auto id = rule_node["id"].as(); + custom_rules_[id] = rule_node; + } } test_runner::~test_runner() { ddwaf_destroy(handle_); } @@ -161,7 +168,12 @@ void test_runner::validate(const YAML::Node &expected, const YAML::Node &obtaine seen[j] = true; found_expected = true; - auto rule = rules_[id]; + YAML::Node rule; + if (rules_.contains(id)) { + rule = rules_[id]; + } else { + rule = custom_rules_[id]; + } validate_rule(rule, obtained_rule_match["rule"]); validate_conditions(rule["conditions"], obtained_rule_match["rule_matches"]); validate_matches(expected_rule_match, obtained_rule_match["rule_matches"]); diff --git a/validator/runner.hpp b/validator/runner.hpp index adab6f6b8..b5579d9af 100644 --- a/validator/runner.hpp +++ b/validator/runner.hpp @@ -48,6 +48,7 @@ class test_runner { static constexpr unsigned timeout = 1000000; ddwaf_handle handle_; std::map rules_; + std::map custom_rules_; std::stringstream output_; std::stringstream error_; }; diff --git a/validator/tests/rules/modules/authentication-acl/001_rule1_base_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/001_rule1_base_rule_match.yaml new file mode 100644 index 000000000..2086ca33c --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/001_rule1_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Base rules have precedence in authentication-acl", + runs: [ + { + persistent-input: { + rule1-input: admin + }, + rules: [ + { + 1: [ + { + address: rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/authentication-acl/002_rule1_user_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/002_rule1_user_rule_match.yaml new file mode 100644 index 000000000..31dfe6d08 --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/002_rule1_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that user rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule1-input: admin + }, + rules: [ + { + custom-1: [ + { + address: custom-rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/authentication-acl/003_rule2_blocking_user_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/003_rule2_blocking_user_rule_match.yaml new file mode 100644 index 000000000..10b8f343f --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/003_rule2_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over non-blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule2-input: admin + }, + rules: [ + { + custom-2: [ + { + address: rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/authentication-acl/004_rule2_base_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/004_rule2_base_rule_match.yaml new file mode 100644 index 000000000..af02700b2 --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/004_rule2_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule2-input: admin + }, + rules: [ + { + 2: [ + { + address: base-rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/authentication-acl/005_rule3_blocking_base_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/005_rule3_blocking_base_rule_match.yaml new file mode 100644 index 000000000..303882ba1 --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/005_rule3_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over non-blocking user rules in authentication-acl", + runs: [ + { + persistent-input: { + rule3-input: admin + }, + rules: [ + { + 3: [ + { + address: rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/authentication-acl/006_rule3_user_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/006_rule3_user_rule_match.yaml new file mode 100644 index 000000000..46280d8da --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/006_rule3_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule3-input: admin + }, + rules: [ + { + custom-3: [ + { + address: custom-rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/authentication-acl/007_rule4_blocking_base_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/007_rule4_blocking_base_rule_match.yaml new file mode 100644 index 000000000..dc63f2e91 --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/007_rule4_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over blocking user rules in authentication-acl", + runs: [ + { + persistent-input: { + rule4-input: admin + }, + rules: [ + { + 4: [ + { + address: rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/authentication-acl/008_rule4_user_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/008_rule4_user_rule_match.yaml new file mode 100644 index 000000000..08e4768b9 --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/008_rule4_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that user rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule4-input: admin + }, + rules: [ + { + custom-4: [ + { + address: custom-rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/authentication-acl/ruleset.yaml b/validator/tests/rules/modules/authentication-acl/ruleset.yaml new file mode 100644 index 000000000..79091b8b3 --- /dev/null +++ b/validator/tests/rules/modules/authentication-acl/ruleset.yaml @@ -0,0 +1,123 @@ +version: '2.1' +rules: + - id: "1" + name: rule1-non-blocking + tags: + type: flow1 + category: category + module: authentication-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: base-rule1-input + list: + - "admin" + - id: "2" + name: rule2-non-blocking + tags: + type: flow2 + category: category + module: authentication-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: base-rule2-input + list: + - "admin" + - id: "3" + name: rule3-blocking + tags: + type: flow3 + category: category + module: authentication-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: base-rule3-input + list: + - "admin" + on_match: + - block + - id: "4" + name: rule4-blocking + tags: + type: flow4 + category: category + module: authentication-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: base-rule4-input + list: + - "admin" + on_match: + - block +custom_rules: + - id: "custom-1" + name: custom-rule1-non-blocking + tags: + type: flow1 + category: category + module: authentication-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: custom-rule1-input + list: + - "admin" + - id: "custom-2" + name: custom-rule2-blocking + tags: + type: flow2 + category: category + module: authentication-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: custom-rule2-input + list: + - "admin" + on_match: + - block + - id: "custom-3" + name: custom-rule3-blocking + tags: + type: flow3 + category: category + module: authentication-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: custom-rule3-input + list: + - "admin" + - id: "custom-4" + name: custom-rule4-blocking + tags: + type: flow4 + category: category + module: authentication-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: custom-rule4-input + list: + - "admin" + on_match: + - block diff --git a/validator/tests/rules/modules/business-logic/001_rule1_user_rule_match.yaml b/validator/tests/rules/modules/business-logic/001_rule1_user_rule_match.yaml new file mode 100644 index 000000000..f0a0d6bc7 --- /dev/null +++ b/validator/tests/rules/modules/business-logic/001_rule1_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "User rules have precedence in custom-acl", + runs: [ + { + persistent-input: { + rule1-input: admin + }, + rules: [ + { + custom-1: [ + { + address: rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/business-logic/002_rule1_base_rule_match.yaml b/validator/tests/rules/modules/business-logic/002_rule1_base_rule_match.yaml new file mode 100644 index 000000000..ca5e2f1ff --- /dev/null +++ b/validator/tests/rules/modules/business-logic/002_rule1_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule1-input: admin + }, + rules: [ + { + 1: [ + { + address: base-rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/business-logic/003_rule2_blocking_user_rule_match.yaml b/validator/tests/rules/modules/business-logic/003_rule2_blocking_user_rule_match.yaml new file mode 100644 index 000000000..10b8f343f --- /dev/null +++ b/validator/tests/rules/modules/business-logic/003_rule2_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over non-blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule2-input: admin + }, + rules: [ + { + custom-2: [ + { + address: rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/business-logic/004_rule2_base_rule_match.yaml b/validator/tests/rules/modules/business-logic/004_rule2_base_rule_match.yaml new file mode 100644 index 000000000..af02700b2 --- /dev/null +++ b/validator/tests/rules/modules/business-logic/004_rule2_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule2-input: admin + }, + rules: [ + { + 2: [ + { + address: base-rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/business-logic/005_rule3_blocking_base_rule_match.yaml b/validator/tests/rules/modules/business-logic/005_rule3_blocking_base_rule_match.yaml new file mode 100644 index 000000000..303882ba1 --- /dev/null +++ b/validator/tests/rules/modules/business-logic/005_rule3_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over non-blocking user rules in authentication-acl", + runs: [ + { + persistent-input: { + rule3-input: admin + }, + rules: [ + { + 3: [ + { + address: rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/business-logic/006_rule3_user_rule_match.yaml b/validator/tests/rules/modules/business-logic/006_rule3_user_rule_match.yaml new file mode 100644 index 000000000..46280d8da --- /dev/null +++ b/validator/tests/rules/modules/business-logic/006_rule3_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule3-input: admin + }, + rules: [ + { + custom-3: [ + { + address: custom-rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/business-logic/007_rule4_blocking_user_rule_match.yaml b/validator/tests/rules/modules/business-logic/007_rule4_blocking_user_rule_match.yaml new file mode 100644 index 000000000..d9105eded --- /dev/null +++ b/validator/tests/rules/modules/business-logic/007_rule4_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule4-input: admin + }, + rules: [ + { + custom-4: [ + { + address: rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/business-logic/008_rule4_base_rule_match.yaml b/validator/tests/rules/modules/business-logic/008_rule4_base_rule_match.yaml new file mode 100644 index 000000000..19bed6178 --- /dev/null +++ b/validator/tests/rules/modules/business-logic/008_rule4_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule4-input: admin + }, + rules: [ + { + 4: [ + { + address: base-rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/business-logic/ruleset.yaml b/validator/tests/rules/modules/business-logic/ruleset.yaml new file mode 100644 index 000000000..0ae8ef1c4 --- /dev/null +++ b/validator/tests/rules/modules/business-logic/ruleset.yaml @@ -0,0 +1,123 @@ +version: '2.1' +rules: + - id: "1" + name: rule1-non-blocking + tags: + type: flow1 + category: category + module: business-logic + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: base-rule1-input + list: + - "admin" + - id: "2" + name: rule2-non-blocking + tags: + type: flow2 + category: category + module: business-logic + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: base-rule2-input + list: + - "admin" + - id: "3" + name: rule3-blocking + tags: + type: flow3 + category: category + module: business-logic + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: base-rule3-input + list: + - "admin" + on_match: + - block + - id: "4" + name: rule4-blocking + tags: + type: flow4 + category: category + module: business-logic + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: base-rule4-input + list: + - "admin" + on_match: + - block +custom_rules: + - id: "custom-1" + name: custom-rule1-non-blocking + tags: + type: flow1 + category: category + module: business-logic + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: custom-rule1-input + list: + - "admin" + - id: "custom-2" + name: custom-rule2-blocking + tags: + type: flow2 + category: category + module: business-logic + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: custom-rule2-input + list: + - "admin" + on_match: + - block + - id: "custom-3" + name: custom-rule3-blocking + tags: + type: flow3 + category: category + module: business-logic + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: custom-rule3-input + list: + - "admin" + - id: "custom-4" + name: custom-rule4-blocking + tags: + type: flow4 + category: category + module: business-logic + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: custom-rule4-input + list: + - "admin" + on_match: + - block diff --git a/validator/tests/rules/modules/configuration/001_rule1_user_rule_match.yaml b/validator/tests/rules/modules/configuration/001_rule1_user_rule_match.yaml new file mode 100644 index 000000000..f0a0d6bc7 --- /dev/null +++ b/validator/tests/rules/modules/configuration/001_rule1_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "User rules have precedence in custom-acl", + runs: [ + { + persistent-input: { + rule1-input: admin + }, + rules: [ + { + custom-1: [ + { + address: rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/configuration/002_rule1_base_rule_match.yaml b/validator/tests/rules/modules/configuration/002_rule1_base_rule_match.yaml new file mode 100644 index 000000000..ca5e2f1ff --- /dev/null +++ b/validator/tests/rules/modules/configuration/002_rule1_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule1-input: admin + }, + rules: [ + { + 1: [ + { + address: base-rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/configuration/003_rule2_blocking_user_rule_match.yaml b/validator/tests/rules/modules/configuration/003_rule2_blocking_user_rule_match.yaml new file mode 100644 index 000000000..10b8f343f --- /dev/null +++ b/validator/tests/rules/modules/configuration/003_rule2_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over non-blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule2-input: admin + }, + rules: [ + { + custom-2: [ + { + address: rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/configuration/004_rule2_base_rule_match.yaml b/validator/tests/rules/modules/configuration/004_rule2_base_rule_match.yaml new file mode 100644 index 000000000..af02700b2 --- /dev/null +++ b/validator/tests/rules/modules/configuration/004_rule2_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule2-input: admin + }, + rules: [ + { + 2: [ + { + address: base-rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/configuration/005_rule3_blocking_base_rule_match.yaml b/validator/tests/rules/modules/configuration/005_rule3_blocking_base_rule_match.yaml new file mode 100644 index 000000000..303882ba1 --- /dev/null +++ b/validator/tests/rules/modules/configuration/005_rule3_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over non-blocking user rules in authentication-acl", + runs: [ + { + persistent-input: { + rule3-input: admin + }, + rules: [ + { + 3: [ + { + address: rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/configuration/006_rule3_user_rule_match.yaml b/validator/tests/rules/modules/configuration/006_rule3_user_rule_match.yaml new file mode 100644 index 000000000..46280d8da --- /dev/null +++ b/validator/tests/rules/modules/configuration/006_rule3_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule3-input: admin + }, + rules: [ + { + custom-3: [ + { + address: custom-rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/configuration/007_rule4_blocking_user_rule_match.yaml b/validator/tests/rules/modules/configuration/007_rule4_blocking_user_rule_match.yaml new file mode 100644 index 000000000..d9105eded --- /dev/null +++ b/validator/tests/rules/modules/configuration/007_rule4_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule4-input: admin + }, + rules: [ + { + custom-4: [ + { + address: rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/configuration/008_rule4_base_rule_match.yaml b/validator/tests/rules/modules/configuration/008_rule4_base_rule_match.yaml new file mode 100644 index 000000000..19bed6178 --- /dev/null +++ b/validator/tests/rules/modules/configuration/008_rule4_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule4-input: admin + }, + rules: [ + { + 4: [ + { + address: base-rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/configuration/ruleset.yaml b/validator/tests/rules/modules/configuration/ruleset.yaml new file mode 100644 index 000000000..f9f022943 --- /dev/null +++ b/validator/tests/rules/modules/configuration/ruleset.yaml @@ -0,0 +1,123 @@ +version: '2.1' +rules: + - id: "1" + name: rule1-non-blocking + tags: + type: flow1 + category: category + module: configuration + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: base-rule1-input + list: + - "admin" + - id: "2" + name: rule2-non-blocking + tags: + type: flow2 + category: category + module: configuration + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: base-rule2-input + list: + - "admin" + - id: "3" + name: rule3-blocking + tags: + type: flow3 + category: category + module: configuration + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: base-rule3-input + list: + - "admin" + on_match: + - block + - id: "4" + name: rule4-blocking + tags: + type: flow4 + category: category + module: configuration + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: base-rule4-input + list: + - "admin" + on_match: + - block +custom_rules: + - id: "custom-1" + name: custom-rule1-non-blocking + tags: + type: flow1 + category: category + module: configuration + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: custom-rule1-input + list: + - "admin" + - id: "custom-2" + name: custom-rule2-blocking + tags: + type: flow2 + category: category + module: configuration + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: custom-rule2-input + list: + - "admin" + on_match: + - block + - id: "custom-3" + name: custom-rule3-blocking + tags: + type: flow3 + category: category + module: configuration + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: custom-rule3-input + list: + - "admin" + - id: "custom-4" + name: custom-rule4-blocking + tags: + type: flow4 + category: category + module: configuration + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: custom-rule4-input + list: + - "admin" + on_match: + - block diff --git a/validator/tests/rules/modules/custom-acl/001_rule1_user_rule_match.yaml b/validator/tests/rules/modules/custom-acl/001_rule1_user_rule_match.yaml new file mode 100644 index 000000000..f0a0d6bc7 --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/001_rule1_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "User rules have precedence in custom-acl", + runs: [ + { + persistent-input: { + rule1-input: admin + }, + rules: [ + { + custom-1: [ + { + address: rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/custom-acl/002_rule1_base_rule_match.yaml b/validator/tests/rules/modules/custom-acl/002_rule1_base_rule_match.yaml new file mode 100644 index 000000000..ca5e2f1ff --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/002_rule1_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule1-input: admin + }, + rules: [ + { + 1: [ + { + address: base-rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/custom-acl/003_rule2_blocking_user_rule_match.yaml b/validator/tests/rules/modules/custom-acl/003_rule2_blocking_user_rule_match.yaml new file mode 100644 index 000000000..10b8f343f --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/003_rule2_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over non-blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule2-input: admin + }, + rules: [ + { + custom-2: [ + { + address: rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/custom-acl/004_rule2_base_rule_match.yaml b/validator/tests/rules/modules/custom-acl/004_rule2_base_rule_match.yaml new file mode 100644 index 000000000..af02700b2 --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/004_rule2_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule2-input: admin + }, + rules: [ + { + 2: [ + { + address: base-rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/custom-acl/005_rule3_blocking_base_rule_match.yaml b/validator/tests/rules/modules/custom-acl/005_rule3_blocking_base_rule_match.yaml new file mode 100644 index 000000000..303882ba1 --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/005_rule3_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over non-blocking user rules in authentication-acl", + runs: [ + { + persistent-input: { + rule3-input: admin + }, + rules: [ + { + 3: [ + { + address: rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/custom-acl/006_rule3_user_rule_match.yaml b/validator/tests/rules/modules/custom-acl/006_rule3_user_rule_match.yaml new file mode 100644 index 000000000..46280d8da --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/006_rule3_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule3-input: admin + }, + rules: [ + { + custom-3: [ + { + address: custom-rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/custom-acl/007_rule4_blocking_user_rule_match.yaml b/validator/tests/rules/modules/custom-acl/007_rule4_blocking_user_rule_match.yaml new file mode 100644 index 000000000..d9105eded --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/007_rule4_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule4-input: admin + }, + rules: [ + { + custom-4: [ + { + address: rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/custom-acl/008_rule4_base_rule_match.yaml b/validator/tests/rules/modules/custom-acl/008_rule4_base_rule_match.yaml new file mode 100644 index 000000000..19bed6178 --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/008_rule4_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule4-input: admin + }, + rules: [ + { + 4: [ + { + address: base-rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/custom-acl/ruleset.yaml b/validator/tests/rules/modules/custom-acl/ruleset.yaml new file mode 100644 index 000000000..eea89dc8f --- /dev/null +++ b/validator/tests/rules/modules/custom-acl/ruleset.yaml @@ -0,0 +1,123 @@ +version: '2.1' +rules: + - id: "1" + name: rule1-non-blocking + tags: + type: flow1 + category: category + module: custom-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: base-rule1-input + list: + - "admin" + - id: "2" + name: rule2-non-blocking + tags: + type: flow2 + category: category + module: custom-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: base-rule2-input + list: + - "admin" + - id: "3" + name: rule3-blocking + tags: + type: flow3 + category: category + module: custom-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: base-rule3-input + list: + - "admin" + on_match: + - block + - id: "4" + name: rule4-blocking + tags: + type: flow4 + category: category + module: custom-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: base-rule4-input + list: + - "admin" + on_match: + - block +custom_rules: + - id: "custom-1" + name: custom-rule1-non-blocking + tags: + type: flow1 + category: category + module: custom-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: custom-rule1-input + list: + - "admin" + - id: "custom-2" + name: custom-rule2-blocking + tags: + type: flow2 + category: category + module: custom-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: custom-rule2-input + list: + - "admin" + on_match: + - block + - id: "custom-3" + name: custom-rule3-blocking + tags: + type: flow3 + category: category + module: custom-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: custom-rule3-input + list: + - "admin" + - id: "custom-4" + name: custom-rule4-blocking + tags: + type: flow4 + category: category + module: custom-acl + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: custom-rule4-input + list: + - "admin" + on_match: + - block diff --git a/validator/tests/rules/modules/network-acl/001_rule1_base_rule_match.yaml b/validator/tests/rules/modules/network-acl/001_rule1_base_rule_match.yaml new file mode 100644 index 000000000..40dc539f0 --- /dev/null +++ b/validator/tests/rules/modules/network-acl/001_rule1_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Base rules have precedence in network-acl", + runs: [ + { + persistent-input: { + rule1-input: 192.168.0.1 + }, + rules: [ + { + 1: [ + { + address: rule1-input, + value: 192.168.0.1 + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/network-acl/002_rule1_user_rule_match.yaml b/validator/tests/rules/modules/network-acl/002_rule1_user_rule_match.yaml new file mode 100644 index 000000000..2d8673189 --- /dev/null +++ b/validator/tests/rules/modules/network-acl/002_rule1_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that user rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule1-input: 192.168.0.1 + }, + rules: [ + { + custom-1: [ + { + address: custom-rule1-input, + value: 192.168.0.1 + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/network-acl/003_rule2_blocking_user_rule_match.yaml b/validator/tests/rules/modules/network-acl/003_rule2_blocking_user_rule_match.yaml new file mode 100644 index 000000000..a2a6ca970 --- /dev/null +++ b/validator/tests/rules/modules/network-acl/003_rule2_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over non-blocking base rules in network-acl", + runs: [ + { + persistent-input: { + rule2-input: 192.168.0.1 + }, + rules: [ + { + custom-2: [ + { + address: rule2-input, + value: 192.168.0.1 + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/network-acl/004_rule2_base_rule_match.yaml b/validator/tests/rules/modules/network-acl/004_rule2_base_rule_match.yaml new file mode 100644 index 000000000..0dffe3b30 --- /dev/null +++ b/validator/tests/rules/modules/network-acl/004_rule2_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule2-input: 192.168.0.1 + }, + rules: [ + { + 2: [ + { + address: base-rule2-input, + value: 192.168.0.1 + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/network-acl/005_rule3_blocking_base_rule_match.yaml b/validator/tests/rules/modules/network-acl/005_rule3_blocking_base_rule_match.yaml new file mode 100644 index 000000000..9aca8f149 --- /dev/null +++ b/validator/tests/rules/modules/network-acl/005_rule3_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over non-blocking user rules in network-acl", + runs: [ + { + persistent-input: { + rule3-input: 192.168.0.1 + }, + rules: [ + { + 3: [ + { + address: rule3-input, + value: 192.168.0.1 + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/network-acl/006_rule3_user_rule_match.yaml b/validator/tests/rules/modules/network-acl/006_rule3_user_rule_match.yaml new file mode 100644 index 000000000..6391389af --- /dev/null +++ b/validator/tests/rules/modules/network-acl/006_rule3_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule3-input: 192.168.0.1 + }, + rules: [ + { + custom-3: [ + { + address: custom-rule3-input, + value: 192.168.0.1 + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/network-acl/007_rule4_blocking_base_rule_match.yaml b/validator/tests/rules/modules/network-acl/007_rule4_blocking_base_rule_match.yaml new file mode 100644 index 000000000..1f97f3cdb --- /dev/null +++ b/validator/tests/rules/modules/network-acl/007_rule4_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over blocking user rules in network-acl", + runs: [ + { + persistent-input: { + rule4-input: 192.168.0.1 + }, + rules: [ + { + 4: [ + { + address: rule4-input, + value: 192.168.0.1 + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/network-acl/008_rule4_user_rule_match.yaml b/validator/tests/rules/modules/network-acl/008_rule4_user_rule_match.yaml new file mode 100644 index 000000000..49c4a25ab --- /dev/null +++ b/validator/tests/rules/modules/network-acl/008_rule4_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that user rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule4-input: 192.168.0.1 + }, + rules: [ + { + custom-4: [ + { + address: custom-rule4-input, + value: 192.168.0.1 + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/network-acl/ruleset.yaml b/validator/tests/rules/modules/network-acl/ruleset.yaml new file mode 100644 index 000000000..77ef22c71 --- /dev/null +++ b/validator/tests/rules/modules/network-acl/ruleset.yaml @@ -0,0 +1,123 @@ +version: '2.1' +rules: + - id: "1" + name: rule1-non-blocking + tags: + type: flow1 + category: category + module: network-acl + conditions: + - operator: ip_match + parameters: + inputs: + - address: rule1-input + - address: base-rule1-input + list: + - "192.168.0.1" + - id: "2" + name: rule2-non-blocking + tags: + type: flow2 + category: category + module: network-acl + conditions: + - operator: ip_match + parameters: + inputs: + - address: rule2-input + - address: base-rule2-input + list: + - "192.168.0.1" + - id: "3" + name: rule3-blocking + tags: + type: flow3 + category: category + module: network-acl + conditions: + - operator: ip_match + parameters: + inputs: + - address: rule3-input + - address: base-rule3-input + list: + - "192.168.0.1" + on_match: + - block + - id: "4" + name: rule4-blocking + tags: + type: flow4 + category: category + module: network-acl + conditions: + - operator: ip_match + parameters: + inputs: + - address: rule4-input + - address: base-rule4-input + list: + - "192.168.0.1" + on_match: + - block +custom_rules: + - id: "custom-1" + name: custom-rule1-non-blocking + tags: + type: flow1 + category: category + module: network-acl + conditions: + - operator: ip_match + parameters: + inputs: + - address: rule1-input + - address: custom-rule1-input + list: + - "192.168.0.1" + - id: "custom-2" + name: custom-rule2-blocking + tags: + type: flow2 + category: category + module: network-acl + conditions: + - operator: ip_match + parameters: + inputs: + - address: rule2-input + - address: custom-rule2-input + list: + - "192.168.0.1" + on_match: + - block + - id: "custom-3" + name: custom-rule3-blocking + tags: + type: flow3 + category: category + module: network-acl + conditions: + - operator: ip_match + parameters: + inputs: + - address: rule3-input + - address: custom-rule3-input + list: + - "192.168.0.1" + - id: "custom-4" + name: custom-rule4-blocking + tags: + type: flow4 + category: category + module: network-acl + conditions: + - operator: ip_match + parameters: + inputs: + - address: rule4-input + - address: custom-rule4-input + list: + - "192.168.0.1" + on_match: + - block diff --git a/validator/tests/rules/modules/rasp/001_rule1_base_rule_match.yaml b/validator/tests/rules/modules/rasp/001_rule1_base_rule_match.yaml new file mode 100644 index 000000000..2086ca33c --- /dev/null +++ b/validator/tests/rules/modules/rasp/001_rule1_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Base rules have precedence in authentication-acl", + runs: [ + { + persistent-input: { + rule1-input: admin + }, + rules: [ + { + 1: [ + { + address: rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/rasp/002_rule1_user_rule_match.yaml b/validator/tests/rules/modules/rasp/002_rule1_user_rule_match.yaml new file mode 100644 index 000000000..31dfe6d08 --- /dev/null +++ b/validator/tests/rules/modules/rasp/002_rule1_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that user rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule1-input: admin + }, + rules: [ + { + custom-1: [ + { + address: custom-rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/rasp/003_rule2_blocking_user_rule_match.yaml b/validator/tests/rules/modules/rasp/003_rule2_blocking_user_rule_match.yaml new file mode 100644 index 000000000..10b8f343f --- /dev/null +++ b/validator/tests/rules/modules/rasp/003_rule2_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over non-blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule2-input: admin + }, + rules: [ + { + custom-2: [ + { + address: rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/rasp/004_rule2_base_rule_match.yaml b/validator/tests/rules/modules/rasp/004_rule2_base_rule_match.yaml new file mode 100644 index 000000000..af02700b2 --- /dev/null +++ b/validator/tests/rules/modules/rasp/004_rule2_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule2-input: admin + }, + rules: [ + { + 2: [ + { + address: base-rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/rasp/005_rule3_blocking_base_rule_match.yaml b/validator/tests/rules/modules/rasp/005_rule3_blocking_base_rule_match.yaml new file mode 100644 index 000000000..303882ba1 --- /dev/null +++ b/validator/tests/rules/modules/rasp/005_rule3_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over non-blocking user rules in authentication-acl", + runs: [ + { + persistent-input: { + rule3-input: admin + }, + rules: [ + { + 3: [ + { + address: rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/rasp/006_rule3_user_rule_match.yaml b/validator/tests/rules/modules/rasp/006_rule3_user_rule_match.yaml new file mode 100644 index 000000000..46280d8da --- /dev/null +++ b/validator/tests/rules/modules/rasp/006_rule3_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule3-input: admin + }, + rules: [ + { + custom-3: [ + { + address: custom-rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/rasp/007_rule4_blocking_base_rule_match.yaml b/validator/tests/rules/modules/rasp/007_rule4_blocking_base_rule_match.yaml new file mode 100644 index 000000000..dc63f2e91 --- /dev/null +++ b/validator/tests/rules/modules/rasp/007_rule4_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over blocking user rules in authentication-acl", + runs: [ + { + persistent-input: { + rule4-input: admin + }, + rules: [ + { + 4: [ + { + address: rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/rasp/008_rule4_user_rule_match.yaml b/validator/tests/rules/modules/rasp/008_rule4_user_rule_match.yaml new file mode 100644 index 000000000..08e4768b9 --- /dev/null +++ b/validator/tests/rules/modules/rasp/008_rule4_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that user rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule4-input: admin + }, + rules: [ + { + custom-4: [ + { + address: custom-rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/rasp/ruleset.yaml b/validator/tests/rules/modules/rasp/ruleset.yaml new file mode 100644 index 000000000..6761ff8bc --- /dev/null +++ b/validator/tests/rules/modules/rasp/ruleset.yaml @@ -0,0 +1,123 @@ +version: '2.1' +rules: + - id: "1" + name: rule1-non-blocking + tags: + type: flow1 + category: category + module: rasp + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: base-rule1-input + list: + - "admin" + - id: "2" + name: rule2-non-blocking + tags: + type: flow2 + category: category + module: rasp + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: base-rule2-input + list: + - "admin" + - id: "3" + name: rule3-blocking + tags: + type: flow3 + category: category + module: rasp + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: base-rule3-input + list: + - "admin" + on_match: + - block + - id: "4" + name: rule4-blocking + tags: + type: flow4 + category: category + module: rasp + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: base-rule4-input + list: + - "admin" + on_match: + - block +custom_rules: + - id: "custom-1" + name: custom-rule1-non-blocking + tags: + type: flow1 + category: category + module: rasp + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: custom-rule1-input + list: + - "admin" + - id: "custom-2" + name: custom-rule2-blocking + tags: + type: flow2 + category: category + module: rasp + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: custom-rule2-input + list: + - "admin" + on_match: + - block + - id: "custom-3" + name: custom-rule3-blocking + tags: + type: flow3 + category: category + module: rasp + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: custom-rule3-input + list: + - "admin" + - id: "custom-4" + name: custom-rule4-blocking + tags: + type: flow4 + category: category + module: rasp + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: custom-rule4-input + list: + - "admin" + on_match: + - block diff --git a/validator/tests/rules/modules/waf/001_rule1_user_rule_match.yaml b/validator/tests/rules/modules/waf/001_rule1_user_rule_match.yaml new file mode 100644 index 000000000..f0a0d6bc7 --- /dev/null +++ b/validator/tests/rules/modules/waf/001_rule1_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "User rules have precedence in custom-acl", + runs: [ + { + persistent-input: { + rule1-input: admin + }, + rules: [ + { + custom-1: [ + { + address: rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/waf/002_rule1_base_rule_match.yaml b/validator/tests/rules/modules/waf/002_rule1_base_rule_match.yaml new file mode 100644 index 000000000..ca5e2f1ff --- /dev/null +++ b/validator/tests/rules/modules/waf/002_rule1_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule1-input: admin + }, + rules: [ + { + 1: [ + { + address: base-rule1-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/waf/003_rule2_blocking_user_rule_match.yaml b/validator/tests/rules/modules/waf/003_rule2_blocking_user_rule_match.yaml new file mode 100644 index 000000000..10b8f343f --- /dev/null +++ b/validator/tests/rules/modules/waf/003_rule2_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over non-blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule2-input: admin + }, + rules: [ + { + custom-2: [ + { + address: rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/waf/004_rule2_base_rule_match.yaml b/validator/tests/rules/modules/waf/004_rule2_base_rule_match.yaml new file mode 100644 index 000000000..af02700b2 --- /dev/null +++ b/validator/tests/rules/modules/waf/004_rule2_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule2-input: admin + }, + rules: [ + { + 2: [ + { + address: base-rule2-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/waf/005_rule3_blocking_base_rule_match.yaml b/validator/tests/rules/modules/waf/005_rule3_blocking_base_rule_match.yaml new file mode 100644 index 000000000..303882ba1 --- /dev/null +++ b/validator/tests/rules/modules/waf/005_rule3_blocking_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking base rules have precedence over non-blocking user rules in authentication-acl", + runs: [ + { + persistent-input: { + rule3-input: admin + }, + rules: [ + { + 3: [ + { + address: rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/waf/006_rule3_user_rule_match.yaml b/validator/tests/rules/modules/waf/006_rule3_user_rule_match.yaml new file mode 100644 index 000000000..46280d8da --- /dev/null +++ b/validator/tests/rules/modules/waf/006_rule3_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + custom-rule3-input: admin + }, + rules: [ + { + custom-3: [ + { + address: custom-rule3-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/waf/007_rule4_blocking_user_rule_match.yaml b/validator/tests/rules/modules/waf/007_rule4_blocking_user_rule_match.yaml new file mode 100644 index 000000000..d9105eded --- /dev/null +++ b/validator/tests/rules/modules/waf/007_rule4_blocking_user_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Blocking user rules have precedence over blocking base rules in authentication-acl", + runs: [ + { + persistent-input: { + rule4-input: admin + }, + rules: [ + { + custom-4: [ + { + address: rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/waf/008_rule4_base_rule_match.yaml b/validator/tests/rules/modules/waf/008_rule4_base_rule_match.yaml new file mode 100644 index 000000000..19bed6178 --- /dev/null +++ b/validator/tests/rules/modules/waf/008_rule4_base_rule_match.yaml @@ -0,0 +1,21 @@ +{ + name: "Validate that base rules still match when there's no contention", + runs: [ + { + persistent-input: { + base-rule4-input: admin + }, + rules: [ + { + 4: [ + { + address: base-rule4-input, + value: admin + } + ] + } + ], + code: match + } + ] +} diff --git a/validator/tests/rules/modules/waf/ruleset.yaml b/validator/tests/rules/modules/waf/ruleset.yaml new file mode 100644 index 000000000..f6959777f --- /dev/null +++ b/validator/tests/rules/modules/waf/ruleset.yaml @@ -0,0 +1,123 @@ +version: '2.1' +rules: + - id: "1" + name: rule1-non-blocking + tags: + type: flow1 + category: category + module: waf + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: base-rule1-input + list: + - "admin" + - id: "2" + name: rule2-non-blocking + tags: + type: flow2 + category: category + module: waf + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: base-rule2-input + list: + - "admin" + - id: "3" + name: rule3-blocking + tags: + type: flow3 + category: category + module: waf + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: base-rule3-input + list: + - "admin" + on_match: + - block + - id: "4" + name: rule4-blocking + tags: + type: flow4 + category: category + module: waf + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: base-rule4-input + list: + - "admin" + on_match: + - block +custom_rules: + - id: "custom-1" + name: custom-rule1-non-blocking + tags: + type: flow1 + category: category + module: waf + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule1-input + - address: custom-rule1-input + list: + - "admin" + - id: "custom-2" + name: custom-rule2-blocking + tags: + type: flow2 + category: category + module: waf + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule2-input + - address: custom-rule2-input + list: + - "admin" + on_match: + - block + - id: "custom-3" + name: custom-rule3-blocking + tags: + type: flow3 + category: category + module: waf + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule3-input + - address: custom-rule3-input + list: + - "admin" + - id: "custom-4" + name: custom-rule4-blocking + tags: + type: flow4 + category: category + module: waf + conditions: + - operator: exact_match + parameters: + inputs: + - address: rule4-input + - address: custom-rule4-input + list: + - "admin" + on_match: + - block From 1b3a53d9dfe9848d6e6304b8934b09b12e4cff7a Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 25 Nov 2024 20:21:02 +0000 Subject: [PATCH 19/32] Add more tests, improvements, etc --- src/builder/module_builder.hpp | 32 ++++---- src/context.cpp | 10 +-- src/module.cpp | 19 ++++- src/module.hpp | 2 + tests/unit/context_test.cpp | 95 ---------------------- tests/unit/module_test.cpp | 144 +++++++++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 124 deletions(-) create mode 100644 tests/unit/module_test.cpp diff --git a/src/builder/module_builder.hpp b/src/builder/module_builder.hpp index d26c70069..8f76d6979 100644 --- a/src/builder/module_builder.hpp +++ b/src/builder/module_builder.hpp @@ -12,6 +12,22 @@ namespace ddwaf { +// Helpers +inline bool user_rule_precedence( + const core_rule::source_type left, const core_rule::source_type right) +{ + return left > right; +} + +inline bool base_rule_precedence( + const core_rule::source_type left, const core_rule::source_type right) +{ + return left < right; +} + +inline std::string_view type_grouping_key(const core_rule *rule) { return rule->get_type(); } +inline constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; } + // The module builder can be used to build a single module class rule_module_builder { public: @@ -55,22 +71,6 @@ class rule_module_set_builder { const std::vector> &rules); protected: - // Helpers - static bool user_rule_precedence( - const core_rule::source_type left, const core_rule::source_type right) - { - return left > right; - } - - static bool base_rule_precedence( - const core_rule::source_type left, const core_rule::source_type right) - { - return left < right; - } - - static std::string_view type_grouping_key(const core_rule *rule) { return rule->get_type(); } - static constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; } - std::array builders_{{ // Network-ACL {base_rule_precedence, null_grouping_key, rule_module::expiration_policy::non_expiring}, diff --git a/src/context.cpp b/src/context.cpp index 0f9da9c68..773d9b209 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -225,21 +225,13 @@ exclusion::context_policy &context::eval_filters(ddwaf::timer &deadline) std::vector context::eval_rules( const exclusion::context_policy &policy, ddwaf::timer &deadline) { - static auto no_deadline = endless_timer(); - std::vector events; for (std::size_t i = 0; i < ruleset_->rule_modules.size(); ++i) { const auto &mod = ruleset_->rule_modules[i]; auto &cache = rule_module_cache_[i]; - rule_module::verdict_type verdict = rule_module::verdict_type::none; - if (mod.may_expire()) { - verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline); - } else { - verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, no_deadline); - } - + auto verdict = mod.eval(events, store_, cache, policy, ruleset_->rule_matchers, deadline); if (verdict == rule_module::verdict_type::block) { break; } diff --git a/src/module.cpp b/src/module.cpp index e770e46d7..604b1b6d1 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -85,6 +85,12 @@ std::pair, verdict_type> eval_rule(const core_rule &rule, } // namespace +ddwaf::timer &rule_module::get_deadline(ddwaf::timer &deadline) const +{ + static auto no_deadline = endless_timer(); + return may_expire() ? deadline : no_deadline; +} + verdict_type rule_module::eval_with_collections(std::vector &events, object_store &store, cache_type &cache, const exclusion::context_policy &exclusion, const std::unordered_map> &dynamic_matchers, @@ -132,22 +138,27 @@ verdict_type rule_module::eval(std::vector &events, object_store &store, const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) const { + auto &apt_deadline = get_deadline(deadline); + if (collections_.empty()) { + auto final_verdict = verdict_type::none; for (std::size_t i = 0; i < rules_.size(); ++i) { const auto &rule = *rules_[i]; auto &rule_cache = cache.rules[i]; auto [event, verdict] = - eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, deadline); + eval_rule(rule, store, rule_cache, exclusion, dynamic_matchers, apt_deadline); if (event.has_value()) { events.emplace_back(std::move(*event)); DDWAF_DEBUG("Found event on rule {}", rule.get_id()); - return verdict; + if (verdict > final_verdict) { + final_verdict = verdict; + } } } - return verdict_type::none; + return final_verdict; } - return eval_with_collections(events, store, cache, exclusion, dynamic_matchers, deadline); + return eval_with_collections(events, store, cache, exclusion, dynamic_matchers, apt_deadline); } } // namespace ddwaf diff --git a/src/module.hpp b/src/module.hpp index b632fa1e0..4cf22b5d0 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -62,6 +62,8 @@ class rule_module { const std::unordered_map> &dynamic_matchers, ddwaf::timer &deadline) const; + ddwaf::timer &get_deadline(ddwaf::timer &deadline) const; + struct rule_collection { std::string_view name; verdict_type type; diff --git a/tests/unit/context_test.cpp b/tests/unit/context_test.cpp index bc8ed97bb..53fa30c1b 100644 --- a/tests/unit/context_test.cpp +++ b/tests/unit/context_test.cpp @@ -679,101 +679,6 @@ TEST(TestContext, MatchMultipleRulesWithPriorityDoubleRunPriorityFirst) } } -// TODO: collections don't work like that any longer -/*TEST(TestContext, MatchMultipleRulesWithPriorityUntilAllActionsMet)*/ -/*{*/ -/*auto ruleset = test::get_default_ruleset();*/ -/*std::vector> rules;*/ -/*{*/ -/*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"}};*/ - -/*rules.emplace_back(*/ -/*std::make_shared("id1", "name1", std::move(tags), builder.build()));*/ -/*}*/ - -/*{*/ -/*test::expression_builder builder(1);*/ -/*builder.start_condition();*/ -/*builder.add_argument();*/ -/*builder.add_target("usr.id");*/ -/*builder.end_condition(std::vector{"admin"});*/ - -/*std::unordered_map tags{*/ -/*{"type", "type"}, {"category", "category2"}};*/ - -/*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);*/ - -/*{*/ -/*ddwaf_object root;*/ -/*ddwaf_object tmp;*/ -/*ddwaf_object_map(&root);*/ -/*ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1"));*/ -/*ctx.insert(root);*/ - -/*auto events = ctx.eval_rules({}, deadline);*/ -/*EXPECT_EQ(events.size(), 1);*/ - -/*auto &event = events[0];*/ -/*EXPECT_STREQ(event.rule->get_id().data(), "id1");*/ -/*EXPECT_STREQ(event.rule->get_name().data(), "name1");*/ -/*EXPECT_STREQ(event.rule->get_tag("type").data(), "type");*/ -/*EXPECT_STREQ(event.rule->get_tag("category").data(), "category1");*/ -/*EXPECT_TRUE(event.rule->get_actions().empty());*/ - -/*auto &match = event.matches[0];*/ -/*EXPECT_STREQ(match.args[0].resolved.c_str(), "192.168.0.1");*/ -/*EXPECT_STREQ(match.highlights[0].c_str(), "192.168.0.1");*/ -/*EXPECT_STREQ(match.operator_name.data(), "ip_match");*/ -/*EXPECT_STREQ(match.operator_value.data(), "");*/ -/*EXPECT_STREQ(match.args[0].address.data(), "http.client_ip");*/ -/*EXPECT_TRUE(match.args[0].key_path.empty());*/ -/*}*/ - -/*{*/ -/*// An existing match in a collection will not inhibit a match in a*/ -/*// priority collection.*/ -/*ddwaf_object root;*/ -/*ddwaf_object tmp;*/ -/*ddwaf_object_map(&root);*/ -/*ddwaf_object_map_add(&root, "usr.id", ddwaf_object_string(&tmp, "admin"));*/ -/*ctx.insert(root);*/ - -/*auto events = ctx.eval_rules({}, deadline);*/ -/*EXPECT_EQ(events.size(), 1);*/ - -/*auto &event = events[0];*/ -/*EXPECT_EQ(events.size(), 1);*/ -/*EXPECT_STREQ(event.rule->get_id().data(), "id2");*/ -/*EXPECT_STREQ(event.rule->get_name().data(), "name2");*/ -/*EXPECT_STREQ(event.rule->get_tag("type").data(), "type");*/ -/*EXPECT_STREQ(event.rule->get_tag("category").data(), "category2");*/ -/*std::vector expected_actions{"redirect"};*/ -/*EXPECT_EQ(event.rule->get_actions(), expected_actions);*/ -/*EXPECT_EQ(event.matches.size(), 1);*/ - -/*auto &match = event.matches[0];*/ -/*EXPECT_STREQ(match.args[0].resolved.c_str(), "admin");*/ -/*EXPECT_STREQ(match.highlights[0].c_str(), "admin");*/ -/*EXPECT_STREQ(match.operator_name.data(), "exact_match");*/ -/*EXPECT_STREQ(match.operator_value.data(), "");*/ -/*EXPECT_STREQ(match.args[0].address.data(), "usr.id");*/ -/*EXPECT_TRUE(match.args[0].key_path.empty());*/ -/*}*/ -/*}*/ - TEST(TestContext, MatchMultipleCollectionsSingleRun) { auto ruleset = test::get_default_ruleset(); diff --git a/tests/unit/module_test.cpp b/tests/unit/module_test.cpp new file mode 100644 index 000000000..07db6db0a --- /dev/null +++ b/tests/unit/module_test.cpp @@ -0,0 +1,144 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2021 Datadog, Inc. + +#include "clock.hpp" +#include "common/gtest_utils.hpp" +#include "condition/scalar_condition.hpp" +#include "matcher/exact_match.hpp" +#include "matcher/ip_match.hpp" +#include "module.hpp" + +using namespace ddwaf; +using namespace std::literals; + +namespace { + +TEST(TestModule, SingleRuleMatch) +{ + test::expression_builder builder(1); + builder.start_condition(); + builder.add_argument(); + builder.add_target("http.client_ip"); + builder.end_condition(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()); + + rule_module_builder mod_builder{base_rule_precedence, null_grouping_key}; + mod_builder.insert(rule.get()); + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + mod.eval(events, store, cache, {}, {}, deadline); + + EXPECT_EQ(events.size(), 1); + } + + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + std::vector events; + ddwaf::timer deadline = endless_timer(); + mod.eval(events, store, cache, {}, {}, deadline); + + EXPECT_EQ(events.size(), 0); + } +} + +TEST(TestModule, NonExpiringModule) +{ + test::expression_builder builder(1); + builder.start_condition(); + builder.add_argument(); + builder.add_target("http.client_ip"); + builder.end_condition(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()); + + rule_module_builder mod_builder{ + base_rule_precedence, null_grouping_key, rule_module::expiration_policy::non_expiring}; + mod_builder.insert(rule.get()); + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline{0s}; + mod.eval(events, store, cache, {}, {}, deadline); + + EXPECT_EQ(events.size(), 1); + } +} + +TEST(TestModule, ExpiringModule) +{ + test::expression_builder builder(1); + builder.start_condition(); + builder.add_argument(); + builder.add_target("http.client_ip"); + builder.end_condition(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()); + + rule_module_builder mod_builder{ + base_rule_precedence, null_grouping_key, rule_module::expiration_policy::expiring}; + mod_builder.insert(rule.get()); + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline{0s}; + EXPECT_THROW(mod.eval(events, store, cache, {}, {}, deadline), ddwaf::timeout_exception); + } +} + +} // namespace From 560d9d5e3e7497a4c3e3342d3afd7ed1e3d08ce1 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 25 Nov 2024 20:32:07 +0000 Subject: [PATCH 20/32] Minor change --- src/module.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/module.cpp b/src/module.cpp index 604b1b6d1..204268575 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -141,7 +141,6 @@ verdict_type rule_module::eval(std::vector &events, object_store &store, auto &apt_deadline = get_deadline(deadline); if (collections_.empty()) { - auto final_verdict = verdict_type::none; for (std::size_t i = 0; i < rules_.size(); ++i) { const auto &rule = *rules_[i]; auto &rule_cache = cache.rules[i]; @@ -151,12 +150,10 @@ verdict_type rule_module::eval(std::vector &events, object_store &store, if (event.has_value()) { events.emplace_back(std::move(*event)); DDWAF_DEBUG("Found event on rule {}", rule.get_id()); - if (verdict > final_verdict) { - final_verdict = verdict; - } + return verdict; } } - return final_verdict; + return verdict_type::none; } return eval_with_collections(events, store, cache, exclusion, dynamic_matchers, apt_deadline); From ba3d3c8e04841b5aa524fd1829c37b14ed8454d4 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 25 Nov 2024 21:34:56 +0000 Subject: [PATCH 21/32] Allow multiple matches in monitor mode --- src/module.cpp | 8 ++++++-- .../001_rule1_base_rule_match.yaml | 12 ++++++++++-- .../business-logic/001_rule1_user_rule_match.yaml | 11 ++++++++++- .../configuration/001_rule1_user_rule_match.yaml | 11 ++++++++++- .../custom-acl/001_rule1_user_rule_match.yaml | 10 +++++++++- .../network-acl/001_rule1_base_rule_match.yaml | 10 +++++++++- .../modules/rasp/001_rule1_base_rule_match.yaml | 10 +++++++++- 7 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/module.cpp b/src/module.cpp index 204268575..088880121 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -141,6 +141,7 @@ verdict_type rule_module::eval(std::vector &events, object_store &store, auto &apt_deadline = get_deadline(deadline); if (collections_.empty()) { + auto final_verdict = verdict_type::none; for (std::size_t i = 0; i < rules_.size(); ++i) { const auto &rule = *rules_[i]; auto &rule_cache = cache.rules[i]; @@ -150,10 +151,13 @@ verdict_type rule_module::eval(std::vector &events, object_store &store, if (event.has_value()) { events.emplace_back(std::move(*event)); DDWAF_DEBUG("Found event on rule {}", rule.get_id()); - return verdict; + final_verdict = verdict; + if (final_verdict == verdict_type::block) { + break; + } } } - return verdict_type::none; + return final_verdict; } return eval_with_collections(events, store, cache, exclusion, dynamic_matchers, apt_deadline); diff --git a/validator/tests/rules/modules/authentication-acl/001_rule1_base_rule_match.yaml b/validator/tests/rules/modules/authentication-acl/001_rule1_base_rule_match.yaml index 2086ca33c..227e7a972 100644 --- a/validator/tests/rules/modules/authentication-acl/001_rule1_base_rule_match.yaml +++ b/validator/tests/rules/modules/authentication-acl/001_rule1_base_rule_match.yaml @@ -1,5 +1,5 @@ { - name: "Base rules have precedence in authentication-acl", + name: "All rules are provided in monitoring mode", runs: [ { persistent-input: { @@ -13,7 +13,15 @@ value: admin } ] - } + }, + { + custom-1: [ + { + address: rule1-input, + value: admin + } + ] + }, ], code: match } diff --git a/validator/tests/rules/modules/business-logic/001_rule1_user_rule_match.yaml b/validator/tests/rules/modules/business-logic/001_rule1_user_rule_match.yaml index f0a0d6bc7..ab9ba579b 100644 --- a/validator/tests/rules/modules/business-logic/001_rule1_user_rule_match.yaml +++ b/validator/tests/rules/modules/business-logic/001_rule1_user_rule_match.yaml @@ -1,5 +1,5 @@ { - name: "User rules have precedence in custom-acl", + name: "All rules are provided in monitoring mode", runs: [ { persistent-input: { @@ -13,7 +13,16 @@ value: admin } ] + }, + { + 1: [ + { + address: rule1-input, + value: admin + } + ] } + ], code: match } diff --git a/validator/tests/rules/modules/configuration/001_rule1_user_rule_match.yaml b/validator/tests/rules/modules/configuration/001_rule1_user_rule_match.yaml index f0a0d6bc7..ab9ba579b 100644 --- a/validator/tests/rules/modules/configuration/001_rule1_user_rule_match.yaml +++ b/validator/tests/rules/modules/configuration/001_rule1_user_rule_match.yaml @@ -1,5 +1,5 @@ { - name: "User rules have precedence in custom-acl", + name: "All rules are provided in monitoring mode", runs: [ { persistent-input: { @@ -13,7 +13,16 @@ value: admin } ] + }, + { + 1: [ + { + address: rule1-input, + value: admin + } + ] } + ], code: match } diff --git a/validator/tests/rules/modules/custom-acl/001_rule1_user_rule_match.yaml b/validator/tests/rules/modules/custom-acl/001_rule1_user_rule_match.yaml index f0a0d6bc7..c5f6f7e8f 100644 --- a/validator/tests/rules/modules/custom-acl/001_rule1_user_rule_match.yaml +++ b/validator/tests/rules/modules/custom-acl/001_rule1_user_rule_match.yaml @@ -1,5 +1,5 @@ { - name: "User rules have precedence in custom-acl", + name: "All rules are provided in monitoring mode", runs: [ { persistent-input: { @@ -13,6 +13,14 @@ value: admin } ] + }, + { + 1: [ + { + address: rule1-input, + value: admin + } + ] } ], code: match diff --git a/validator/tests/rules/modules/network-acl/001_rule1_base_rule_match.yaml b/validator/tests/rules/modules/network-acl/001_rule1_base_rule_match.yaml index 40dc539f0..af567d00c 100644 --- a/validator/tests/rules/modules/network-acl/001_rule1_base_rule_match.yaml +++ b/validator/tests/rules/modules/network-acl/001_rule1_base_rule_match.yaml @@ -1,5 +1,5 @@ { - name: "Base rules have precedence in network-acl", + name: "All rules are provided in monitoring mode", runs: [ { persistent-input: { @@ -12,6 +12,14 @@ address: rule1-input, value: 192.168.0.1 } + ], + }, + { + custom-1: [ + { + address: rule1-input, + value: 192.168.0.1 + } ] } ], diff --git a/validator/tests/rules/modules/rasp/001_rule1_base_rule_match.yaml b/validator/tests/rules/modules/rasp/001_rule1_base_rule_match.yaml index 2086ca33c..7d4d3ef92 100644 --- a/validator/tests/rules/modules/rasp/001_rule1_base_rule_match.yaml +++ b/validator/tests/rules/modules/rasp/001_rule1_base_rule_match.yaml @@ -1,5 +1,5 @@ { - name: "Base rules have precedence in authentication-acl", + name: "All rules are provided in monitoring mode", runs: [ { persistent-input: { @@ -13,6 +13,14 @@ value: admin } ] + }, + { + custom-1: [ + { + address: rule1-input, + value: admin + } + ] } ], code: match From c01b719575190b81ed26b6b50dae23493ea3ce72 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:01:23 +0000 Subject: [PATCH 22/32] Tests --- tests/unit/clock_test.cpp | 9 + tests/unit/module_test.cpp | 337 +++++++++++++++++++++- tests/unit/processor/fingerprint_test.cpp | 32 ++ 3 files changed, 375 insertions(+), 3 deletions(-) diff --git a/tests/unit/clock_test.cpp b/tests/unit/clock_test.cpp index d71816ed6..43a9a0686 100644 --- a/tests/unit/clock_test.cpp +++ b/tests/unit/clock_test.cpp @@ -56,4 +56,13 @@ TEST(TestTimer, EndlessTimer) EXPECT_FALSE(deadline.expired()); } +TEST(TestTimer, TimerExpirationBeyondSizeLimit) +{ + ddwaf::base_timer<5> deadline{std::chrono::nanoseconds::max()}; + EXPECT_FALSE(deadline.expired()); + + std::this_thread::sleep_for(1ms); + EXPECT_FALSE(deadline.expired()); +} + } // namespace diff --git a/tests/unit/module_test.cpp b/tests/unit/module_test.cpp index 07db6db0a..23298d95a 100644 --- a/tests/unit/module_test.cpp +++ b/tests/unit/module_test.cpp @@ -47,8 +47,8 @@ TEST(TestModule, SingleRuleMatch) std::vector events; ddwaf::timer deadline = endless_timer(); - mod.eval(events, store, cache, {}, {}, deadline); - + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); EXPECT_EQ(events.size(), 1); } @@ -62,11 +62,341 @@ TEST(TestModule, SingleRuleMatch) std::vector events; ddwaf::timer deadline = endless_timer(); mod.eval(events, store, cache, {}, {}, deadline); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::none); + EXPECT_EQ(events.size(), 0); + } +} + +TEST(TestModule, MultipleMonitoringRuleMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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"}}; + + rules.emplace_back( + std::make_shared("id2", "name", std::move(tags), builder.build())); + } + + rule_module_builder mod_builder{base_rule_precedence, null_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 2); + } + + { + 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 = endless_timer(); + mod.eval(events, store, cache, {}, {}, deadline); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::none); EXPECT_EQ(events.size(), 0); } } +TEST(TestModule, BlockingRuleMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{base_rule_precedence, null_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + } + + // No further calls should happen after a blocking rule matches +} + +TEST(TestModule, MonitoringRuleMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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.2"}); + + std::unordered_map tags{ + {"type", "type"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{base_rule_precedence, null_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 1); + } + + // Check that we can still match the blocking rule + { + 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.2")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + } +} + +TEST(TestModule, BlockingRuleMatchBasePrecedence) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back(std::make_shared("id1", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::user, + core_rule::verdict_type::block)); + rules.emplace_back( + std::make_shared("id", "name", std::move(tags), builder.build())); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{base_rule_precedence, null_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + EXPECT_STRV(events[0].rule->get_id(), "id2"); + } + + // No further calls should happen after a blocking rule matches +} + +TEST(TestModule, BlockingRuleMatchUserPrecedence) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back(std::make_shared("id1", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::user, + core_rule::verdict_type::block)); + rules.emplace_back( + std::make_shared("id", "name", std::move(tags), builder.build())); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{user_rule_precedence, null_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + EXPECT_STRV(events[0].rule->get_id(), "id1"); + } + + // No further calls should happen after a blocking rule matches +} + TEST(TestModule, NonExpiringModule) { test::expression_builder builder(1); @@ -84,6 +414,7 @@ TEST(TestModule, NonExpiringModule) mod_builder.insert(rule.get()); auto mod = mod_builder.build(); + EXPECT_FALSE(mod.may_expire()); rule_module_cache cache; mod.init_cache(cache); @@ -104,7 +435,6 @@ TEST(TestModule, NonExpiringModule) EXPECT_EQ(events.size(), 1); } } - TEST(TestModule, ExpiringModule) { test::expression_builder builder(1); @@ -122,6 +452,7 @@ TEST(TestModule, ExpiringModule) mod_builder.insert(rule.get()); auto mod = mod_builder.build(); + EXPECT_TRUE(mod.may_expire()); rule_module_cache cache; mod.init_cache(cache); diff --git a/tests/unit/processor/fingerprint_test.cpp b/tests/unit/processor/fingerprint_test.cpp index cca033538..39237474b 100644 --- a/tests/unit/processor/fingerprint_test.cpp +++ b/tests/unit/processor/fingerprint_test.cpp @@ -644,6 +644,22 @@ TEST(TestHttpHeaderFingerprint, UnknownHeaders) ddwaf_object_free(&output); } +TEST(TestHttpHeaderFingerprint, InvalidHeaderType) +{ + ddwaf_object headers; + ddwaf_object_string(&headers, "value"); + + http_header_fingerprint gen{"id", {}, {}, false, true}; + + ddwaf::timer deadline{2s}; + processor_cache cache; + auto [output, attr] = gen.eval_impl({{}, {}, false, &headers}, cache, deadline); + EXPECT_EQ(output.type, DDWAF_OBJ_INVALID); + EXPECT_EQ(attr, object_store::attribute::none); + + ddwaf_object_free(&headers); +} + TEST(TestHttpNetworkFingerprint, AllXFFHeaders) { ddwaf_object tmp; @@ -817,6 +833,22 @@ TEST(TestHttpNetworkFingerprint, HeaderPrecedence) match_frag(get_headers(9), "net-10-0000000001"); } +TEST(TestNetworkHeaderFingerprint, InvalidHeaderType) +{ + ddwaf_object headers; + ddwaf_object_string(&headers, "value"); + + http_network_fingerprint gen{"id", {}, {}, false, true}; + + ddwaf::timer deadline{2s}; + processor_cache cache; + auto [output, attr] = gen.eval_impl({{}, {}, false, &headers}, cache, deadline); + EXPECT_EQ(output.type, DDWAF_OBJ_INVALID); + EXPECT_EQ(attr, object_store::attribute::none); + + ddwaf_object_free(&headers); +} + TEST(TestSessionFingerprint, UserOnly) { ddwaf_object cookies; From 22601523758ff9f7603d686d274aa43da31752c5 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:58:30 +0000 Subject: [PATCH 23/32] Test --- tests/integration/context/test.cpp | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/integration/context/test.cpp b/tests/integration/context/test.cpp index d3bad2465..c063a69aa 100644 --- a/tests/integration/context/test.cpp +++ b/tests/integration/context/test.cpp @@ -987,4 +987,54 @@ TEST(TestContextIntegration, MultipleModuleSingleCollectionMatch) ddwaf_destroy(handle); } +TEST(TestContextIntegration, TimeoutBeyondLimit) +{ + // Initialize a WAF rule + auto rule = read_file("processor.yaml", base_dir); + ASSERT_TRUE(rule.type != DDWAF_OBJ_INVALID); + + ddwaf_handle handle = ddwaf_init(&rule, nullptr, nullptr); + ASSERT_NE(handle, nullptr); + ddwaf_object_free(&rule); + + ddwaf_context context = ddwaf_context_init(handle); + ASSERT_NE(context, nullptr); + + // Setup the parameter structure + ddwaf_object parameter = DDWAF_OBJECT_MAP; + ddwaf_object subMap = DDWAF_OBJECT_MAP; + ddwaf_object tmp; + ddwaf_object_map_add(¶meter, "value", ddwaf_object_string(&tmp, "rule2")); + ddwaf_object_map_add(&subMap, "key", ddwaf_object_string(&tmp, "rule3")); + ddwaf_object_map_add(¶meter, "value2", &subMap); // ddwaf_object_string(&,"rule3")); + + ddwaf_result ret; + EXPECT_EQ(ddwaf_run(context, ¶meter, nullptr, &ret, std::numeric_limits::max()), + DDWAF_MATCH); + + EXPECT_FALSE(ret.timeout); + EXPECT_EVENTS(ret, {.id = "1", + .name = "rule1", + .tags = {{"type", "flow1"}, {"category", "category1"}}, + .matches = {{.op = "match_regex", + .op_value = "rule2", + .highlight = "rule2", + .args = {{ + .value = "rule2", + .address = "value", + }}}, + {.op = "match_regex", + .op_value = "rule3", + .highlight = "rule3", + .args = {{ + .value = "rule3", + .address = "value2", + .path = {"key"}, + }}}}}); + + ddwaf_result_free(&ret); + ddwaf_context_destroy(context); + ddwaf_destroy(handle); +} + } // namespace From 84d31f783be861325f754839826d3a488c40ada8 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:52:28 +0000 Subject: [PATCH 24/32] Tests and improvements --- src/module.cpp | 6 +- tests/unit/context_test.cpp | 4 +- tests/unit/module_test.cpp | 227 ++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 4 deletions(-) diff --git a/src/module.cpp b/src/module.cpp index 088880121..ded4e3fca 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -123,9 +123,11 @@ verdict_type rule_module::eval_with_collections(std::vector &events, obje events.emplace_back(std::move(*event)); DDWAF_DEBUG("Found event on rule {}", rule.get_id()); - if (verdict > final_verdict) { - final_verdict = verdict; + if (verdict == verdict_type::block) { + return verdict_type::block; } + + final_verdict = verdict_type::monitor; break; } } diff --git a/tests/unit/context_test.cpp b/tests/unit/context_test.cpp index 53fa30c1b..47f2537be 100644 --- a/tests/unit/context_test.cpp +++ b/tests/unit/context_test.cpp @@ -726,7 +726,7 @@ TEST(TestContext, MatchMultipleCollectionsSingleRun) EXPECT_EQ(events.size(), 2); } -TEST(TestContext, MatchMultiplePriorityCollectionsSingleRun) +TEST(TestContext, MatchPriorityCollectionsSingleRun) { auto ruleset = test::get_default_ruleset(); std::vector> rules; @@ -771,7 +771,7 @@ TEST(TestContext, MatchMultiplePriorityCollectionsSingleRun) ctx.insert(root); auto events = ctx.eval_rules({}, deadline); - EXPECT_EQ(events.size(), 2); + EXPECT_EQ(events.size(), 1); } TEST(TestContext, MatchMultipleCollectionsDoubleRun) diff --git a/tests/unit/module_test.cpp b/tests/unit/module_test.cpp index 23298d95a..4dbc47605 100644 --- a/tests/unit/module_test.cpp +++ b/tests/unit/module_test.cpp @@ -435,6 +435,7 @@ TEST(TestModule, NonExpiringModule) EXPECT_EQ(events.size(), 1); } } + TEST(TestModule, ExpiringModule) { test::expression_builder builder(1); @@ -472,4 +473,230 @@ TEST(TestModule, ExpiringModule) } } +TEST(TestModule, MultipleCollectionsMonitoringRuleMatch) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id2", "name", std::move(tags), builder.build())); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 2); + } +} + +TEST(TestModule, MultipleCollectionsBlockingRuleMatch) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + } +} + +TEST(TestModule, SingleCollectionsBlockingRuleMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + } +} + +TEST(TestModule, SingleCollectionsMonitoringRuleMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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"}}; + + rules.emplace_back( + std::make_shared("id2", "name", std::move(tags), builder.build())); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 1); + } +} + } // namespace From 3e8618fcb2e14e9281fc86b444a7b5581ef00d3e Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:22:33 +0000 Subject: [PATCH 25/32] More tests --- tests/unit/module_test.cpp | 384 +++++++++++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) diff --git a/tests/unit/module_test.cpp b/tests/unit/module_test.cpp index 4dbc47605..ca15ccd04 100644 --- a/tests/unit/module_test.cpp +++ b/tests/unit/module_test.cpp @@ -699,4 +699,388 @@ TEST(TestModule, SingleCollectionsMonitoringRuleMatch) } } +TEST(TestModule, UserPrecedenceSingleCollectionsMonitoringUserMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 1); + EXPECT_STRV(events[0].rule->get_id(), "id2"); + } +} + +TEST(TestModule, BasePrecedenceSingleCollectionsMonitoringBaseNatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 1); + EXPECT_STRV(events[0].rule->get_id(), "id1"); + } +} + +TEST(TestModule, UserPrecedenceSingleCollectionsBlockingBaseMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back(std::make_shared("id1", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + EXPECT_STRV(events[0].rule->get_id(), "id1"); + } +} + +TEST(TestModule, UserPrecedenceSingleCollectionsBlockingUserMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back(std::make_shared("id1", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::user, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + EXPECT_STRV(events[0].rule->get_id(), "id2"); + } +} + +TEST(TestModule, UserPrecedenceMultipleCollectionsMonitoringMatch) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 2); + } +} + +TEST(TestModule, UserPrecedenceMultipleCollectionsBlockingMatch) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::user, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + } +} + +TEST(TestModule, DisabledRules) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared( + "id1", "name", std::move(tags), builder.build(), std::vector{}, false)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::none); + } +} } // namespace From 8551f2ece658171e9c3707ff64f51115139d141a Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:39:41 +0000 Subject: [PATCH 26/32] More tests --- tests/unit/module_test.cpp | 786 +++++++++++++++++++++++++++++++++++-- 1 file changed, 757 insertions(+), 29 deletions(-) diff --git a/tests/unit/module_test.cpp b/tests/unit/module_test.cpp index ca15ccd04..4b1771d26 100644 --- a/tests/unit/module_test.cpp +++ b/tests/unit/module_test.cpp @@ -16,7 +16,17 @@ using namespace std::literals; namespace { -TEST(TestModule, SingleRuleMatch) +bool contains(auto events, auto id) +{ + for (const auto &event : events) { + if (event.rule->get_id() == id) { + return true; + } + } + return false; +} + +TEST(TestModuleUngrouped, SingleRuleMatch) { test::expression_builder builder(1); builder.start_condition(); @@ -68,7 +78,7 @@ TEST(TestModule, SingleRuleMatch) } } -TEST(TestModule, MultipleMonitoringRuleMatch) +TEST(TestModuleUngrouped, MultipleMonitoringRuleMatch) { std::vector> rules; { @@ -121,6 +131,8 @@ TEST(TestModule, MultipleMonitoringRuleMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::monitor); EXPECT_EQ(events.size(), 2); + EXPECT_TRUE(contains(events, "id1")); + EXPECT_TRUE(contains(events, "id2")); } { @@ -139,7 +151,7 @@ TEST(TestModule, MultipleMonitoringRuleMatch) } } -TEST(TestModule, BlockingRuleMatch) +TEST(TestModuleUngrouped, BlockingRuleMatch) { std::vector> rules; { @@ -193,12 +205,13 @@ TEST(TestModule, BlockingRuleMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::block); EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id2")); } // No further calls should happen after a blocking rule matches } -TEST(TestModule, MonitoringRuleMatch) +TEST(TestModuleUngrouped, MonitoringRuleMatch) { std::vector> rules; { @@ -252,6 +265,7 @@ TEST(TestModule, MonitoringRuleMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::monitor); EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id1")); } // Check that we can still match the blocking rule @@ -271,7 +285,7 @@ TEST(TestModule, MonitoringRuleMatch) } } -TEST(TestModule, BlockingRuleMatchBasePrecedence) +TEST(TestModuleUngrouped, BlockingRuleMatchBasePrecedence) { std::vector> rules; { @@ -328,13 +342,13 @@ TEST(TestModule, BlockingRuleMatchBasePrecedence) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::block); EXPECT_EQ(events.size(), 1); - EXPECT_STRV(events[0].rule->get_id(), "id2"); + EXPECT_TRUE(contains(events, "id2")); } // No further calls should happen after a blocking rule matches } -TEST(TestModule, BlockingRuleMatchUserPrecedence) +TEST(TestModuleUngrouped, BlockingRuleMatchUserPrecedence) { std::vector> rules; { @@ -391,13 +405,13 @@ TEST(TestModule, BlockingRuleMatchUserPrecedence) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::block); EXPECT_EQ(events.size(), 1); - EXPECT_STRV(events[0].rule->get_id(), "id1"); + EXPECT_TRUE(contains(events, "id1")); } // No further calls should happen after a blocking rule matches } -TEST(TestModule, NonExpiringModule) +TEST(TestModuleUngrouped, NonExpiringModule) { test::expression_builder builder(1); builder.start_condition(); @@ -433,10 +447,11 @@ TEST(TestModule, NonExpiringModule) mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id")); } } -TEST(TestModule, ExpiringModule) +TEST(TestModuleUngrouped, ExpiringModule) { test::expression_builder builder(1); builder.start_condition(); @@ -473,7 +488,48 @@ TEST(TestModule, ExpiringModule) } } -TEST(TestModule, MultipleCollectionsMonitoringRuleMatch) +TEST(TestModuleUngrouped, DisabledRules) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared( + "id1", "name", std::move(tags), builder.build(), std::vector{}, false)); + } + + rule_module_builder mod_builder{user_rule_precedence, null_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::none); + } +} + +TEST(TestModuleGrouped, MultipleGroupsMonitoringRuleMatch) { std::vector> rules; { @@ -526,10 +582,12 @@ TEST(TestModule, MultipleCollectionsMonitoringRuleMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::monitor); EXPECT_EQ(events.size(), 2); + EXPECT_TRUE(contains(events, "id1")); + EXPECT_TRUE(contains(events, "id2")); } } -TEST(TestModule, MultipleCollectionsBlockingRuleMatch) +TEST(TestModuleGrouped, MultipleGroupsBlockingRuleMatch) { std::vector> rules; { @@ -583,10 +641,11 @@ TEST(TestModule, MultipleCollectionsBlockingRuleMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::block); EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id2")); } } -TEST(TestModule, SingleCollectionsBlockingRuleMatch) +TEST(TestModuleGrouped, SingleGroupBlockingRuleMatch) { std::vector> rules; { @@ -640,10 +699,11 @@ TEST(TestModule, SingleCollectionsBlockingRuleMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::block); EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id2")); } } -TEST(TestModule, SingleCollectionsMonitoringRuleMatch) +TEST(TestModuleGrouped, SingleGroupMonitoringRuleMatch) { std::vector> rules; { @@ -696,10 +756,11 @@ TEST(TestModule, SingleCollectionsMonitoringRuleMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::monitor); EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id1")); } } -TEST(TestModule, UserPrecedenceSingleCollectionsMonitoringUserMatch) +TEST(TestModuleGrouped, UserPrecedenceSingleGroupMonitoringUserMatch) { std::vector> rules; { @@ -752,11 +813,11 @@ TEST(TestModule, UserPrecedenceSingleCollectionsMonitoringUserMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::monitor); EXPECT_EQ(events.size(), 1); - EXPECT_STRV(events[0].rule->get_id(), "id2"); + EXPECT_TRUE(contains(events, "id2")); } } -TEST(TestModule, BasePrecedenceSingleCollectionsMonitoringBaseNatch) +TEST(TestModuleGrouped, BasePrecedenceSingleGroupMonitoringBaseMatch) { std::vector> rules; { @@ -809,11 +870,11 @@ TEST(TestModule, BasePrecedenceSingleCollectionsMonitoringBaseNatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::monitor); EXPECT_EQ(events.size(), 1); - EXPECT_STRV(events[0].rule->get_id(), "id1"); + EXPECT_TRUE(contains(events, "id1")); } } -TEST(TestModule, UserPrecedenceSingleCollectionsBlockingBaseMatch) +TEST(TestModuleGrouped, UserPrecedenceSingleGroupBlockingBaseMatch) { std::vector> rules; { @@ -867,11 +928,11 @@ TEST(TestModule, UserPrecedenceSingleCollectionsBlockingBaseMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::block); EXPECT_EQ(events.size(), 1); - EXPECT_STRV(events[0].rule->get_id(), "id1"); + EXPECT_TRUE(contains(events, "id1")); } } -TEST(TestModule, UserPrecedenceSingleCollectionsBlockingUserMatch) +TEST(TestModuleGrouped, UserPrecedenceSingleGroupBlockingUserMatch) { std::vector> rules; { @@ -926,11 +987,128 @@ TEST(TestModule, UserPrecedenceSingleCollectionsBlockingUserMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::block); EXPECT_EQ(events.size(), 1); - EXPECT_STRV(events[0].rule->get_id(), "id2"); + EXPECT_TRUE(contains(events, "id2")); + } +} + +TEST(TestModuleGrouped, BasePrecedenceSingleGroupBlockingBaseMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back(std::make_shared("id1", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id1")); + } +} + +TEST(TestModuleGrouped, BasePrecedenceSingleGroupBlockingUserMatch) +{ + std::vector> rules; + { + 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"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build(), + std::vector{"block"}, true, core_rule::source_type::base)); + } + + { + 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"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::user, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id2")); } } -TEST(TestModule, UserPrecedenceMultipleCollectionsMonitoringMatch) +TEST(TestModuleGrouped, UserPrecedenceMultipleGroupsMonitoringMatch) { std::vector> rules; { @@ -983,10 +1161,12 @@ TEST(TestModule, UserPrecedenceMultipleCollectionsMonitoringMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::monitor); EXPECT_EQ(events.size(), 2); + EXPECT_TRUE(contains(events, "id1")); + EXPECT_TRUE(contains(events, "id2")); } } -TEST(TestModule, UserPrecedenceMultipleCollectionsBlockingMatch) +TEST(TestModuleGrouped, UserPrecedenceMultipleGroupsBlockingMatch) { std::vector> rules; { @@ -1040,10 +1220,11 @@ TEST(TestModule, UserPrecedenceMultipleCollectionsBlockingMatch) auto verdict = mod.eval(events, store, cache, {}, {}, deadline); EXPECT_EQ(verdict, rule_module::verdict_type::block); EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id2")); } } -TEST(TestModule, DisabledRules) +TEST(TestModuleGrouped, BasePrecedenceMultipleGroupsMonitoringMatch) { std::vector> rules; { @@ -1056,11 +1237,25 @@ TEST(TestModule, DisabledRules) std::unordered_map tags{ {"type", "type1"}, {"category", "category"}}; - rules.emplace_back(std::make_shared( - "id1", "name", std::move(tags), builder.build(), std::vector{}, false)); + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); } - rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id2", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; for (const auto &rule : rules) { mod_builder.insert(rule.get()); } auto mod = mod_builder.build(); @@ -1080,7 +1275,540 @@ TEST(TestModule, DisabledRules) std::vector events; ddwaf::timer deadline = endless_timer(); auto verdict = mod.eval(events, store, cache, {}, {}, deadline); - EXPECT_EQ(verdict, rule_module::verdict_type::none); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 2); + EXPECT_TRUE(contains(events, "id1")); + EXPECT_TRUE(contains(events, "id2")); + } +} + +TEST(TestModuleGrouped, BasePrecedenceMultipleGroupsBlockingMatch) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id1", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id2", "name", std::move(tags), builder.build())); + } + + rule_module_builder mod_builder{base_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id1")); } } + +TEST(TestModuleGrouped, MultipleGroupsRulesAndMatches) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id1", "name", std::move(tags), + builder.build(), std::vector{"block"}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + { + 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.2"}); + + std::unordered_map tags{ + {"type", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id2", "name", std::move(tags), builder.build())); + } + + { + 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.2"}); + + std::unordered_map tags{ + {"type", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id3", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id4", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user, + core_rule::verdict_type::block)); + } + + { + 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.2"}); + + std::unordered_map tags{ + {"type", "type3"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id5", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + { + 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.2"}); + + std::unordered_map tags{ + {"type", "type4"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id6", "name", std::move(tags), builder.build())); + } + + { + 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", "type5"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id7", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user, + core_rule::verdict_type::block)); + } + + { + 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", "type6"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id8", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::base, + core_rule::verdict_type::block)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + { + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.2")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 4); + EXPECT_TRUE(contains(events, "id2")); + EXPECT_TRUE(contains(events, "id3")); + EXPECT_TRUE(contains(events, "id5")); + EXPECT_TRUE(contains(events, "id6")); + } +} + +TEST(TestModuleGrouped, MultipleGroupsSingleMatchPerGroup) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id2", "name", std::move(tags), builder.build())); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id3", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id4", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + { + 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", "type3"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id5", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + { + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::monitor); + EXPECT_EQ(events.size(), 3); + + EXPECT_TRUE(contains(events, "id1")); + EXPECT_TRUE(contains(events, "id3")); + EXPECT_TRUE(contains(events, "id5")); + } +} + +TEST(TestModuleGrouped, MultipleGroupsOnlyBlockingMatch) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id1", "name", std::move(tags), builder.build())); + } + + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back( + std::make_shared("id2", "name", std::move(tags), builder.build())); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id3", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user, + core_rule::verdict_type::block)); + } + + { + 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", "type2"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id4", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + { + 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", "type3"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared("id5", "name", std::move(tags), + builder.build(), std::vector{}, true, core_rule::source_type::user)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + { + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::block); + EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id3")); + } +} + +TEST(TestModuleGrouped, DisabledRules) +{ + std::vector> rules; + { + 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", "type1"}, {"category", "category"}}; + + rules.emplace_back(std::make_shared( + "id1", "name", std::move(tags), builder.build(), std::vector{}, false)); + } + + rule_module_builder mod_builder{user_rule_precedence, type_grouping_key}; + for (const auto &rule : rules) { mod_builder.insert(rule.get()); } + + auto mod = mod_builder.build(); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline = endless_timer(); + auto verdict = mod.eval(events, store, cache, {}, {}, deadline); + EXPECT_EQ(verdict, rule_module::verdict_type::none); + } +} + +TEST(TestModuleGrouped, NonExpiringModule) +{ + 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()); + + rule_module_builder mod_builder{ + base_rule_precedence, type_grouping_key, rule_module::expiration_policy::non_expiring}; + mod_builder.insert(rule.get()); + + auto mod = mod_builder.build(); + EXPECT_FALSE(mod.may_expire()); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline{0s}; + mod.eval(events, store, cache, {}, {}, deadline); + + EXPECT_EQ(events.size(), 1); + EXPECT_TRUE(contains(events, "id")); + } +} + +TEST(TestModuleGrouped, ExpiringModule) +{ + 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()); + + rule_module_builder mod_builder{ + base_rule_precedence, type_grouping_key, rule_module::expiration_policy::expiring}; + mod_builder.insert(rule.get()); + + auto mod = mod_builder.build(); + EXPECT_TRUE(mod.may_expire()); + + rule_module_cache cache; + mod.init_cache(cache); + + ddwaf::object_store store; + { + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "http.client_ip", ddwaf_object_string(&tmp, "192.168.0.1")); + + store.insert(root); + + std::vector events; + ddwaf::timer deadline{0s}; + EXPECT_THROW(mod.eval(events, store, cache, {}, {}, deadline), ddwaf::timeout_exception); + } +} + } // namespace From 8a4279f7438511babf98bbd461cc138073e3b6b4 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 1 Dec 2024 21:39:33 +0000 Subject: [PATCH 27/32] Review comments --- src/builder/module_builder.hpp | 45 +++++++++++++++++++++++++++++----- src/clock.hpp | 14 ++++++++--- src/context.cpp | 3 +-- src/context.hpp | 4 +-- src/interface.cpp | 4 +-- src/rule.hpp | 2 +- src/ruleset.hpp | 2 +- 7 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/builder/module_builder.hpp b/src/builder/module_builder.hpp index 8f76d6979..2a5c9ff1f 100644 --- a/src/builder/module_builder.hpp +++ b/src/builder/module_builder.hpp @@ -26,7 +26,7 @@ inline bool base_rule_precedence( } inline std::string_view type_grouping_key(const core_rule *rule) { return rule->get_type(); } -inline constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; } +constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; } // The module builder can be used to build a single module class rule_module_builder { @@ -71,21 +71,54 @@ class rule_module_set_builder { const std::vector> &rules); protected: + /* Rules Ordering + * --------------------------- + * network_acl: (ignore timeout) + * - Collection: [order by: verdict (block then monitor then none), source (base then user)] + * + * authentication_acl: (ignore timeout) + * - Collection: [order by: verdict, source (base then user)] + * + * custom_acl: + * - Collection: [order by: verdict, source (user then base)] + * + * configuration: + * - Collection: [order by: verdict, source (user then base)] + * + * business_logic: + * - Collection: [order by: verdict, source (user then base)] + * + * rasp: + * - Collection: [order by: verdict, source (base then user)] + * + * waf: + * - Collection: [verdict=block, source=user, type=] + * - Collection: [verdict=block, source=user, type=...] + * - Collection: [verdict=block, source=base, type=] + * - Collection: [verdict=block, source=base, type=...] + * - Collection: [verdict=monitor, source=user, type=] + * - Collection: [verdict=monitor, source=user, type=...] + * - Collection: [verdict=monitor, source=base, type=] + * - Collection: [verdict=monitor, source=base, type=...] + * + * Note: Non expiring modules should always be placed before expiring modules. + */ + std::array builders_{{ // Network-ACL {base_rule_precedence, null_grouping_key, rule_module::expiration_policy::non_expiring}, // Authentication-ACL {base_rule_precedence, null_grouping_key, rule_module::expiration_policy::non_expiring}, // Custom-ACL - {user_rule_precedence, null_grouping_key}, + {user_rule_precedence, null_grouping_key, rule_module::expiration_policy::expiring}, // Configuration - {user_rule_precedence, null_grouping_key}, + {user_rule_precedence, null_grouping_key, rule_module::expiration_policy::expiring}, // Business logic - {user_rule_precedence, null_grouping_key}, + {user_rule_precedence, null_grouping_key, rule_module::expiration_policy::expiring}, // RASP - {base_rule_precedence, null_grouping_key}, + {base_rule_precedence, null_grouping_key, rule_module::expiration_policy::expiring}, // WAF - {user_rule_precedence, type_grouping_key}, + {user_rule_precedence, type_grouping_key, rule_module::expiration_policy::expiring}, }}; }; diff --git a/src/clock.hpp b/src/clock.hpp index 2342496bd..c1d670588 100644 --- a/src/clock.hpp +++ b/src/clock.hpp @@ -61,9 +61,17 @@ template class base_timer { static monotonic_clock::time_point add_saturated( monotonic_clock::time_point augend, std::chrono::nanoseconds addend) { - return (addend > (monotonic_clock::time_point::max() - augend)) - ? monotonic_clock::time_point::max() - : augend + addend; + using duration = monotonic_clock::duration; + + duration::rep augend_count = augend.time_since_epoch().count(); + duration::rep addend_count = addend.count(); + duration::rep result; + + if (__builtin_add_overflow(augend_count, addend_count, &result)) { + return monotonic_clock::time_point::max(); + } + + return monotonic_clock::time_point(duration(result)); } monotonic_clock::time_point start_; diff --git a/src/context.cpp b/src/context.cpp index 773d9b209..9dca5afc5 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -77,8 +77,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; } diff --git a/src/context.hpp b/src/context.hpp index 68fc0e796..4392da9ea 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -87,11 +87,11 @@ 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 + // Cache of modules to avoid processing once a result has been obtained std::array rule_module_cache_; }; diff --git a/src/interface.cpp b/src/interface.cpp index db84024ca..8a2a45a70 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -234,9 +234,7 @@ DDWAF_RET_CODE ddwaf_run(ddwaf_context context, ddwaf_object *persistent_data, // The timers will actually count nanoseconds, std::chrono doesn't // deal well with durations being beyond range. constexpr uint64_t max_timeout_ms = std::chrono::nanoseconds::max().count() / 1000; - if (timeout > max_timeout_ms) { - timeout = max_timeout_ms; - } + timeout = std::min(timeout, max_timeout_ms); return context->run(persistent, ephemeral, res, timeout); } catch (const std::exception &e) { diff --git a/src/rule.hpp b/src/rule.hpp index 8e9e92c04..af28bc288 100644 --- a/src/rule.hpp +++ b/src/rule.hpp @@ -134,7 +134,7 @@ class core_rule { std::vector actions_; // Frequently accessed tags - std::string type_; + std::string_view type_; rule_module_category mod_; // Tags provided through rules_override diff --git a/src/ruleset.hpp b/src/ruleset.hpp index 53f34f1e0..b5dda5253 100644 --- a/src/ruleset.hpp +++ b/src/ruleset.hpp @@ -23,7 +23,7 @@ namespace ddwaf { struct ruleset { - // OLINTNEXTLINE(bugprone-easily-swappable-parameters) + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) void insert_rules(const std::vector> &base, const std::vector> &user) { From ab843af6ecc9177ce2033e3ecb01d9624be57e18 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Sun, 1 Dec 2024 21:56:06 +0000 Subject: [PATCH 28/32] Review comments --- src/builder/module_builder.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/builder/module_builder.hpp b/src/builder/module_builder.hpp index 2a5c9ff1f..50405afa2 100644 --- a/src/builder/module_builder.hpp +++ b/src/builder/module_builder.hpp @@ -25,8 +25,14 @@ inline bool base_rule_precedence( return left < right; } -inline std::string_view type_grouping_key(const core_rule *rule) { return rule->get_type(); } -constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) { return {}; } +[[gnu::const]] inline std::string_view type_grouping_key(const core_rule *rule) +{ + return rule->get_type(); +} +[[gnu::const]] constexpr std::string_view null_grouping_key(const core_rule * /*rule*/) +{ + return {}; +} // The module builder can be used to build a single module class rule_module_builder { From a9c3e7da0f568dd05faadc418b4c19079d66054d Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 2 Dec 2024 08:57:13 +0000 Subject: [PATCH 29/32] Fix clock compilation --- src/clock.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/clock.hpp b/src/clock.hpp index c1d670588..05fbdc917 100644 --- a/src/clock.hpp +++ b/src/clock.hpp @@ -9,6 +9,12 @@ #include #include +#if defined __has_builtin +# if __has_builtin(__builtin_add_overflow) +# define HAS_BUILTIN_ADD_OVERFLOW +# endif +#endif + namespace ddwaf { #ifndef __linux__ using monotonic_clock = std::chrono::steady_clock; @@ -61,6 +67,7 @@ template class base_timer { static monotonic_clock::time_point add_saturated( monotonic_clock::time_point augend, std::chrono::nanoseconds addend) { +#ifdef HAS_BUILTIN_ADD_OVERFLOW using duration = monotonic_clock::duration; duration::rep augend_count = augend.time_since_epoch().count(); @@ -72,6 +79,11 @@ template class base_timer { } return monotonic_clock::time_point(duration(result)); +#else + return (addend > (monotonic_clock::time_point::max() - augend)) + ? monotonic_clock::time_point::max() + : augend + addend; +#endif } monotonic_clock::time_point start_; From 44f163467cc072a4df21c2929da34955bb4de2cd Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:39:35 +0000 Subject: [PATCH 30/32] Lint fix --- src/interface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/interface.cpp b/src/interface.cpp index 8a2a45a70..da6bab70c 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -4,6 +4,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2021 Datadog, Inc. +#include #include #include #include From 35cb184b6bcd1057a758345c4e415471137e73f5 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:43:29 +0000 Subject: [PATCH 31/32] Adjust comment --- src/builder/module_builder.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builder/module_builder.hpp b/src/builder/module_builder.hpp index 50405afa2..a93f2ef8f 100644 --- a/src/builder/module_builder.hpp +++ b/src/builder/module_builder.hpp @@ -80,7 +80,7 @@ class rule_module_set_builder { /* Rules Ordering * --------------------------- * network_acl: (ignore timeout) - * - Collection: [order by: verdict (block then monitor then none), source (base then user)] + * - Collection: [order by: verdict (block then monitor), source (base then user)] * * authentication_acl: (ignore timeout) * - Collection: [order by: verdict, source (base then user)] From d88012685f3f7d3e93c2c7e697cc6fdefbeecd3b Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:28:39 +0000 Subject: [PATCH 32/32] Review comments --- src/builder/module_builder.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/builder/module_builder.cpp b/src/builder/module_builder.cpp index 998367b66..53fc7e294 100644 --- a/src/builder/module_builder.cpp +++ b/src/builder/module_builder.cpp @@ -26,11 +26,10 @@ rule_module rule_module_builder::build() const auto rverdict = right->get_verdict(); const auto lsource = left->get_source(); const auto rsource = right->get_source(); - std::string_view const lkey = grouping_key_fn_(left); - std::string_view const rkey = grouping_key_fn_(right); return lverdict > rverdict || - (lverdict == rverdict && (source_precedence_fn_(lsource, rsource) || - (lsource == rsource && lkey < rkey))); + (lverdict == rverdict && + (source_precedence_fn_(lsource, rsource) || + (lsource == rsource && grouping_key_fn_(left) < grouping_key_fn_(right)))); }; // Sort first