#include <cstdint>
#include <span>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
#include "argument_retriever.hpp"
#include "clock.hpp"
#include "condition/base.hpp"
#include "condition/exists.hpp"
#include "exception.hpp"
#include "exclusion/common.hpp"
#include "object.hpp"
#include "object_type.hpp"
#include "utils.hpp"
namespace ddwaf {
namespace {
enum class search_outcome : uint8_t { found, not_found, unknown };
object_view get_key(object_view root, const std::variant<std::string, int64_t> &key)
{
if (root.is_map() && std::holds_alternative<std::string>(key)) {
return root.find(std::get<std::string>(key));
}
if (root.is_array() && std::holds_alternative<int64_t>(key)) {
auto index = std::get<int64_t>(key);
if (index >= 0 && root.size<int64_t>() > index) {
return root.at_value(index);
}
if (index < 0 && root.size<int64_t>() + index >= 0) {
return root.at_value(root.size<int64_t>() + index);
}
}
return {};
}
search_outcome exists(object_view root,
std::span<const std::variant<std::string, int64_t>> key_path,
const object_set_ref &objects_excluded)
{
if (key_path.empty()) {
return search_outcome::found;
}
auto it = key_path.begin();
if (std::holds_alternative<std::string>(*it) && root.type() != object_type::map) {
return search_outcome::not_found;
}
if (std::holds_alternative<int64_t>(*it) && root.type() != object_type::array) {
return search_outcome::not_found;
}
while ((root = get_key(root, *it)).has_value()) {
if (objects_excluded.contains(root)) {
return search_outcome::unknown;
}
if (++it == key_path.end()) {
return search_outcome::found;
}
if (std::holds_alternative<std::string>(*it) && root.type() != object_type::map) {
return search_outcome::not_found;
}
if (std::holds_alternative<int64_t>(*it) && root.type() != object_type::array) {
return search_outcome::not_found;
}
}
return search_outcome::not_found;
}
}
[[nodiscard]] bool exists_condition::eval_impl(const variadic_argument<object_view> &inputs,
condition_cache &cache, const object_set_ref &objects_excluded, ddwaf::timer &deadline) const
{
for (const auto &input : inputs) {
if (deadline.expired()) {
throw ddwaf::timeout_exception();
}
if (exists(input.value, input.key_path, objects_excluded) == search_outcome::found) {
const std::vector<std::variant<std::string_view, int64_t>> key_path;
cache.match = {{.args = {{.name = "input",
.resolved = {},
.address = input.address,
.key_path = convert_key_path(input.key_path)}},
.highlights = {},
.operator_name = "exists",
.operator_value = {}}};
return true;
}
}
return false;
}
[[nodiscard]] bool negated_exists_condition::eval_impl(const unary_argument<object_view> &input,
condition_cache &cache, const object_set_ref &objects_excluded,
ddwaf::timer & ) const
{
if (exists(input.value, input.key_path, objects_excluded) != search_outcome::not_found) {
return false;
}
cache.match = {{.args = {{.name = "input",
.resolved = {},
.address = input.address,
.key_path = convert_key_path(input.key_path)}},
.highlights = {},
.operator_name = "!exists",
.operator_value = {}}};
return true;
}
}