#include <cstdint>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#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 "negated_scalar_condition.hpp"
#include "object.hpp"
#include "object_store.hpp"
#include "transformer/base.hpp"
#include "transformer/manager.hpp"
#include "utils.hpp"
using namespace std::literals;
namespace ddwaf {
namespace {
enum class match_result : uint8_t {
unknown, no_match, match };
template <typename Iterator>
match_result 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 match_result::no_match;
}
DDWAF_TRACE("Target {} matched parameter value {}", address, highlight);
return match_result::match;
}
}
}
auto [res, highlight] = matcher.match(src);
if (!res) {
return match_result::no_match;
}
DDWAF_TRACE("Target {} matched parameter value {}", address, highlight);
return match_result::match;
}
template <typename Iterator>
match_result eval_target(Iterator &it, std::string_view address, const matcher::base &matcher,
const std::span<const transformer_id> &transformers, ddwaf::timer &deadline)
{
auto result = match_result::unknown;
for (; it; ++it) {
if (deadline.expired()) {
throw ddwaf::timeout_exception();
}
if (!matcher.is_supported_type(it.type())) {
continue;
}
result = eval_object(it, address, matcher, transformers);
if (result == match_result::match) {
break;
}
}
return result;
}
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 negated_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
{
if (deadline.expired()) {
throw ddwaf::timeout_exception();
}
const auto *matcher = get_matcher(matcher_, data_id_, dynamic_matchers);
if (matcher == nullptr) {
return {};
}
if (cache.targets.size() != 1) {
cache.targets.assign(1, object_cache_key{});
}
auto object = store.get_target(target_.index);
if (!object.has_value() || object == cache.targets[0]) {
return {};
}
cache.targets[0] = object;
auto target_object = object.find_key_path(target_.key_path, objects_excluded);
if (!target_object.has_value()) {
return false;
}
if (target_.source == data_source::keys) {
if (!target_object.is_map()) {
return false;
}
key_iterator it(target_object, {}, objects_excluded);
auto result = eval_target(it, target_.name, *matcher, target_.transformers, deadline);
if (result == match_result::no_match) {
cache.match = {{.args = {{.name = "input"sv,
.resolved = {},
.address = target_.name,
.key_path = convert_key_path(target_.key_path)}},
.highlights = {},
.operator_name = matcher->negated_name(),
.operator_value = matcher->to_string()}};
return true;
}
} else {
value_iterator it(target_object, {}, objects_excluded);
auto result = eval_target(it, target_.name, *matcher, target_.transformers, deadline);
if (result == match_result::no_match) {
if (target_object.is_array() && target_object.size() == 1) {
target_object = target_object.at_value(0);
}
std::vector<dynamic_string> highlights;
auto resolved = target_object.convert<std::string>();
if (!resolved.empty()) {
highlights.emplace_back(resolved);
}
cache.match = {{.args = {{.name = "input"sv,
.resolved = std::move(resolved),
.address = target_.name,
.key_path = convert_key_path(target_.key_path)}},
.highlights = std::move(highlights),
.operator_name = matcher->negated_name(),
.operator_value = matcher->to_string()}};
return true;
}
}
return false;
}
}