#include "processor/jwt_decode.hpp"
#include "argument_retriever.hpp"
#include "clock.hpp"
#include "cow_string.hpp"
#include "json_utils.hpp"
#include "memory_resource.hpp"
#include "object.hpp"
#include "pointer.hpp"
#include "processor/base.hpp"
#include "transformer/base64_decode.hpp"
#include "utils.hpp"
#include <cstddef>
#include <cstdint>
#include <span>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <variant>
using namespace std::literals;
namespace ddwaf {
namespace {
struct exploded_jwt {
std::string_view header;
std::string_view payload;
std::string_view signature;
};
std::pair<bool, exploded_jwt> split_token(std::string_view source)
{
constexpr char delim = '.';
exploded_jwt parts;
std::size_t end = source.find(delim);
if (end == std::string_view::npos || end == source.size() - 1) {
return {false, {}};
}
parts.header = source.substr(0, end);
source.remove_prefix(end + 1);
end = source.find(delim);
if (end == std::string_view::npos) {
return {false, {}};
}
parts.payload = source.substr(0, end);
if (end != source.size() - 1) {
parts.signature = source.substr(end + 1);
}
return {true, parts};
}
owned_object decode_and_parse(std::string_view source, nonnull_ptr<memory::memory_resource> alloc)
{
cow_string cstr{source};
if (!transformer::base64url_decode::transform(cstr)) {
return owned_object{};
}
return json_to_object(static_cast<std::string_view>(cstr), alloc);
}
std::string_view find_token(
object_view root, std::span<const std::variant<std::string, int64_t>> key_path)
{
object_view object = root;
if (!key_path.empty()) {
object = root.find_key_path(key_path);
if (!object.has_value()) {
return {};
}
}
if (object.is_array() && !object.empty()) {
object = object.at_value(0);
}
if (!object.is_string() || object.empty()) {
return {};
}
return object.as<std::string_view>();
}
}
owned_object jwt_decode::eval_impl(const unary_argument<object_view> &input,
processor_cache & , nonnull_ptr<memory::memory_resource> alloc,
ddwaf::timer & ) const
{
std::string_view token = find_token(input.value, input.key_path);
if (token.empty()) {
return owned_object{};
}
static const std::string_view prefix = "Bearer";
if (!token.starts_with(prefix)) {
return owned_object{};
}
token.remove_prefix(prefix.size());
std::size_t spaces = 0;
while (spaces < token.size() && ddwaf::isspace(token[spaces])) { ++spaces; }
token.remove_prefix(spaces);
auto [valid, jwt] = split_token(token);
if (!valid) {
return owned_object{};
}
auto output = object_builder::map(
{{"header", decode_and_parse(jwt.header, alloc)},
{"payload", decode_and_parse(jwt.payload, alloc)},
{"signature", object_builder::map({{"available", !jwt.signature.empty()}}, alloc)}},
alloc);
return output;
}
}