From 4ff6adabf46b96f2d788ca59cde554170981e02f Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:20:49 +0100 Subject: [PATCH] Fixes and more tests --- src/condition/scalar_condition.cpp | 19 ++- src/condition/scalar_condition.hpp | 10 +- tests/condition/scalar_condition_test.cpp | 111 ++++++++++++++++ .../scalar_negated_condition_test.cpp | 123 ++++++++++++++++++ tests/expression_test.cpp | 37 ++++++ tests/test_utils.hpp | 25 +++- 6 files changed, 304 insertions(+), 21 deletions(-) create mode 100644 tests/condition/scalar_condition_test.cpp create mode 100644 tests/condition/scalar_negated_condition_test.cpp diff --git a/src/condition/scalar_condition.cpp b/src/condition/scalar_condition.cpp index 1856e399f..0bb677ffd 100644 --- a/src/condition/scalar_condition.cpp +++ b/src/condition/scalar_condition.cpp @@ -196,10 +196,7 @@ eval_result scalar_negated_condition::eval(condition_cache &cache, const object_ cache.targets.assign(1, nullptr); } - // This type of scalar condition only accepts a single target - const auto &target = targets_[0]; - - auto [object, attr] = store.get_target(target.index); + auto [object, attr] = store.get_target(target_.index); if (object == nullptr || object == cache.targets[0]) { return {}; } @@ -210,19 +207,19 @@ eval_result scalar_negated_condition::eval(condition_cache &cache, const object_ } bool match = false; - if (target.source == data_source::keys) { - object::key_iterator it(object, target.key_path, objects_excluded, limits_); + if (target_.source == data_source::keys) { + object::key_iterator it(object, target_.key_path, objects_excluded, limits_); match = eval_target( - it, target.name, ephemeral, *matcher, target.transformers, limits_, deadline); + it, target_.name, ephemeral, *matcher, target_.transformers, limits_, deadline); } else { - object::value_iterator it(object, target.key_path, objects_excluded, limits_); + object::value_iterator it(object, target_.key_path, objects_excluded, limits_); match = eval_target( - it, target.name, ephemeral, *matcher, target.transformers, limits_, deadline); + it, target_.name, ephemeral, *matcher, target_.transformers, limits_, deadline); } if (!match) { - cache.match = {{{{"input"sv, object_to_string(*object), target.name, - {target.key_path.begin(), target.key_path.end()}}}, + cache.match = {{{{"input"sv, object_to_string(*object), target_.name, + {target_.key_path.begin(), target_.key_path.end()}}}, {}, matcher_name_, matcher->to_string(), ephemeral}}; return {true, ephemeral}; } diff --git a/src/condition/scalar_condition.hpp b/src/condition/scalar_condition.hpp index e98d8b481..ebd499a4d 100644 --- a/src/condition/scalar_condition.hpp +++ b/src/condition/scalar_condition.hpp @@ -65,7 +65,11 @@ class scalar_negated_condition : public base_condition { throw std::invalid_argument("Matcher initialised without arguments"); } - targets_ = std::move(args[0].targets); + if (args[0].targets.size() > 1) { + throw std::invalid_argument("Negated matchers don't support variadic arguments"); + } + + target_ = std::move(args[0].targets[0]); } eval_result eval(condition_cache &cache, const object_store &store, @@ -75,7 +79,7 @@ class scalar_negated_condition : public base_condition { void get_addresses(std::unordered_map &addresses) const override { - for (const auto &target : targets_) { addresses.emplace(target.index, target.name); } + addresses.emplace(target_.index, target_.name); } static constexpr auto arguments() @@ -86,7 +90,7 @@ class scalar_negated_condition : public base_condition { protected: std::unique_ptr matcher_; std::string data_id_; - std::vector targets_; + condition_target target_; std::string matcher_name_; const object_limits limits_; }; diff --git a/tests/condition/scalar_condition_test.cpp b/tests/condition/scalar_condition_test.cpp new file mode 100644 index 000000000..d331a5e68 --- /dev/null +++ b/tests/condition/scalar_condition_test.cpp @@ -0,0 +1,111 @@ +// 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 "../test.hpp" +#include "condition/scalar_condition.hpp" +#include "matcher/regex_match.hpp" +#include "utils.hpp" + +using namespace ddwaf; +using namespace std::literals; + +namespace { + +template condition_parameter gen_variadic_param(Args... addresses) +{ + return {{{std::string{addresses}, get_target_index(addresses)}...}}; +} +TEST(TestScalarCondition, TooManyAddressesInConstructor) +{ + EXPECT_THROW((scalar_condition{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw"), + gen_variadic_param("server.request.query")}}), + std::invalid_argument); +} + +TEST(TestScalarCondition, NoAddressesInConstructor) +{ + EXPECT_THROW((scalar_condition{std::make_unique(".*", 0, true), {}, {}}), + std::invalid_argument); +} + +TEST(TestScalarCondition, NoMatch) +{ + scalar_condition cond{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw")}}; + + ddwaf_object tmp; + ddwaf_object root; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.uri_raw", ddwaf_object_invalid(&tmp)); + + object_store store; + store.insert(root); + + ddwaf::timer deadline{2s}; + condition_cache cache; + auto res = cond.eval(cache, store, {}, {}, deadline); + ASSERT_FALSE(res.outcome); + ASSERT_FALSE(res.ephemeral); +} + +TEST(TestScalarCondition, SimpleMatch) +{ + scalar_condition cond{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw")}}; + + ddwaf_object tmp; + ddwaf_object root; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.uri_raw", ddwaf_object_string(&tmp, "hello")); + + object_store store; + store.insert(root); + + ddwaf::timer deadline{2s}; + condition_cache cache; + auto res = cond.eval(cache, store, {}, {}, deadline); + ASSERT_TRUE(res.outcome); + ASSERT_FALSE(res.ephemeral); +} + +TEST(TestScalarCondition, CachedMatch) +{ + scalar_condition cond{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw")}, {}}; + + ddwaf::timer deadline{2s}; + condition_cache cache; + + ddwaf_object tmp; + ddwaf_object root; + + { + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.uri_raw", ddwaf_object_string(&tmp, "hello")); + + object_store store; + store.insert(root); + + auto res = cond.eval(cache, store, {}, {}, deadline); + ASSERT_TRUE(res.outcome); + ASSERT_FALSE(res.ephemeral); + } + + { + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.uri_raw", ddwaf_object_string(&tmp, "hello")); + + object_store store; + store.insert(root); + + auto res = cond.eval(cache, store, {}, {}, deadline); + ASSERT_FALSE(res.outcome); + ASSERT_FALSE(res.ephemeral); + } +} + +} // namespace diff --git a/tests/condition/scalar_negated_condition_test.cpp b/tests/condition/scalar_negated_condition_test.cpp new file mode 100644 index 000000000..7bc5c10c2 --- /dev/null +++ b/tests/condition/scalar_negated_condition_test.cpp @@ -0,0 +1,123 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2021 Datadog, Inc. + +#include "../test.hpp" +#include "condition/scalar_condition.hpp" +#include "matcher/regex_match.hpp" +#include "utils.hpp" + +using namespace ddwaf; +using namespace std::literals; + +namespace { + +template condition_parameter gen_variadic_param(Args... addresses) +{ + return {{{std::string{addresses}, get_target_index(addresses)}...}}; +} + +TEST(TestScalarNegatedCondition, VariadicTargetInConstructor) +{ + EXPECT_THROW( + (scalar_negated_condition{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw", "server.request.query")}, {}}), + std::invalid_argument); +} + +TEST(TestScalarNegatedCondition, TooManyAddressesInConstructor) +{ + EXPECT_THROW( + (scalar_negated_condition{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw"), + gen_variadic_param("server.request.query")}, + {}}), + std::invalid_argument); +} + +TEST(TestScalarNegatedCondition, NoAddressesInConstructor) +{ + EXPECT_THROW((scalar_negated_condition{ + std::make_unique(".*", 0, true), {}, {}, {}}), + std::invalid_argument); +} + +TEST(TestScalarNegatedCondition, NoMatch) +{ + scalar_negated_condition cond{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw")}, {}}; + + ddwaf_object tmp; + ddwaf_object root; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.uri_raw", ddwaf_object_string(&tmp, "hello")); + + object_store store; + store.insert(root); + + ddwaf::timer deadline{2s}; + condition_cache cache; + auto res = cond.eval(cache, store, {}, {}, deadline); + ASSERT_FALSE(res.outcome); + ASSERT_FALSE(res.ephemeral); +} + +TEST(TestScalarNegatedCondition, SimpleMatch) +{ + scalar_negated_condition cond{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw")}, {}}; + + ddwaf_object tmp; + ddwaf_object root; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.uri_raw", ddwaf_object_invalid(&tmp)); + + object_store store; + store.insert(root); + + ddwaf::timer deadline{2s}; + condition_cache cache; + auto res = cond.eval(cache, store, {}, {}, deadline); + ASSERT_TRUE(res.outcome); + ASSERT_FALSE(res.ephemeral); +} + +TEST(TestScalarNegatedCondition, CachedMatch) +{ + scalar_negated_condition cond{std::make_unique(".*", 0, true), {}, + {gen_variadic_param("server.request.uri_raw")}, {}}; + + ddwaf::timer deadline{2s}; + condition_cache cache; + + ddwaf_object tmp; + ddwaf_object root; + + { + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.uri_raw", ddwaf_object_invalid(&tmp)); + + object_store store; + store.insert(root); + + auto res = cond.eval(cache, store, {}, {}, deadline); + ASSERT_TRUE(res.outcome); + ASSERT_FALSE(res.ephemeral); + } + + { + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.uri_raw", ddwaf_object_invalid(&tmp)); + + object_store store; + store.insert(root); + + auto res = cond.eval(cache, store, {}, {}, deadline); + ASSERT_FALSE(res.outcome); + ASSERT_FALSE(res.ephemeral); + } +} + +} // namespace diff --git a/tests/expression_test.cpp b/tests/expression_test.cpp index f968738ba..2c1807334 100644 --- a/tests/expression_test.cpp +++ b/tests/expression_test.cpp @@ -49,6 +49,43 @@ TEST(TestExpression, SimpleMatch) }}}); } +TEST(TestExpression, SimpleNegatedMatch) +{ + test::expression_builder builder(1); + builder.start_condition(); + builder.add_argument(); + builder.add_target("server.request.query"); + builder.end_condition(".*", 5, true); + + auto expr = builder.build(); + + ddwaf_object root; + ddwaf_object tmp; + ddwaf_object_map(&root); + ddwaf_object_map_add(&root, "server.request.query", ddwaf_object_string(&tmp, "val")); + + ddwaf::object_store store; + store.insert(root); + + ddwaf::timer deadline{2s}; + + expression::cache_type cache; + auto res = expr->eval(cache, store, {}, {}, deadline); + EXPECT_TRUE(res.outcome); + EXPECT_FALSE(res.ephemeral); + + auto matches = expr->get_matches(cache); + EXPECT_EQ(matches.size(), 1); + EXPECT_FALSE(matches[0].ephemeral); + EXPECT_MATCHES(matches, {.op = "!match_regex", + .op_value = ".*", + .highlight = "", + .args = {{ + .value = "val", + .address = "server.request.query", + }}}); +} + TEST(TestExpression, EphemeralMatch) { test::expression_builder builder(1); diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp index 895b91852..01f4d8f02 100644 --- a/tests/test_utils.hpp +++ b/tests/test_utils.hpp @@ -66,21 +66,32 @@ class expression_builder { void start_condition() { arguments_.clear(); } - template + template void end_condition(Args... args) requires std::is_base_of_v { - conditions_.emplace_back( - std::make_unique(std::make_unique(std::forward(args)...), - std::string{}, std::move(arguments_))); + if constexpr (Expected) { + conditions_.emplace_back( + std::make_unique(std::make_unique(std::forward(args)...), + std::string{}, std::move(arguments_))); + } else { + conditions_.emplace_back(std::make_unique( + std::make_unique(std::forward(args)...), std::string{}, + std::move(arguments_), "!" + std::string{T::matcher_name})); + } } - template + template void end_condition_with_data(std::string data_id) requires std::is_base_of_v { - conditions_.emplace_back(std::make_unique( - std::unique_ptr{}, std::move(data_id), std::move(arguments_))); + if constexpr (Expected) { + conditions_.emplace_back(std::make_unique( + std::unique_ptr{}, std::move(data_id), std::move(arguments_))); + } else { + conditions_.emplace_back(std::make_unique( + std::unique_ptr{}, std::move(data_id), std::move(arguments_))); + } } template