libddwaf-src 2.1.0

Source distribution and build integration for DataDog/libddwaf
Documentation
// Unless explicitly stated otherwise all files in this repository are
// dual-licensed under the Apache-2.0 License or BSD-3-Clause License.
//
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2021 Datadog, Inc.

#pragma once

#include <concepts>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
#include <vector>

#include "exclusion/common.hpp"
#include "object.hpp"
#include "object_store.hpp"
#include "traits.hpp"
#include "transformer/base.hpp"

namespace ddwaf {

template <typename T>
concept has_transformers = requires(const T &obj) {
    requires std::same_as<std::remove_cvref_t<decltype(obj.transformers)>,
        std::vector<transformer_id>>;
};

// A type of argument with a single address (target) mapping
template <typename T> struct unary_argument {
    // The memory associated with the address and the key path is owned
    // by either the condition (condition_target) or the processor (processor_target).
    std::string_view address{};
    std::span<const std::variant<std::string, int64_t>> key_path;
    T value;
    std::span<const transformer_id> transformers;
};

template <typename T, typename = void> struct is_unary_argument : std::false_type {};
template <typename T> struct is_unary_argument<unary_argument<T>> : std::true_type {};

// A type of argument which is considered to be optional
template <typename T> using optional_argument = std::optional<unary_argument<T>>;

template <typename T, typename = void> struct is_optional_argument : std::false_type {};
template <typename T> struct is_optional_argument<optional_argument<T>> : std::true_type {};

// A type of argument with multiple address(target) mappings
template <typename T> using variadic_argument = std::vector<unary_argument<T>>;

template <typename T, typename = void> struct is_variadic_argument : std::false_type {};
template <typename T> struct is_variadic_argument<variadic_argument<T>> : std::true_type {};

template <typename T> std::optional<T> convert(object_view obj)
{
    if constexpr (std::is_same_v<std::decay_t<T>, object_view>) {
        return obj;
    } else if constexpr (is_type_in_set_v<T, uint64_t, unsigned, int64_t, int, bool, std::string,
                             std::string_view, array_view, map_view>) {
        if (obj.is<T>()) {
            return obj.as<T>();
        }
    } else {
        static_assert(!std::is_same_v<T, T>, "unsupported type on argument_retriever::convert<T>");
    }
    return {};
}

struct default_argument_retriever {
    static constexpr bool is_variadic = false;
    static constexpr bool is_optional = false;
};

template <typename T> struct argument_retriever : default_argument_retriever {};

template <typename T> struct argument_retriever<unary_argument<T>> : default_argument_retriever {
    template <typename TargetType>
    static std::optional<unary_argument<T>> retrieve(
        const object_store &store, const object_set_ref &objects_excluded, const TargetType &target)
    {
        auto object = store.get_target(target.index);
        if (!object.has_value() || objects_excluded.contains(object)) {
            return std::nullopt;
        }

        auto converted = convert<T>(object);
        if (!converted.has_value()) {
            return std::nullopt;
        }

        if constexpr (has_transformers<TargetType>) {
            return unary_argument<T>{
                target.name, target.key_path, std::move(converted.value()), target.transformers};
        } else {
            return unary_argument<T>{
                target.name, target.key_path, std::move(converted.value()), {}};
        }
    }
};

template <typename T> struct argument_retriever<optional_argument<T>> : default_argument_retriever {
    static constexpr bool is_optional = true;

    template <typename TargetType>
    static optional_argument<T> retrieve(
        const object_store &store, const object_set_ref &objects_excluded, const TargetType &target)
    {
        return argument_retriever<unary_argument<T>>::retrieve(store, objects_excluded, target);
    }
};

template <typename T> struct argument_retriever<variadic_argument<T>> : default_argument_retriever {
    static constexpr bool is_variadic = true;

    template <typename TargetType>
    static variadic_argument<T> retrieve(const object_store &store,
        const object_set_ref &objects_excluded, const std::vector<TargetType> &targets)
    {
        variadic_argument<T> args;
        for (const auto &target : targets) {
            auto arg =
                argument_retriever<unary_argument<T>>::retrieve(store, objects_excluded, target);
            if (!arg.has_value()) {
                continue;
            }
            args.emplace_back(std::move(arg.value()));
        }
        return args;
    }
};

} // namespace ddwaf