#include <cstddef>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>
#include "clock.hpp"
#include "condition/match_iterator.hpp"
#include "exception.hpp"
#include "exclusion/common.hpp"
#include "iterator.hpp"
#include "object.hpp"
#include "tokenizer/shell.hpp"
#include "transformer/base.hpp"
namespace ddwaf {
struct shi_result {
std::string value;
std::vector<std::variant<std::string_view, int64_t>> key_path;
};
struct shell_argument_array {
static constexpr std::size_t npos = std::string_view::npos;
explicit shell_argument_array(object_view root);
std::size_t find(std::string_view str, std::size_t start = 0);
[[nodiscard]] bool empty() const { return resource.empty(); }
std::vector<std::pair<std::size_t, std::size_t>> indices;
std::string resource;
};
template <typename ResourceType, typename IteratorType = kv_iterator>
std::optional<shi_result> find_shi_from_params(const ResourceType &resource,
std::vector<shell_token> &resource_tokens, object_view params,
std::span<const transformer_id> transformers, const object_set_ref &objects_excluded,
ddwaf::timer &deadline)
{
match_iterator<2, IteratorType, ResourceType> it(
resource, params, transformers, objects_excluded, deadline);
for (; it; ++it) {
if (deadline.expired()) {
throw ddwaf::timeout_exception();
}
const auto [param, param_index] = *it;
if (resource_tokens.empty()) {
if constexpr (std::is_same_v<ResourceType, shell_argument_array>) {
shell_tokenizer tokenizer(resource.resource);
resource_tokens = tokenizer.tokenize();
} else {
shell_tokenizer tokenizer(resource);
resource_tokens = tokenizer.tokenize();
}
}
auto end_index = param_index + param.size();
std::size_t i = 0;
for (; i < resource_tokens.size(); ++i) {
const auto &token = resource_tokens[i];
if (end_index >= token.index && param_index < (token.index + token.str.size())) {
break;
}
}
if ((i + 1) < resource_tokens.size() && resource_tokens[i + 1].index >= end_index) {
continue;
}
for (; i < resource_tokens.size() && end_index >= resource_tokens[i].index; ++i) {
const auto &token = resource_tokens[i];
if (token.type == shell_token_type::executable ||
token.type == shell_token_type::redirection) {
return {{std::string(param), it.get_current_path()}};
}
}
}
return std::nullopt;
}
}