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.

#include <cstdint>
#include <limits>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>

#include "configuration/common/parser_exception.hpp"
#include "configuration/common/raw_configuration.hpp"
#include "object.hpp"
#include "object_type.hpp"
#include "semver.hpp"
#include "utils.hpp"

namespace ddwaf {
namespace {

std::string strtype(object_type type)
{
    switch (type) {
    case object_type::map:
    case object_type::large_map:
        return "map";
    case object_type::array:
    case object_type::large_array:
        return "array";
    case object_type::string:
    case object_type::literal_string:
    case object_type::small_string:
        return "string";
    case object_type::boolean:
        return "bool";
    case object_type::uint64:
        return "unsigned";
    case object_type::int64:
        return "signed";
    case object_type::float64:
        return "float";
    case object_type::null:
        return "null";
    default:
        break;
    }
    return "unknown";
}

} // namespace

raw_configuration::operator raw_configuration::map() const
{
    if (!view_.is_map()) {
        throw bad_cast("map", strtype(view_.type()));
    }

    const map_view mv{view_};
    if (mv.empty()) {
        return {};
    }

    std::unordered_map<std::string_view, raw_configuration> map;
    map.reserve(view_.size());
    for (auto [key, value] : mv) {
        if (key.empty()) {
            throw malformed_object("invalid key on map entry");
        }
        map.emplace(key.as<std::string_view>(), value);
    }
    return map;
}

raw_configuration::operator raw_configuration::vector() const
{
    if (!view_.is_array()) {
        throw bad_cast("array", strtype(view_.type()));
    }

    const array_view av{view_};
    if (av.empty()) {
        return {};
    }
    raw_configuration::vector vec;
    vec.reserve(av.size());

    for (auto value : av) { vec.emplace_back(value); }
    return vec;
}

raw_configuration::operator raw_configuration::string_set() const
{
    if (!view_.is_array()) {
        throw bad_cast("array", strtype(view_.type()));
    }

    const array_view av{view_};
    if (av.empty()) {
        return {};
    }

    raw_configuration::string_set set;
    set.reserve(av.size());

    for (auto value : av) {
        if (!value.is_string()) {
            throw malformed_object("item in array not a string, can't cast to string set");
        }
        set.emplace(value.as<std::string_view>());
    }
    return set;
}

raw_configuration::operator std::string_view() const
{
    if (!view_.is_string()) {
        throw bad_cast("string_view", strtype(view_.type()));
    }

    return view_.as<std::string_view>();
}

raw_configuration::operator std::string() const
{
    if (!view_.is_scalar()) {
        throw bad_cast("string", strtype(view_.type()));
    }

    if (view_.is_string() && view_.empty()) {
        return {};
    }

    return view_.convert<std::string>();
}

raw_configuration::operator uint64_t() const
{
    if (view_.is<uint64_t>()) {
        return view_.as<uint64_t>();
    }

    if (view_.is<int64_t>() && view_.as<int64_t>() >= 0) {
        return view_.as<int64_t>();
    }

    // NOLINTBEGIN(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
    // Closest 64-bit floating-point value to UINT64_MAX
    static constexpr double uint64_max = 0xFFFFFFFFFFFFF800ULL;
    if (view_.is<double>()) {
        auto f64 = view_.as<double>();
        if ((f64 >= 0.0) && (f64 <= uint64_max) && static_cast<uint64_t>(f64) == f64) {
            return static_cast<uint64_t>(f64);
        }
    }
    // NOLINTEND(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)

    if (view_.is_string() && !view_.empty()) {
        auto [res, result] = from_string<uint64_t>(view_.as<std::string_view>());
        if (res) {
            return result;
        }
    }

    throw bad_cast("unsigned", strtype(view_.type()));
}

raw_configuration::operator int64_t() const
{
    if (view_.is<int64_t>()) {
        return view_.as<int64_t>();
    }

    if (view_.is<uint64_t>() && view_.as<uint64_t>() <= std::numeric_limits<int64_t>::max()) {
        return static_cast<int64_t>(view_.as<uint64_t>());
    }

    // NOLINTBEGIN(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
    // Closest 64-bit floating-point value to INT64_MAX
    static constexpr double int64_max = 0x7FFFFFFFFFFFFC00LL;
    static constexpr double int64_min = std::numeric_limits<int64_t>::min();
    if (view_.is<double>()) {
        auto f64 = view_.as<double>();
        if ((f64 >= int64_min) && (f64 <= int64_max) && static_cast<int64_t>(f64) == f64) {
            return static_cast<int64_t>(f64);
        }
    }
    // NOLINTEND(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)

    if (view_.is_string() && !view_.empty()) {
        auto [res, result] = from_string<int64_t>(view_.as<std::string_view>());
        if (res) {
            return result;
        }
    }

    throw bad_cast("signed", strtype(view_.type()));
}

raw_configuration::operator double() const
{
    if (view_.is<double>()) {
        return view_.as<double>();
    }

    if (view_.is_string() && !view_.empty()) {
        auto [res, result] = from_string<double>(view_.as<std::string_view>());
        if (res) {
            return result;
        }
    }

    throw bad_cast("double", strtype(view_.type()));
}

raw_configuration::operator bool() const
{
    if (view_.is<bool>()) {
        return view_.as<bool>();
    }

    if (view_.is_string() && !view_.empty()) {
        const auto str_bool = view_.as<std::string_view>();
        if (str_bool.size() == (sizeof("true") - 1) && (str_bool[0] == 'T' || str_bool[0] == 't') &&
            (str_bool[1] == 'R' || str_bool[1] == 'r') &&
            (str_bool[2] == 'U' || str_bool[2] == 'u') &&
            (str_bool[3] == 'E' || str_bool[3] == 'e')) {
            return true;
        }

        if (str_bool.size() == (sizeof("false") - 1) &&
            (str_bool[0] == 'F' || str_bool[0] == 'f') &&
            (str_bool[1] == 'A' || str_bool[1] == 'a') &&
            (str_bool[2] == 'L' || str_bool[2] == 'l') &&
            (str_bool[3] == 'S' || str_bool[3] == 's') &&
            (str_bool[4] == 'E' || str_bool[4] == 'e')) {
            return false;
        }
    }

    throw bad_cast("bool", strtype(view_.type()));
}

raw_configuration::operator std::vector<std::string>() const
{
    if (!view_.is_array()) {
        throw bad_cast("array", strtype(view_.type()));
    }

    const array_view av{view_};
    if (av.empty()) {
        return {};
    }

    std::vector<std::string> vec;
    vec.reserve(av.size());

    for (auto value : av) {
        const raw_configuration item{value};
        vec.emplace_back(static_cast<std::string>(item));
    }
    return vec;
}

raw_configuration::operator std::vector<std::variant<std::string, int64_t>>() const
{
    if (!view_.is_array()) {
        throw bad_cast("array", strtype(view_.type()));
    }

    const array_view av{view_};
    if (av.empty()) {
        return {};
    }

    std::vector<std::variant<std::string, int64_t>> vec;
    vec.reserve(av.size());

    for (auto value : av) {
        const raw_configuration item{value};
        if (item->is_string()) {
            vec.emplace_back(static_cast<std::string>(item));
        } else if (item->type() == object_type::int64 || item->type() == object_type::uint64) {
            vec.emplace_back(static_cast<int64_t>(item));
        }
    }
    return vec;
}

raw_configuration::operator std::vector<std::string_view>() const
{
    if (!view_.is_array()) {
        throw bad_cast("array", strtype(view_.type()));
    }

    const array_view av{view_};
    if (av.empty()) {
        return {};
    }

    std::vector<std::string_view> vec;
    vec.reserve(av.size());

    for (auto value : av) {
        if (!value.is_string()) {
            throw malformed_object("item in array not a string, can't cast to string_view vector");
        }
        vec.emplace_back(value.as<std::string_view>());
    }
    return vec;
}

raw_configuration::operator std::unordered_map<std::string, std::string>() const
{
    if (!view_.is_map()) {
        throw bad_cast("map", strtype(view_.type()));
    }

    const map_view mv{view_};
    if (mv.empty()) {
        return {};
    }

    std::unordered_map<std::string, std::string> map;
    map.reserve(mv.size());
    for (auto [key, value] : mv) {
        if (key.empty()) {
            throw malformed_object("invalid key on map entry");
        }
        const raw_configuration item{value};
        map.emplace(key.as<std::string_view>(), static_cast<std::string>(item));
    }
    return map;
}

raw_configuration::operator std::unordered_map<std::string, scalar_type>() const
{
    if (!view_.is_map()) {
        throw bad_cast("map", strtype(view_.type()));
    }

    const map_view mv{view_};
    if (mv.empty()) {
        return {};
    }

    std::unordered_map<std::string, scalar_type> data;
    data.reserve(mv.size());
    for (auto [key, value] : mv) {
        switch (value.type()) {
        case object_type::boolean:
            data.emplace(key.as<std::string>(), value.as<bool>());
            break;
        case object_type::int64:
            data.emplace(key.as<std::string>(), value.as<int64_t>());
            break;
        case object_type::uint64:
            data.emplace(key.as<std::string>(), value.as<uint64_t>());
            break;
        case object_type::float64:
            data.emplace(key.as<std::string>(), value.as<double>());
            break;
        case object_type::string:
        case object_type::small_string:
        case object_type::literal_string:
            data.emplace(key.as<std::string>(), value.as<std::string>());
            break;
        default:
            throw malformed_object("item in scalar map not a valid scalar");
        }
    }
    return data;
}

raw_configuration::operator semantic_version() const
{
    if (!view_.is_string()) {
        throw bad_cast("string", strtype(view_.type()));
    }

    return semantic_version{view_.as<std::string_view>()};
}

} // namespace ddwaf