#include <stack>
#include <string_view>
#include <tuple>
#include <unordered_set>
#include "clock.hpp"
#include "exception.hpp"
#include "exclusion/common.hpp"
#include "exclusion/object_filter.hpp"
#include "log.hpp"
#include "object.hpp"
#include "object_store.hpp"
namespace ddwaf {
namespace {
void iterate_object(const path_trie::traverser &filter, object_view object,
std::unordered_set<object_cache_key> &objects_to_exclude)
{
using state = path_trie::traverser::state;
if (!object.has_value()) {
return;
}
{
const auto filter_state = filter.get_state();
if (filter_state == state::not_found) {
return;
}
if (filter_state == state::found) {
objects_to_exclude.emplace(object);
return;
}
}
if (!object.is_container()) {
return;
}
std::stack<std::tuple<object_view, unsigned, path_trie::traverser>> path_stack;
path_stack.emplace(object, 0, filter);
while (!path_stack.empty()) {
auto &[current_object, current_index, current_trie] = path_stack.top();
if (!object.is_container()) {
DDWAF_DEBUG("This is a bug, the object in the stack is not a container");
path_stack.pop();
continue;
}
bool found_node{false};
for (; current_index < current_object.size(); ++current_index) {
auto [key, child] = current_object.at(current_index);
path_trie::traverser child_traverser{nullptr};
if (!key.has_value()) {
child_traverser = current_trie.descend_wildcard();
} else {
child_traverser = current_trie.descend(key.as<std::string_view>());
}
const auto filter_state = child_traverser.get_state();
if (filter_state == state::found) {
objects_to_exclude.emplace(child);
continue;
}
if (filter_state == state::not_found) {
continue;
}
if (child.is_container()) {
++current_index;
found_node = true;
path_stack.emplace(child, 0, child_traverser);
break;
}
}
if (!found_node) {
path_stack.pop();
}
}
}
}
object_set object_filter::match(
const object_store &store, cache_type &cache, ddwaf::timer &deadline) const
{
object_set objects_to_exclude;
for (const auto &[target, filter] : target_paths_) {
if (deadline.expired()) {
throw ddwaf::timeout_exception();
}
auto object = store.get_target(target);
if (!object.has_value()) {
continue;
}
auto &cached_object = cache[target];
if (cached_object == object) {
continue;
}
cached_object = object;
iterate_object(filter.get_traverser(), object, objects_to_exclude);
}
return objects_to_exclude;
}
}