#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "clock.hpp"
#include "condition/base.hpp"
#include "exclusion/common.hpp"
#include "expression.hpp"
#include "matcher/base.hpp"
#include "module_category.hpp"
#include "object_store.hpp"
#include "target_address.hpp"
namespace ddwaf {
enum class rule_flags : uint8_t {
none = 0,
generate_event = 1 << 0, keep_outcome = 1 << 1 };
constexpr ddwaf::rule_flags operator|(ddwaf::rule_flags a, ddwaf::rule_flags b) noexcept
{
return static_cast<ddwaf::rule_flags>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
}
constexpr ddwaf::rule_flags operator&(ddwaf::rule_flags a, ddwaf::rule_flags b) noexcept
{
return static_cast<ddwaf::rule_flags>(static_cast<uint8_t>(a) & static_cast<uint8_t>(b));
}
constexpr bool operator==(ddwaf::rule_flags a, ddwaf::rule_flags b) noexcept
{
return static_cast<uint8_t>(a) == static_cast<uint8_t>(b);
}
constexpr bool operator!=(ddwaf::rule_flags a, ddwaf::rule_flags b) noexcept
{
return static_cast<uint8_t>(a) != static_cast<uint8_t>(b);
}
constexpr bool contains(rule_flags set, rule_flags opt) { return (set & opt) != rule_flags::none; }
enum class rule_verdict : uint8_t { none = 0, monitor = 1, block = 2 };
enum class rule_source : uint8_t { base = 1, user = 2 };
struct rule_attribute {
struct input_target {
std::string name;
target_index index; std::vector<std::variant<std::string, int64_t>> key_path;
};
std::variant<input_target, std::string, uint64_t, int64_t, double, bool> value_or_target;
std::string key;
};
struct rule_event {
struct {
std::string_view id;
std::string_view name;
std::reference_wrapper<const std::unordered_map<std::string, std::string>> tags;
} rule;
std::vector<condition_match> matches;
};
struct rule_result {
bool keep{false};
std::optional<rule_event> event{std::nullopt};
std::string_view action_override;
std::reference_wrapper<const std::vector<std::string>> actions;
std::reference_wrapper<const std::vector<rule_attribute>> attributes;
};
class core_rule {
public:
using source_type = rule_source;
using verdict_type = rule_verdict;
using cache_type = expression::cache_type;
core_rule(std::string id, std::string name, std::unordered_map<std::string, std::string> tags, std::shared_ptr<expression> expr, std::vector<std::string> actions = {}, std::vector<rule_attribute> attributes = {}, source_type source = source_type::base, verdict_type verdict = verdict_type::monitor, bool enabled = true, rule_flags flags = rule_flags::generate_event |
rule_flags::keep_outcome )
: enabled_(enabled), flags_(flags), source_(source), verdict_(verdict), id_(std::move(id)),
name_(std::move(name)), tags_(std::move(tags)), actions_(std::move(actions)),
attributes_(std::move(attributes)), expr_(std::move(expr))
{
if (!expr_) {
throw std::invalid_argument("rule constructed with null expression");
}
auto it = tags_.find("module");
if (it != tags_.end()) {
mod_ = string_to_rule_module_category(it->second);
}
type_ = tags_["type"];
}
core_rule(const core_rule &) = delete;
core_rule &operator=(const core_rule &) = delete;
core_rule(core_rule &&rhs) noexcept = default;
core_rule &operator=(core_rule &&rhs) = default;
~core_rule() = default;
std::pair<verdict_type, std::optional<rule_result>> match(const object_store &store,
cache_type &cache, const object_set_ref &objects_excluded,
const matcher_mapper &dynamic_matchers, timer &deadline) const;
[[nodiscard]] bool is_enabled() const { return enabled_; }
[[nodiscard]] std::string_view get_id() const { return id_; }
[[nodiscard]] std::string_view get_name() const { return name_; }
[[nodiscard]] std::string_view get_type() const { return type_; }
[[nodiscard]] rule_module_category get_module() const { return mod_; }
[[nodiscard]] verdict_type get_verdict() const { return verdict_; }
[[nodiscard]] source_type get_source() const { return source_; }
[[nodiscard]] const std::unordered_map<std::string, std::string> &get_tags() const
{
return tags_;
}
[[nodiscard]] const std::vector<std::string> &get_actions() const { return actions_; }
void get_addresses(std::unordered_map<target_index, std::string> &addresses) const
{
expr_->get_addresses(addresses);
for (const auto &attr : attributes_) {
if (std::holds_alternative<rule_attribute::input_target>(attr.value_or_target)) {
const auto &input = std::get<rule_attribute::input_target>(attr.value_or_target);
addresses.emplace(input.index, input.name);
}
}
}
protected:
bool enabled_{true};
rule_flags flags_{rule_flags::generate_event | rule_flags::keep_outcome};
source_type source_;
verdict_type verdict_{verdict_type::monitor};
std::string id_;
std::string name_;
std::unordered_map<std::string, std::string> tags_;
std::vector<std::string> actions_;
std::vector<rule_attribute> attributes_;
std::string_view type_;
rule_module_category mod_{rule_module_category::waf};
std::shared_ptr<expression> expr_;
};
}