#include <memory>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include "clock.hpp"
#include "condition/base.hpp"
#include "dynamic_string.hpp"
#include "exception.hpp"
#include "exclusion/common.hpp"
#include "iterator.hpp"
#include "log.hpp"
#include "matcher/base.hpp"
#include "object.hpp"
#include "object_store.hpp"
#include "scalar_condition.hpp"
#include "transformer/base.hpp"
#include "transformer/manager.hpp"
using namespace std::literals;
namespace ddwaf {
namespace {
template <typename Iterator>
std::optional<condition_match> eval_object(Iterator &it, std::string_view address,
const matcher::base &matcher, const std::span<const transformer_id> &transformers)
{
const object_view src = *it;
if (src.is_string()) {
if (!transformers.empty()) {
auto transformed = transformer::manager::transform(src, transformers);
if (transformed) {
auto transformed_sv = static_cast<std::string_view>(transformed.value());
auto [res, highlight] = matcher.match(transformed_sv);
if (!res) {
return {};
}
DDWAF_TRACE("Target {} matched parameter value {}", address, highlight);
return {{{{"input"sv, static_cast<dynamic_string>(transformed.value()), address,
it.get_current_path()}},
{std::move(highlight)}, matcher.name(), matcher.to_string()}};
}
}
}
auto [res, highlight] = matcher.match(src);
if (!res) {
return {};
}
DDWAF_TRACE("Target {} matched parameter value {}", address, highlight);
return {{{{"input"sv, src.convert<std::string>(), address, it.get_current_path()}},
{std::move(highlight)}, matcher.name(), matcher.to_string()}};
}
template <typename Iterator>
std::optional<condition_match> eval_target(Iterator &it, std::string_view address,
const matcher::base &matcher, const std::span<const transformer_id> &transformers,
ddwaf::timer &deadline)
{
for (; it; ++it) {
if (deadline.expired()) {
throw ddwaf::timeout_exception();
}
if (!matcher.is_supported_type(it.type())) {
continue;
}
auto match = eval_object(it, address, matcher, transformers);
if (match) {
return match;
}
}
return {};
}
const matcher::base *get_matcher(const std::unique_ptr<matcher::base> &matcher,
const std::string &data_id, const matcher_mapper &dynamic_matchers)
{
if (matcher || data_id.empty()) {
return matcher.get();
}
auto it = dynamic_matchers.find(data_id);
if (it != dynamic_matchers.end()) {
return it->second.get();
}
return nullptr;
}
}
bool scalar_condition::eval(condition_cache &cache, const object_store &store,
const object_set_ref &objects_excluded, const matcher_mapper &dynamic_matchers,
ddwaf::timer &deadline) const
{
const auto *matcher = get_matcher(matcher_, data_id_, dynamic_matchers);
if (matcher == nullptr) {
return {};
}
if (cache.targets.size() != targets_.size()) {
cache.targets.assign(targets_.size(), {});
}
for (unsigned i = 0; i < targets_.size(); ++i) {
if (deadline.expired()) {
throw ddwaf::timeout_exception();
}
const auto &target = targets_[i];
auto object = store.get_target(target.index);
if (!object.has_value() || object == cache.targets[i]) {
continue;
}
cache.targets[i] = object;
std::optional<condition_match> match;
if (target.source == data_source::keys) {
key_iterator it(object, target.key_path, objects_excluded);
match = eval_target(it, target.name, *matcher, target.transformers, deadline);
} else {
value_iterator it(object, target.key_path, objects_excluded);
match = eval_target(it, target.name, *matcher, target.transformers, deadline);
}
if (match.has_value()) {
cache.match = std::move(match);
return true;
}
}
return false;
}
}