1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 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 "shi_common.hpp"
#include "object.hpp"
#include <algorithm>
#include <cstddef>
#include <string_view>
#include <utility>
using namespace std::literals;
namespace ddwaf {
shell_argument_array::shell_argument_array(object_view root)
{
// Since the type check is performed elsewhere, we don't need to check again
auto argc = root.size();
if (argc == 0) {
return;
}
// Calculate the final resource length
std::size_t resource_len = 0;
for (std::size_t i = 0; i < argc; ++i) {
const auto &child = root.at_value(i);
if (child.is_string() && !child.empty()) {
// if the string is valid or non-empty, increase the resource
// length + 1 for the extra space when relevant
resource_len += child.size() + static_cast<std::size_t>(i > 0);
}
}
indices.reserve(argc);
resource.reserve(resource_len);
std::size_t index = 0;
for (std::size_t i = 0; i < argc; ++i) {
const auto &child = root.at_value(i);
if (!child.is_string() || child.empty()) {
continue;
}
const auto str = child.as<std::string_view>();
indices.emplace_back(index, index + str.size() - 1);
index += str.size() + 1;
if (!resource.empty()) {
resource.append(" "sv);
}
resource.append(str);
}
}
std::size_t shell_argument_array::find(std::string_view str, std::size_t start)
{
while ((start = resource.find(str, start)) != npos) {
auto end = start + str.size() - 1;
// Lower bound returns the first element where the condition is false,
// which must be equivalent to cur < start_pair for the binary search to
// work as expected. The condition will match the first iterator where
// cur.second >= start.
auto res = std::lower_bound(indices.begin(), indices.end(), std::pair{start, 0},
[](const auto &cur, const auto &start_pair) { return cur.second < start_pair.first; });
if (res != indices.end() && res->first <= start && res->second >= end) {
return start;
}
// Otherwise, there's overlap and it's not a valid match.
// Attempt the next match
start += 1;
}
return npos;
}
} // namespace ddwaf