Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
- name: Create Build Directory
run: cmake -E make_directory ${{ github.workspace }}/build ${{ github.workspace }}/packages
- name: Generating Build Scripts
run: cmake -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} -DCPACK_PACKAGE_DIRECTORY=${{ github.workspace }}/packages ${{ github.workspace }}
run: cmake -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBDDWAF_ENABLE_LTO=ON -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} -DCPACK_PACKAGE_DIRECTORY=${{ github.workspace }}/packages ${{ github.workspace }}
working-directory: ${{ github.workspace }}/build
- name: Build Binaries
run: cmake --build . --config RelWithDebInfo --verbose --target all --target waf_test -j $(getconf _NPROCESSORS_ONLN)
Expand Down
5 changes: 4 additions & 1 deletion cmake/objects.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ set(LIBDDWAF_SOURCE
${libddwaf_SOURCE_DIR}/src/dynamic_string.cpp
${libddwaf_SOURCE_DIR}/src/attribute_collector.cpp
${libddwaf_SOURCE_DIR}/src/rule.cpp
${libddwaf_SOURCE_DIR}/src/semver.cpp
${libddwaf_SOURCE_DIR}/src/utils.cpp
${libddwaf_SOURCE_DIR}/src/utf8.cpp
${libddwaf_SOURCE_DIR}/src/builder/action_mapper_builder.cpp
${libddwaf_SOURCE_DIR}/src/builder/matcher_builder.cpp
Expand Down Expand Up @@ -91,6 +93,7 @@ set(LIBDDWAF_SOURCE
${libddwaf_SOURCE_DIR}/src/transformer/css_decode.cpp
${libddwaf_SOURCE_DIR}/src/transformer/html_entity_decode.cpp
${libddwaf_SOURCE_DIR}/src/transformer/js_decode.cpp
${libddwaf_SOURCE_DIR}/src/vendor/fmt/format.cc
${libddwaf_SOURCE_DIR}/src/vendor/radixlib/radixlib.c
${libddwaf_SOURCE_DIR}/src/vendor/lua-aho-corasick/ac_fast.cxx
${libddwaf_SOURCE_DIR}/src/vendor/lua-aho-corasick/ac_slow.cxx
Expand Down Expand Up @@ -147,7 +150,7 @@ function(gen_objects target_name)
target_include_directories(${target_name} PUBLIC ${LIBDDWAF_PUBLIC_INCLUDES})
target_include_directories(${target_name} PRIVATE ${LIBDDWAF_PRIVATE_INCLUDES})

target_compile_definitions(${target_name} PRIVATE UTF8PROC_STATIC=1)
target_compile_definitions(${target_name} PRIVATE UTF8PROC_STATIC=1 FMT_OPTIMIZE_SIZE=2)
if (MSVC)
target_compile_definitions(${target_name} PRIVATE NOMINMAX)
endif()
Expand Down
10 changes: 0 additions & 10 deletions cmake/shared.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,4 @@ elseif (MSVC)
PUBLIC ${LIBDDWAF_INTERFACE_LIBRARIES})

install(FILES $<TARGET_PDB_FILE:libddwaf_shared> DESTINATION lib OPTIONAL)
elseif (MINGW)
target_link_libraries(libddwaf_shared PUBLIC ${LIBDDWAF_INTERFACE_LIBRARIES})
target_link_libraries(libddwaf_shared PRIVATE
$<$<BOOL:${LIBDDWAF_ENABLE_LTO}>:-flto>
-Wl,--no-undefined
-Wl,-version-script=${libddwaf_SOURCE_DIR}/libddwaf.version
-Wl,--build-id=0x${BUILD_ID}
${LIBDDWAF_PRIVATE_LIBRARIES}
-static-libstdc++
glibc_compat_time64 glibc_compat_math)
endif()
8 changes: 4 additions & 4 deletions src/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1136,13 +1136,13 @@ template <> struct object_converter<std::string> {
case object_type::small_string:
return view.as<std::string>();
case object_type::boolean:
return ddwaf::to_string<std::string>(view.as<bool>());
return ddwaf::to_string(view.as<bool>());
case object_type::uint64:
return ddwaf::to_string<std::string>(view.as<uint64_t>());
return ddwaf::to_string(view.as<uint64_t>());
case object_type::int64:
return ddwaf::to_string<std::string>(view.as<int64_t>());
return ddwaf::to_string(view.as<int64_t>());
case object_type::float64:
return ddwaf::to_string<std::string>(view.as<double>());
return ddwaf::to_string(view.as<double>());
default:
break;
}
Expand Down
6 changes: 3 additions & 3 deletions src/processor/fingerprint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ template <typename T>
struct unsigned_field : field_generator<unsigned_field<T>> {
explicit unsigned_field(T input) : value(input) {}

[[nodiscard]] dynamic_string generate() { return ddwaf::to_string<std::string>(value); }
[[nodiscard]] dynamic_string generate() { return ddwaf::to_string(value); }

T value;
};
Expand Down Expand Up @@ -564,7 +564,7 @@ owned_object http_header_fingerprint::eval_impl(const unary_argument<map_view> &
}
std::sort(unknown_headers.begin(), unknown_headers.end());

auto unknown_header_size = unknown_headers.size();
const uint64_t unknown_header_size = unknown_headers.size();
owned_object res;
try {
res = generate_fragment("hdr", alloc, string_field{known_header_bitset},
Expand Down Expand Up @@ -611,7 +611,7 @@ owned_object http_network_fingerprint::eval_impl(const unary_argument<map_view>
}
}

unsigned ip_count = 0;
uint64_t ip_count = 0;
if (!chosen_header_value.empty()) {
// For now, count commas
++ip_count;
Expand Down
2 changes: 1 addition & 1 deletion src/ruleset_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

namespace ddwaf {

inline std::string index_to_id(unsigned idx) { return "index:" + to_string<std::string>(idx); }
inline std::string index_to_id(unsigned idx) { return "index:" + to_string(idx); }

enum class ruleset_info_state : uint8_t { empty, invalid, valid };

Expand Down
67 changes: 67 additions & 0 deletions src/semver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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 2025 Datadog, Inc.

#include <cstddef>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <string_view>

#include "semver.hpp"
#include "utils.hpp"

namespace ddwaf {

semantic_version::semantic_version(std::string_view version) : str_(version)
{
// The expected version string is: xxx.yyy.zzz[-label]
// We only try to extract xxx, yyy and zzz, while discarding the label.
// Each element can be 1 to 3 digits long, but no longer.
// Any deviation from this will be rejected.

// Major
std::size_t start = 0;
auto end = version.find('.');
if (end == std::string_view::npos) {
throw std::invalid_argument("invalid version syntax");
}
auto major_str = version.substr(start, end - start);
if (major_str.empty() || major_str.size() > 3 || !parse_number(major_str, major_)) {
throw std::invalid_argument("invalid major version: " + std::string{major_str});
}

// Minor
start = end + 1;
end = version.find('.', start);
if (end == std::string_view::npos) {
throw std::invalid_argument("invalid version syntax");
}
auto minor_str = version.substr(start, end - start);
if (minor_str.empty() || minor_str.size() > 3 || !parse_number(minor_str, minor_)) {
throw std::invalid_argument("invalid minor version: " + std::string{minor_str});
}

// Patch
start = end + 1;
end = version.find('-', start);
auto patch_str = version.substr(start, end - start);
if (patch_str.empty() || patch_str.size() > 3 || !parse_number(patch_str, patch_)) {
throw std::invalid_argument("invalid patch version: " + std::string{patch_str});
}

number_ = major_ * 1000000 + minor_ * 1000 + patch_;
}

bool semantic_version::parse_number(std::string_view str, uint16_t &output)
{
if (auto [res, value] = from_string<uint16_t>(str); res) {
output = value;
return true;
}
return false;
}

} // namespace ddwaf
53 changes: 2 additions & 51 deletions src/semver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,16 @@

#pragma once

#include <cstddef>
#include <cstdint>
#include <fmt/format.h>
#include <stdexcept>
#include <string>
#include <string_view>

#include "fmt/core.h"
#include "utils.hpp"

namespace ddwaf {
class semantic_version {
public:
explicit semantic_version(std::string_view version) : str_(version)
{
// The expected version string is: xxx.yyy.zzz[-label]
// We only try to extract xxx, yyy and zzz, while discarding the label.
// Each element can be 1 to 3 digits long, but no longer.
// Any deviation from this will be rejected.

// Major
std::size_t start = 0;
auto end = version.find('.');
if (end == std::string_view::npos) {
throw std::invalid_argument("invalid version syntax");
}
auto major_str = version.substr(start, end - start);
if (major_str.empty() || major_str.size() > 3 || !parse_number(major_str, major_)) {
throw std::invalid_argument("invalid major version: " + std::string{major_str});
}

// Minor
start = end + 1;
end = version.find('.', start);
if (end == std::string_view::npos) {
throw std::invalid_argument("invalid version syntax");
}
auto minor_str = version.substr(start, end - start);
if (minor_str.empty() || minor_str.size() > 3 || !parse_number(minor_str, minor_)) {
throw std::invalid_argument("invalid minor version: " + std::string{minor_str});
}
explicit semantic_version(std::string_view version);

// Patch
start = end + 1;
end = version.find('-', start);
auto patch_str = version.substr(start, end - start);
if (patch_str.empty() || patch_str.size() > 3 || !parse_number(patch_str, patch_)) {
throw std::invalid_argument("invalid patch version: " + std::string{patch_str});
}

number_ = major_ * 1000000 + minor_ * 1000 + patch_;
}
semantic_version(semantic_version &&other) = default;
semantic_version &operator=(semantic_version &&other) noexcept = default;
semantic_version(const semantic_version &other) = default;
Expand Down Expand Up @@ -99,14 +57,7 @@ class semantic_version {
: str_(str), major_(major), minor_(minor), patch_(patch), number_(number)
{}

static bool parse_number(std::string_view str, uint16_t &output)
{
if (auto [res, value] = from_string<uint16_t>(str); res) {
output = value;
return true;
}
return false;
}
static bool parse_number(std::string_view str, uint16_t &output);

std::string str_;
uint16_t major_{0};
Expand Down
113 changes: 113 additions & 0 deletions src/utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// 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 2025 Datadog, Inc.

#include <algorithm>
#include <charconv>
#include <cstddef>
#include <cstdint>
#include <span>
#include <sstream>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
#include <variant>
#include <vector>

#include <fmt/format.h>

#include "utils.hpp"

namespace ddwaf {
namespace {

template <typename T>
concept has_from_chars = requires(T v) { std::from_chars(nullptr, nullptr, std::declval<T>()); };

} // namespace

std::vector<std::string_view> split(std::string_view str, char sep)
{
std::vector<std::string_view> components;

std::size_t start = 0;
while (start < str.size()) {
const std::size_t end = str.find(sep, start);

if (end == start) {
// Ignore zero-sized strings
start = end + 1;
continue;
}

if (end == std::string_view::npos) {
// Last element
components.emplace_back(str.substr(start));
start = str.size();
} else {
components.emplace_back(str.substr(start, end - start));
start = end + 1;
}
}

return components;
}

std::vector<std::variant<std::string_view, int64_t>> convert_key_path(
std::span<const std::variant<std::string, int64_t>> key_path)
{
std::vector<std::variant<std::string_view, int64_t>> result;
result.reserve(key_path.size());

for (const auto &key : key_path) {
std::visit([&result](auto &&k) { result.emplace_back(k); }, key);
}
return result;
}

bool string_iequals(std::string_view left, std::string_view right)
{
return left.size() == right.size() &&
std::equal(left.begin(), left.end(), right.begin(),
[](char l, char r) { return tolower(l) == tolower(r); });
}

template <typename T> std::string to_string(T value) { return ddwaf::fmt::format("{}", value); }

template <typename T> std::pair<bool, T> from_string(std::string_view str)
{
T result;
if constexpr (has_from_chars<T>) {
const auto *end = str.data() + str.size();
auto [endConv, err] = std::from_chars(str.data(), end, result);
if (err == std::errc{} && endConv == end) {
return {true, result};
}
} else {
// NOLINTNEXTLINE(misc-const-correctness)
std::istringstream iss(std::string{str});
iss >> result;
if (!iss.fail() && iss.eof()) {
return {true, result};
}
}

return {false, {}};
}

template std::string to_string<bool>(bool value);
template std::string to_string<int64_t>(int64_t value);
template std::string to_string<uint64_t>(uint64_t value);
template std::string to_string<unsigned>(unsigned value);
template std::string to_string<double>(double value);

template std::pair<bool, uint16_t> from_string<uint16_t>(std::string_view str);
template std::pair<bool, unsigned> from_string<unsigned>(std::string_view str);
template std::pair<bool, int64_t> from_string<int64_t>(std::string_view str);
template std::pair<bool, uint64_t> from_string<uint64_t>(std::string_view str);
template std::pair<bool, double> from_string<double>(std::string_view str);

} // namespace ddwaf
Loading