Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module-based rule evaluation precedence #353

Merged
merged 35 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
fbee78f
Rename rule to core_rule
Anilm3 Oct 28, 2024
ae1c1e9
Add target_address abstraction for addresses
Anilm3 Oct 28, 2024
8aa2726
Add more headers
Anilm3 Oct 28, 2024
32c3af4
Avoid spaceship
Anilm3 Oct 28, 2024
d85ef0e
Merge branch 'master' into anilm3/rule-precedence
Anilm3 Nov 12, 2024
e0b5e37
Fixes
Anilm3 Nov 12, 2024
0ee9bcb
Merge branch 'master' into anilm3/rule-precedence
Anilm3 Nov 14, 2024
bb22c56
Minor change
Anilm3 Nov 18, 2024
ba4907a
Add modules and builder
Anilm3 Nov 19, 2024
c722ca1
Merge branch 'master' into anilm3/rule-precedence
Anilm3 Nov 19, 2024
792265a
Builder and other related changes
Anilm3 Nov 21, 2024
ec2ffa0
Fix tests, remove collections, other changes...
Anilm3 Nov 22, 2024
472c898
Fix compilation in clang
Anilm3 Nov 22, 2024
74980d0
Short-circuit modules
Anilm3 Nov 23, 2024
017c76d
Prevent network and authentication ACLs from expiration
Anilm3 Nov 23, 2024
ce3ee1f
Allow non-expiring rules to be evaluated
Anilm3 Nov 24, 2024
37f9bb4
Update v1 parser, change clock, other small changes
Anilm3 Nov 24, 2024
216e045
Fix endless timer
Anilm3 Nov 25, 2024
6144b41
Minor change
Anilm3 Nov 25, 2024
c8a3ff1
Minor fix
Anilm3 Nov 25, 2024
5815ee8
Add validator tests to verify rule precedence per module
Anilm3 Nov 25, 2024
1b3a53d
Add more tests, improvements, etc
Anilm3 Nov 25, 2024
560d9d5
Minor change
Anilm3 Nov 25, 2024
ba3d3c8
Allow multiple matches in monitor mode
Anilm3 Nov 25, 2024
c01b719
Tests
Anilm3 Nov 26, 2024
2260152
Test
Anilm3 Nov 26, 2024
84d31f7
Tests and improvements
Anilm3 Nov 27, 2024
3e8618f
More tests
Anilm3 Nov 27, 2024
8551f2e
More tests
Anilm3 Nov 27, 2024
8a4279f
Review comments
Anilm3 Dec 1, 2024
ab843af
Review comments
Anilm3 Dec 1, 2024
a9c3e7d
Fix clock compilation
Anilm3 Dec 2, 2024
44f1634
Lint fix
Anilm3 Dec 2, 2024
35cb184
Adjust comment
Anilm3 Dec 2, 2024
d880126
Review comments
Anilm3 Dec 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ perf/test_files/breakdown.numbers
.vscode
*.swp
*.swo
*.swn
3 changes: 2 additions & 1 deletion cmake/objects.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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
${libddwaf_SOURCE_DIR}/src/ip_utils.cpp
Expand All @@ -22,6 +22,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
Expand Down
76 changes: 76 additions & 0 deletions src/builder/module_builder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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 <algorithm>
#include <array>
#include <cstddef>
#include <memory>
#include <string_view>
#include <utility>
#include <vector>

#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;
}
using rule_collection = rule_module::rule_collection;
collections_.emplace_back(rule_collection{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_), policy_};
}

std::array<rule_module, rule_module_count> rule_module_set_builder::build(
const std::vector<std::shared_ptr<core_rule>> &rules)
{
std::array<rule_module, rule_module_count> all_modules;

for (const auto &rule : rules) {
auto &builder = builders_[static_cast<std::size_t>(rule->get_module())];
builder.insert(rule.get());
}

for (std::size_t i = 0; i < builders_.size(); ++i) { all_modules[i] = builders_[i].build(); }

return all_modules;
}

} // namespace ddwaf
92 changes: 92 additions & 0 deletions src/builder/module_builder.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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 "module_category.hpp"
#include "rule.hpp"

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:
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, 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;
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;

void insert(core_rule *rule) { rules_.emplace_back(rule); }

rule_module build();

protected:
source_precedence_fn_type source_precedence_fn_;
grouping_key_fn_type grouping_key_fn_;
std::vector<core_rule *> rules_;
std::vector<rule_module::rule_collection> collections_;
expiration_policy policy_;
};

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;

std::array<rule_module, rule_module_count> build(
const std::vector<std::shared_ptr<core_rule>> &rules);

protected:
std::array<rule_module_builder, rule_module_count> 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},
// 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},
}};
};

} // namespace ddwaf
30 changes: 20 additions & 10 deletions src/clock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +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 <std::size_t SyscallPeriod = 16> 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_(start_ + exp), syscall_period_(syscall_period)
explicit base_timer(std::chrono::nanoseconds exp)
: start_(monotonic_clock::now()), end_(add_saturated(start_, exp))
{}

bool expired()
Expand All @@ -44,7 +44,7 @@ class timer {
if (end_ <= monotonic_clock::now()) {
expired_ = true;
} else {
calls_ = syscall_period_;
calls_ = SyscallPeriod;
}
}
return expired_;
Expand All @@ -58,12 +58,22 @@ class timer {
}

protected:
constexpr static uint32_t default_syscall_period{16};
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;
}

monotonic_clock::time_point start_;
monotonic_clock::time_point end_;
const uint32_t syscall_period_;
uint32_t calls_{1};
bool expired_{false};
};

using timer = base_timer<16>;

inline timer endless_timer() { return timer{std::chrono::nanoseconds::max()}; }

} // namespace ddwaf
121 changes: 0 additions & 121 deletions src/collection.cpp

This file was deleted.

Loading